From c8e222d9b37a6eb9af51d0c67ad90aa02fc682da Mon Sep 17 00:00:00 2001 From: pramesthias Date: Tue, 14 Nov 2023 15:58:21 +0700 Subject: [PATCH 01/28] database setup --- server/.gitignore | 1 + server/app.js | 18 + server/config/config.json | 23 + server/data/user.json | 7 + server/helpers/bcryptjs.js | 13 + server/helpers/jwt.js | 0 .../migrations/20231114055501-create-user.js | 38 + server/models/index.js | 43 + server/models/user.js | 68 + server/package-lock.json | 6149 +++++++++++++++++ server/package.json | 27 + server/seeders/20231114085507-user-seed.js | 28 + 12 files changed, 6415 insertions(+) create mode 100644 server/.gitignore create mode 100644 server/app.js create mode 100644 server/config/config.json create mode 100644 server/data/user.json create mode 100644 server/helpers/bcryptjs.js create mode 100644 server/helpers/jwt.js create mode 100644 server/migrations/20231114055501-create-user.js create mode 100644 server/models/index.js create mode 100644 server/models/user.js create mode 100644 server/package-lock.json create mode 100644 server/package.json create mode 100644 server/seeders/20231114085507-user-seed.js diff --git a/server/.gitignore b/server/.gitignore new file mode 100644 index 0000000..b512c09 --- /dev/null +++ b/server/.gitignore @@ -0,0 +1 @@ +node_modules \ No newline at end of file diff --git a/server/app.js b/server/app.js new file mode 100644 index 0000000..c3a3cec --- /dev/null +++ b/server/app.js @@ -0,0 +1,18 @@ +// if(process.env.NODE_ENV !== "production"){ +// require('dotenv').config() //selalu ditulis di awal app.js & di pakai tahap dev +// } + +const express = require('express'); +const app = express(); +// const cors = require('cors'); //DISTRIBUTION + +// app.use(cors()) // DEPLOY + +app.use(express.urlencoded({ extended: true })); + +app.use(express.json()); + +app.use(require("./routes")); + + +module.exports = app; \ No newline at end of file diff --git a/server/config/config.json b/server/config/config.json new file mode 100644 index 0000000..ef1749e --- /dev/null +++ b/server/config/config.json @@ -0,0 +1,23 @@ +{ + "development": { + "username": "postgres", + "password": "postgres", + "database": "cyto_dev", + "host": "127.0.0.1", + "dialect": "postgres" + }, + "test": { + "username": "root", + "password": null, + "database": "database_test", + "host": "127.0.0.1", + "dialect": "mysql" + }, + "production": { + "username": "root", + "password": null, + "database": "database_production", + "host": "127.0.0.1", + "dialect": "mysql" + } +} diff --git a/server/data/user.json b/server/data/user.json new file mode 100644 index 0000000..dc4cd37 --- /dev/null +++ b/server/data/user.json @@ -0,0 +1,7 @@ +[ + { + "username": "Your Majesty", + "email": "admin@email.com", + "password": "12345" + } +] \ No newline at end of file diff --git a/server/helpers/bcryptjs.js b/server/helpers/bcryptjs.js new file mode 100644 index 0000000..3f440e2 --- /dev/null +++ b/server/helpers/bcryptjs.js @@ -0,0 +1,13 @@ +const bcrypt = require('bcryptjs'); + +const hashPassword = (password) => { + const salt = bcrypt.genSaltSync(10); + return bcrypt.hashSync(password, salt); +} + +const comparePassword = (password, hashedPassword) => { + return bcrypt.compareSync(password, hashedPassword); +} + + +module.exports = {hashPassword, comparePassword}; \ No newline at end of file diff --git a/server/helpers/jwt.js b/server/helpers/jwt.js new file mode 100644 index 0000000..e69de29 diff --git a/server/migrations/20231114055501-create-user.js b/server/migrations/20231114055501-create-user.js new file mode 100644 index 0000000..7296d92 --- /dev/null +++ b/server/migrations/20231114055501-create-user.js @@ -0,0 +1,38 @@ +'use strict'; +/** @type {import('sequelize-cli').Migration} */ +module.exports = { + async up(queryInterface, Sequelize) { + await queryInterface.createTable('Users', { + id: { + allowNull: false, + autoIncrement: true, + primaryKey: true, + type: Sequelize.INTEGER + }, + username: { + type: Sequelize.STRING, + defaultValue: "ViCYTOr" + }, + email: { + type: Sequelize.STRING, + allowNull: false, + unique: true + }, + password: { + type: Sequelize.STRING, + allowNull: false + }, + createdAt: { + allowNull: false, + type: Sequelize.DATE + }, + updatedAt: { + allowNull: false, + type: Sequelize.DATE + } + }); + }, + async down(queryInterface, Sequelize) { + await queryInterface.dropTable('Users'); + } +}; \ No newline at end of file diff --git a/server/models/index.js b/server/models/index.js new file mode 100644 index 0000000..024200e --- /dev/null +++ b/server/models/index.js @@ -0,0 +1,43 @@ +'use strict'; + +const fs = require('fs'); +const path = require('path'); +const Sequelize = require('sequelize'); +const process = require('process'); +const basename = path.basename(__filename); +const env = process.env.NODE_ENV || 'development'; +const config = require(__dirname + '/../config/config.json')[env]; +const db = {}; + +let sequelize; +if (config.use_env_variable) { + sequelize = new Sequelize(process.env[config.use_env_variable], config); +} else { + sequelize = new Sequelize(config.database, config.username, config.password, config); +} + +fs + .readdirSync(__dirname) + .filter(file => { + return ( + file.indexOf('.') !== 0 && + file !== basename && + file.slice(-3) === '.js' && + file.indexOf('.test.js') === -1 + ); + }) + .forEach(file => { + const model = require(path.join(__dirname, file))(sequelize, Sequelize.DataTypes); + db[model.name] = model; + }); + +Object.keys(db).forEach(modelName => { + if (db[modelName].associate) { + db[modelName].associate(db); + } +}); + +db.sequelize = sequelize; +db.Sequelize = Sequelize; + +module.exports = db; diff --git a/server/models/user.js b/server/models/user.js new file mode 100644 index 0000000..f8823a5 --- /dev/null +++ b/server/models/user.js @@ -0,0 +1,68 @@ +'use strict'; +const { Model } = require('sequelize'); +const { hashPassword } = require('../helpers/bcryptjs'); + + +module.exports = (sequelize, DataTypes) => { + class User extends Model { + /** + * Helper method for defining associations. + * This method is not a part of Sequelize lifecycle. + * The `models/index` file will call this method automatically. + */ + static associate(models) { + // define association here + } + } + User.init({ + + username: { + type: DataTypes.STRING, + defaultValue: "ViCYTOr" + }, + + email: { + type: DataTypes.STRING, + allowNull: false, + unique: true, + validate: { + notNull: { + msg: "Email cannot be Empty!" + }, + notEmpty: { + msg: "Email cannot be Empty!" + }, + isEmail: { + msg: "Email should be written in email format!" + } + } + }, + + password: { + type: DataTypes.STRING, + allowNull: false, + validate: { + notNull: { + msg: "Password cannot be Empty!" + }, + notEmpty: { + msg: "Password cannot be Empty!" + }, + len: { + args: [8], + msg: "Minimum password length is 8" + } + } + } + + }, { + sequelize, + modelName: 'User', + }); + + User.beforeCreate((user) => { + user.password = hashPassword(user.password); + }) + + return User; +}; \ No newline at end of file diff --git a/server/package-lock.json b/server/package-lock.json new file mode 100644 index 0000000..fadbdfa --- /dev/null +++ b/server/package-lock.json @@ -0,0 +1,6149 @@ +{ + "name": "server", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "server", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "bcryptjs": "^2.4.3", + "express": "^4.18.2", + "jsonwebtoken": "^9.0.2", + "pg": "^8.11.3", + "sequelize": "^6.35.0" + }, + "devDependencies": { + "dotenv": "^16.3.1", + "jest": "^29.7.0", + "nodemon": "^3.0.1", + "sequelize-cli": "^6.6.2", + "supertest": "^6.3.3" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", + "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.22.13", + "chalk": "^2.4.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/code-frame/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/code-frame/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.3.tgz", + "integrity": "sha512-BmR4bWbDIoFJmJ9z2cZ8Gmm2MXgEDgjdWgpKmKWUt54UGFJdlj31ECtbaDvCG/qVdG3AQ1SfpZEs01lUFbzLOQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.3.tgz", + "integrity": "sha512-Jg+msLuNuCJDyBvFv5+OKOUjWMZgd85bKjbICd3zWrKAo+bJ49HJufi7CQE0q0uR8NGyO6xkCACScNqyjHSZew==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.3", + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helpers": "^7.23.2", + "@babel/parser": "^7.23.3", + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.3", + "@babel/types": "^7.23.3", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@babel/core/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.3.tgz", + "integrity": "sha512-keeZWAV4LU3tW0qRi19HRpabC/ilM0HRBBzf9/k8FFiG4KVpiv0FIy4hHfLfFQZNhziCTPTmd59zoyv6DNISzg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.23.3", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz", + "integrity": "sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.22.9", + "@babel/helper-validator-option": "^7.22.15", + "browserslist": "^4.21.9", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", + "dev": true, + "dependencies": { + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", + "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", + "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-simple-access": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/helper-validator-identifier": "^7.22.20" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", + "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", + "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz", + "integrity": "sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.2.tgz", + "integrity": "sha512-lzchcp8SjTSVe/fPmLwtWVBFC7+Tbn8LGHDVfDp9JGxpAY5opSaEFgt8UQvrnECWOTdji2mOWMz1rOhkHscmGQ==", + "dev": true, + "dependencies": { + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.2", + "@babel/types": "^7.23.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", + "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.3.tgz", + "integrity": "sha512-uVsWNvlVsIninV2prNz/3lHCb+5CJ+e+IUBfbjToAHODtfGYLfCFuY4AU7TskI+dAKk+njsPiBjq1gKTvZOBaw==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.23.3.tgz", + "integrity": "sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.23.3.tgz", + "integrity": "sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.3.tgz", + "integrity": "sha512-+K0yF1/9yR0oHdE0StHuEj3uTPzwwbrLGfNOndVJVV2TqA5+j3oljJUb4nmB954FLGjNem976+B+eDuLIjesiQ==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.3", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.3", + "@babel/types": "^7.23.3", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@babel/traverse/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/@babel/types": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.3.tgz", + "integrity": "sha512-OZnvoH2l8PK5eUvEcUyCt/sXgr/h+UWpVuBbOljwcrAgUl6lpchoQ++PHGyQy1AtYnVA6CEq3y5xeEI10brpXw==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/core": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", + "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", + "dev": true, + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/reporters": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.7.0", + "jest-config": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-resolve-dependencies": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "jest-watcher": "^29.7.0", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/core/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/core/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/environment": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", + "dev": true, + "dependencies": { + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", + "dev": true, + "dependencies": { + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "dev": true, + "dependencies": { + "jest-get-type": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/types": "^29.6.3", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", + "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", + "dev": true, + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/reporters/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/reporters/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@jest/reporters/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@jest/reporters/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@jest/reporters/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.18", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", + "dev": true, + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", + "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", + "dev": true, + "dependencies": { + "@jest/test-result": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.20", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", + "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@one-ini/wasm": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@one-ini/wasm/-/wasm-0.1.1.tgz", + "integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==", + "dev": true + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", + "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.4", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.4.tgz", + "integrity": "sha512-mLnSC22IC4vcWiuObSRjrLd9XcBTGf59vUSoq2jkQDJ/QQ8PMI9rSuzE+aEV8karUMbskw07bKYoUJCKTUaygg==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.7", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.7.tgz", + "integrity": "sha512-6Sfsq+EaaLrw4RmdFWE9Onp63TOUue71AWb4Gpa6JxzgTYtimbM086WnYTy2U67AofR++QKCo08ZP6pwx8YFHQ==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.4", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.4.tgz", + "integrity": "sha512-mSM/iKUk5fDDrEV/e83qY+Cr3I1+Q3qqTuEn++HAWYjEa1+NxZr6CNrcJGf2ZTnq4HoFGC3zaTPZTobCzCFukA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/debug": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/ms": { + "version": "0.7.34", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz", + "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==" + }, + "node_modules/@types/node": { + "version": "20.9.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.0.tgz", + "integrity": "sha512-nekiGu2NDb1BcVofVcEKMIwzlx4NjHlcjhoxxKBNLtz15Y1z7MYf549DFvkHSId02Ax6kGwWntIBPC3l/JZcmw==", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true + }, + "node_modules/@types/validator": { + "version": "13.11.6", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.11.6.tgz", + "integrity": "sha512-HUgHujPhKuNzgNXBRZKYexwoG+gHKU+tnfPqjWXFghZAnn73JElicMkuSKJyLGr9JgyA8IgK7fj88IyA9rwYeQ==" + }, + "node_modules/@types/yargs": { + "version": "17.0.31", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.31.tgz", + "integrity": "sha512-bocYSx4DI8TmdlvxqGpVNXOgCNR1Jj0gNPhhAY+iz1rgKDAaYrAYdFYnhDV1IFuiuVc9HkOwyDcFxaTElF3/wg==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true + }, + "node_modules/abbrev": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", + "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "dev": true + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true + }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/babel-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", + "dev": true, + "dependencies": { + "@jest/transform": "^29.7.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", + "dev": true, + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "dev": true, + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-top-level-await": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-jest": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", + "dev": true, + "dependencies": { + "babel-plugin-jest-hoist": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/bcryptjs": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", + "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==" + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true + }, + "node_modules/body-parser": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.22.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.1.tgz", + "integrity": "sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001541", + "electron-to-chromium": "^1.4.535", + "node-releases": "^2.0.13", + "update-browserslist-db": "^1.0.13" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "node_modules/buffer-writer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", + "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", + "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", + "dependencies": { + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.1", + "set-function-length": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001561", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001561.tgz", + "integrity": "sha512-NTt0DNoKe958Q0BE0j0c1V9jbUzhBxHIEJy7asmGrpE0yG63KTV7PLHPnK2E1O9RsQrQ081I3NLuXGS6zht3cw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/chalk/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "engines": { + "node": ">=8" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", + "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", + "dev": true + }, + "node_modules/cli-color": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/cli-color/-/cli-color-2.0.3.tgz", + "integrity": "sha512-OkoZnxyC4ERN3zLzZaY9Emb7f/MhBOIpePv0Ycok0fJYT+Ouo00UBEIwsVsr0yoow++n5YWlSUgST9GKhNHiRQ==", + "dev": true, + "dependencies": { + "d": "^1.0.1", + "es5-ext": "^0.10.61", + "es6-iterator": "^2.0.3", + "memoizee": "^0.4.15", + "timers-ext": "^0.1.7" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", + "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", + "dev": true + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/config-chain": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", + "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", + "dev": true, + "dependencies": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "node_modules/cookiejar": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", + "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", + "dev": true + }, + "node_modules/create-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", + "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "prompts": "^2.0.1" + }, + "bin": { + "create-jest": "bin/create-jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "dev": true, + "dependencies": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/dedent": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz", + "integrity": "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==", + "dev": true, + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-data-property": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", + "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", + "dependencies": { + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/dezalgo": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", + "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", + "dev": true, + "dependencies": { + "asap": "^2.0.0", + "wrappy": "1" + } + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/dotenv": { + "version": "16.3.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", + "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/motdotla/dotenv?sponsor=1" + } + }, + "node_modules/dottie": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/dottie/-/dottie-2.0.6.tgz", + "integrity": "sha512-iGCHkfUc5kFekGiqhe8B/mdaurD+lakO9txNnTvKtA6PISrw86LgqHvRzWYPyoE2Ph5aMIrCw9/uko6XHTKCwA==" + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/editorconfig": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-1.0.4.tgz", + "integrity": "sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==", + "dev": true, + "dependencies": { + "@one-ini/wasm": "0.1.1", + "commander": "^10.0.0", + "minimatch": "9.0.1", + "semver": "^7.5.3" + }, + "bin": { + "editorconfig": "bin/editorconfig" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "node_modules/electron-to-chromium": { + "version": "1.4.582", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.582.tgz", + "integrity": "sha512-89o0MGoocwYbzqUUjc+VNpeOFSOK9nIdC5wY4N+PVUarUK0MtjyTjks75AZS2bW4Kl8MdewdFsWaH0jLy+JNoA==", + "dev": true + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es5-ext": { + "version": "0.10.62", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz", + "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "next-tick": "^1.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", + "dev": true, + "dependencies": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/es6-symbol": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", + "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", + "dev": true, + "dependencies": { + "d": "^1.0.1", + "ext": "^1.1.2" + } + }, + "node_modules/es6-weak-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz", + "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==", + "dev": true, + "dependencies": { + "d": "1", + "es5-ext": "^0.10.46", + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", + "dev": true, + "dependencies": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/execa/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", + "dev": true, + "dependencies": { + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/express": { + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.1", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/ext": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", + "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", + "dev": true, + "dependencies": { + "type": "^2.7.2" + } + }, + "node_modules/ext/node_modules/type": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", + "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==", + "dev": true + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", + "dev": true + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/foreground-child": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", + "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/formidable": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.2.tgz", + "integrity": "sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g==", + "dev": true, + "dependencies": { + "dezalgo": "^1.0.4", + "hexoid": "^1.0.0", + "once": "^1.4.0", + "qs": "^6.11.0" + }, + "funding": { + "url": "https://ko-fi.com/tunnckoCore/commissions" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", + "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", + "dependencies": { + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.5", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", + "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", + "dependencies": { + "get-intrinsic": "^1.2.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", + "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hexoid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz", + "integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", + "dev": true + }, + "node_modules/import-local": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "dev": true, + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflection": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.13.4.tgz", + "integrity": "sha512-6I/HUDeYFfuNCVS3td055BaXBwKYuzw7K3ExVMStBowKo9oOAMJIXIHvdyR3iboTCp1b+1i5DSkIZTcwIktuDw==", + "engines": [ + "node >= 0.4.0" + ] + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-promise": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==", + "dev": true + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.1.tgz", + "integrity": "sha512-EAMEJBsYuyyztxMxW3g7ugGPkrZsV57v0Hmv3mm1uQsmB+QnZuepg731CRaIgeUVSdmsTngOkSnauNF8p7FIhA==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/istanbul-reports": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz", + "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jackspeak": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", + "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", + "dev": true, + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/types": "^29.6.3", + "import-local": "^3.0.2", + "jest-cli": "^29.7.0" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", + "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", + "dev": true, + "dependencies": { + "execa": "^5.0.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", + "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^1.0.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0", + "pretty-format": "^29.7.0", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-cli": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", + "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", + "dev": true, + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "create-jest": "^29.7.0", + "exit": "^0.1.2", + "import-local": "^3.0.2", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-cli/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-cli/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-cli/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/jest-cli/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/jest-cli/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-cli/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-cli/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/jest-cli/node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/jest-cli/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/jest-config": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-config/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/jest-config/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jest-config/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", + "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", + "dev": true, + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", + "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-leak-detector": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", + "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", + "dev": true, + "dependencies": { + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-mock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", + "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", + "dev": true, + "dependencies": { + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", + "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", + "dev": true, + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/jest-runtime/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jest-runtime/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/jest-snapshot": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.7.0", + "semver": "^7.5.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watcher": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", + "dev": true, + "dependencies": { + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.7.0", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "dev": true, + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/js-beautify": { + "version": "1.14.11", + "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.14.11.tgz", + "integrity": "sha512-rPogWqAfoYh1Ryqqh2agUpVfbxAhbjuN1SmU86dskQUKouRiggUTCO4+2ym9UPXllc2WAp0J+T5qxn7Um3lCdw==", + "dev": true, + "dependencies": { + "config-chain": "^1.1.13", + "editorconfig": "^1.0.3", + "glob": "^10.3.3", + "nopt": "^7.2.0" + }, + "bin": { + "css-beautify": "js/bin/css-beautify.js", + "html-beautify": "js/bin/html-beautify.js", + "js-beautify": "js/bin/js-beautify.js" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jsonwebtoken/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/lru-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz", + "integrity": "sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ==", + "dev": true, + "dependencies": { + "es5-ext": "~0.10.2" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/memoizee": { + "version": "0.4.15", + "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.15.tgz", + "integrity": "sha512-UBWmJpLZd5STPm7PMUlOw/TSy972M+z8gcyQ5veOnSDRREz/0bmpyTfKt3/51DhEBqCZQn1udM/5flcSPYhkdQ==", + "dev": true, + "dependencies": { + "d": "^1.0.1", + "es5-ext": "^0.10.53", + "es6-weak-map": "^2.0.3", + "event-emitter": "^0.3.5", + "is-promise": "^2.2.2", + "lru-queue": "^0.1.0", + "next-tick": "^1.1.0", + "timers-ext": "^0.1.7" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz", + "integrity": "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", + "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/moment": { + "version": "2.29.4", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", + "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", + "engines": { + "node": "*" + } + }, + "node_modules/moment-timezone": { + "version": "0.5.43", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.43.tgz", + "integrity": "sha512-72j3aNyuIsDxdF1i7CEgV2FfxM1r6aaqJyLB2vwb33mXYyoyLly+F1zbWqhA3/bVIoJ4szlUoMbUnVdid32NUQ==", + "dependencies": { + "moment": "^2.29.4" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/next-tick": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==", + "dev": true + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true + }, + "node_modules/node-releases": { + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", + "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", + "dev": true + }, + "node_modules/nodemon": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.0.1.tgz", + "integrity": "sha512-g9AZ7HmkhQkqXkRc20w+ZfQ73cHLbE8hnPbtaFbFtCumZsjyMhKk9LajQ07U5Ux28lvFjZ5X7HvWR1xzU8jHVw==", + "dev": true, + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^3.2.7", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^7.5.3", + "simple-update-notifier": "^2.0.0", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/nodemon/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/nodemon/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/nodemon/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/nodemon/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/nopt": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.0.tgz", + "integrity": "sha512-CVDtwCdhYIvnAzFoJ6NJ6dX3oga9/HyciQDnG1vQDjSLMeKLJ4A93ZqYKDrgYSr1FBY5/hMYC+2VCi24pgpkGA==", + "dev": true, + "dependencies": { + "abbrev": "^2.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/object-inspect": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-locate/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/packet-reader": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", + "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==" + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-scurry": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", + "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", + "dev": true, + "dependencies": { + "lru-cache": "^9.1.1 || ^10.0.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.2.tgz", + "integrity": "sha512-Yj9mA8fPiVgOUpByoTZO5pNrcl5Yk37FcSHsUINpAsaBIEZIuqcCclDZJCVxqQShDsmYX8QG63svJiTbOATZwg==", + "dev": true, + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": "14 || >=16.14" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + }, + "node_modules/pg": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.11.3.tgz", + "integrity": "sha512-+9iuvG8QfaaUrrph+kpF24cXkH1YOOUeArRNYIxq1viYHZagBxrTno7cecY1Fa44tJeZvaoG+Djpkc3JwehN5g==", + "dependencies": { + "buffer-writer": "2.0.0", + "packet-reader": "1.0.0", + "pg-connection-string": "^2.6.2", + "pg-pool": "^3.6.1", + "pg-protocol": "^1.6.0", + "pg-types": "^2.1.0", + "pgpass": "1.x" + }, + "engines": { + "node": ">= 8.0.0" + }, + "optionalDependencies": { + "pg-cloudflare": "^1.1.1" + }, + "peerDependencies": { + "pg-native": ">=3.0.1" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } + } + }, + "node_modules/pg-cloudflare": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz", + "integrity": "sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==", + "optional": true + }, + "node_modules/pg-connection-string": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz", + "integrity": "sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==" + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-pool": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.1.tgz", + "integrity": "sha512-jizsIzhkIitxCGfPRzJn1ZdcosIt3pz9Sh3V01fm1vZnbnCMgmGl5wvGGdNN2EL9Rmb0EcFoCkixH4Pu+sP9Og==", + "peerDependencies": { + "pg": ">=8.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.0.tgz", + "integrity": "sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q==" + }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "dependencies": { + "split2": "^4.1.0" + } + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", + "dev": true + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true + }, + "node_modules/pure-rand": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.4.tgz", + "integrity": "sha512-LA0Y9kxMYv47GIPJy6MI84fqTd2HmYZI83W/kM/SkKfDlajnZYfmXFTxkbY+xSBPkLJxltMa9hIkmdc29eguMA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ] + }, + "node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve.exports": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", + "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/retry-as-promised": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-7.0.4.tgz", + "integrity": "sha512-XgmCoxKWkDofwH8WddD0w85ZfqYz+ZHlr5yo+3YUCfycWawU56T5ckWXsScsj5B8tqUcIG67DxXByo3VUgiAdA==" + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/sequelize": { + "version": "6.35.0", + "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-6.35.0.tgz", + "integrity": "sha512-cnxnmjUfphGfSKCwTtNZ3YD/F35fqMTNPw/Qe9xsMij49t6LkW2G57sNQkuKac8fkQgSX+M8OZOQsxCS6dnUwQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/sequelize" + } + ], + "dependencies": { + "@types/debug": "^4.1.8", + "@types/validator": "^13.7.17", + "debug": "^4.3.4", + "dottie": "^2.0.6", + "inflection": "^1.13.4", + "lodash": "^4.17.21", + "moment": "^2.29.4", + "moment-timezone": "^0.5.43", + "pg-connection-string": "^2.6.1", + "retry-as-promised": "^7.0.4", + "semver": "^7.5.4", + "sequelize-pool": "^7.1.0", + "toposort-class": "^1.0.1", + "uuid": "^8.3.2", + "validator": "^13.9.0", + "wkx": "^0.5.0" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependenciesMeta": { + "ibm_db": { + "optional": true + }, + "mariadb": { + "optional": true + }, + "mysql2": { + "optional": true + }, + "oracledb": { + "optional": true + }, + "pg": { + "optional": true + }, + "pg-hstore": { + "optional": true + }, + "snowflake-sdk": { + "optional": true + }, + "sqlite3": { + "optional": true + }, + "tedious": { + "optional": true + } + } + }, + "node_modules/sequelize-cli": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/sequelize-cli/-/sequelize-cli-6.6.2.tgz", + "integrity": "sha512-V8Oh+XMz2+uquLZltZES6MVAD+yEnmMfwfn+gpXcDiwE3jyQygLt4xoI0zG8gKt6cRcs84hsKnXAKDQjG/JAgg==", + "dev": true, + "dependencies": { + "cli-color": "^2.0.3", + "fs-extra": "^9.1.0", + "js-beautify": "^1.14.5", + "lodash": "^4.17.21", + "resolve": "^1.22.1", + "umzug": "^2.3.0", + "yargs": "^16.2.0" + }, + "bin": { + "sequelize": "lib/sequelize", + "sequelize-cli": "lib/sequelize" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/sequelize-pool": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/sequelize-pool/-/sequelize-pool-7.1.0.tgz", + "integrity": "sha512-G9c0qlIWQSK29pR/5U2JF5dDQeqqHRragoyahj/Nx4KOOQ3CPPfzxnfqFPCSB7x5UgjOgnZ61nSxz+fjDpRlJg==", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/sequelize/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/sequelize/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-function-length": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", + "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==", + "dependencies": { + "define-data-property": "^1.1.1", + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "dev": true, + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-length/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-length/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/superagent": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-8.1.2.tgz", + "integrity": "sha512-6WTxW1EB6yCxV5VFOIPQruWGHqc3yI7hEmZK6h+pyk69Lk/Ut7rLUY6W/ONF2MjBuGjvmMiIpsrVJ2vjrHlslA==", + "dev": true, + "dependencies": { + "component-emitter": "^1.3.0", + "cookiejar": "^2.1.4", + "debug": "^4.3.4", + "fast-safe-stringify": "^2.1.1", + "form-data": "^4.0.0", + "formidable": "^2.1.2", + "methods": "^1.1.2", + "mime": "2.6.0", + "qs": "^6.11.0", + "semver": "^7.3.8" + }, + "engines": { + "node": ">=6.4.0 <13 || >=14" + } + }, + "node_modules/superagent/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/superagent/node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/superagent/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/supertest": { + "version": "6.3.3", + "resolved": "https://registry.npmjs.org/supertest/-/supertest-6.3.3.tgz", + "integrity": "sha512-EMCG6G8gDu5qEqRQ3JjjPs6+FYT1a7Hv5ApHvtSghmOFJYtsU5S+pSb6Y2EUeCEY3CmEL3mmQ8YWlPOzQomabA==", + "dev": true, + "dependencies": { + "methods": "^1.1.2", + "superagent": "^8.0.5" + }, + "engines": { + "node": ">=6.4.0" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/test-exclude/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/test-exclude/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/test-exclude/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/timers-ext": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.7.tgz", + "integrity": "sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ==", + "dev": true, + "dependencies": { + "es5-ext": "~0.10.46", + "next-tick": "1" + } + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/toposort-class": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toposort-class/-/toposort-class-1.0.1.tgz", + "integrity": "sha512-OsLcGGbYF3rMjPUf8oKktyvCiUxSbqMMS39m33MAjLTC1DVIH6x3WSt63/M77ihI09+Sdfk1AXvfhCEeUmC7mg==" + }, + "node_modules/touch": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", + "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", + "dev": true, + "dependencies": { + "nopt": "~1.0.10" + }, + "bin": { + "nodetouch": "bin/nodetouch.js" + } + }, + "node_modules/touch/node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true + }, + "node_modules/touch/node_modules/nopt": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", + "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==", + "dev": true, + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "*" + } + }, + "node_modules/type": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", + "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==", + "dev": true + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/umzug": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/umzug/-/umzug-2.3.0.tgz", + "integrity": "sha512-Z274K+e8goZK8QJxmbRPhl89HPO1K+ORFtm6rySPhFKfKc5GHhqdzD0SGhSWHkzoXasqJuItdhorSvY7/Cgflw==", + "dev": true, + "dependencies": { + "bluebird": "^3.7.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "dev": true + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/v8-to-istanbul": { + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.3.tgz", + "integrity": "sha512-9lDD+EVI2fjFsMWXc6dy5JJzBsVTcQ2fVkfBvncZ6xJWG9wtBhOldG+mHkSL0+V1K/xgZz0JDO5UT5hFwHUghg==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/validator": { + "version": "13.11.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.11.0.tgz", + "integrity": "sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wkx": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/wkx/-/wkx-0.5.0.tgz", + "integrity": "sha512-Xng/d4Ichh8uN4l0FToV/258EjMGU9MGcA0HV2d9B/ZpZB3lqQm7nkOdZdm5GhKtLLhAE7PiVQwN4eN+2YJJUg==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/write-file-atomic/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/server/package.json b/server/package.json new file mode 100644 index 0000000..678c787 --- /dev/null +++ b/server/package.json @@ -0,0 +1,27 @@ +{ + "name": "server", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "start": "nodemon bin/www" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "bcryptjs": "^2.4.3", + "express": "^4.18.2", + "jsonwebtoken": "^9.0.2", + "pg": "^8.11.3", + "sequelize": "^6.35.0" + }, + "devDependencies": { + "dotenv": "^16.3.1", + "jest": "^29.7.0", + "nodemon": "^3.0.1", + "sequelize-cli": "^6.6.2", + "supertest": "^6.3.3" + } +} diff --git a/server/seeders/20231114085507-user-seed.js b/server/seeders/20231114085507-user-seed.js new file mode 100644 index 0000000..ae25f38 --- /dev/null +++ b/server/seeders/20231114085507-user-seed.js @@ -0,0 +1,28 @@ +'use strict'; + +const { hashPassword } = require('../helpers/bcryptjs'); + +/** @type {import('sequelize-cli').Migration} */ +module.exports = { + async up (queryInterface, Sequelize) { + + const users = require("../data/user.json"); + users.forEach((el) => { + el.password = hashPassword(el.password); + el.createdAt = el.updatedAt = new Date(); + }) + + await queryInterface.bulkInsert("Users", users); + + }, + + async down (queryInterface, Sequelize) { + + await queryInterface.bulkDelete("Users", null, { + truncate: true, + cascade: true, + restartIdentity: true + }); + + } +}; From 8d7cf40b532daacf8faefd0c81f5e127a70d0ee6 Mon Sep 17 00:00:00 2001 From: pramesthias Date: Tue, 14 Nov 2023 16:43:01 +0700 Subject: [PATCH 02/28] feat: add login, register, error handler --- server/.env.example | 2 + server/.gitignore | 3 +- server/bin/www | 7 ++++ server/controllers/UserController.js | 56 ++++++++++++++++++++++++++++ server/helpers/jwt.js | 14 +++++++ server/middlewares/errorHandler.js | 19 ++++++++++ server/package.json | 2 +- server/routes/index.js | 14 +++++++ 8 files changed, 115 insertions(+), 2 deletions(-) create mode 100644 server/.env.example create mode 100644 server/bin/www create mode 100644 server/controllers/UserController.js create mode 100644 server/middlewares/errorHandler.js create mode 100644 server/routes/index.js diff --git a/server/.env.example b/server/.env.example new file mode 100644 index 0000000..0183256 --- /dev/null +++ b/server/.env.example @@ -0,0 +1,2 @@ +JWT_SECRET= +PORT= \ No newline at end of file diff --git a/server/.gitignore b/server/.gitignore index b512c09..1dcef2d 100644 --- a/server/.gitignore +++ b/server/.gitignore @@ -1 +1,2 @@ -node_modules \ No newline at end of file +node_modules +.env \ No newline at end of file diff --git a/server/bin/www b/server/bin/www new file mode 100644 index 0000000..81c186a --- /dev/null +++ b/server/bin/www @@ -0,0 +1,7 @@ +const app = require('../app'); + +const PORT = process.env.PORT || 3000; + +app.listen(PORT, () => { + console.log(`REST API running on PORT ${PORT}`) +}) diff --git a/server/controllers/UserController.js b/server/controllers/UserController.js new file mode 100644 index 0000000..1e7ca08 --- /dev/null +++ b/server/controllers/UserController.js @@ -0,0 +1,56 @@ +const { comparePassword } = require("../helpers/bcryptjs"); +const { signToken } = require("../helpers/jwt"); +const { User } = require("../models"); + +module.exports = class UserController { + + static async register(req, res, next){ + try { + const {username, email, password} = req.body + const user = await User.create({username, email, password}); + + res.status(201).json({ + "id": user.id, + }); + + } catch (error) { + next(error); + } + } + + + static async login(req, res, next){ + try { + const {email, password} = req.body; + + if(!email) { + res.status(400).json({message: "Error invalid email or password"}); + return; + } + + if(!password) { + res.status(400).json({message: "Error invalid email or password"}); + return; + } + + const user = await User.findOne({ where: {email} }); + if(!user){ + next({name: 'Unauthenticated', message: "User not found or Password not matched"}); + return; + } + + const isValidPassword = comparePassword(password, user.password); + if(!isValidPassword){ + next({name: 'Unauthenticated', message: "User not found or Password not matched"}); + return; + } + + const access_token = signToken({ id: user.id }); + res.status(200).json({ access_token }); + + } catch (error) { + next(error); + } + } + +} \ No newline at end of file diff --git a/server/helpers/jwt.js b/server/helpers/jwt.js index e69de29..db5a088 100644 --- a/server/helpers/jwt.js +++ b/server/helpers/jwt.js @@ -0,0 +1,14 @@ +const jwt = require('jsonwebtoken'); + +const JWT_SECRET = process.env.JWT_SECRET; + +const signToken = (payload) => { + return jwt.sign(payload, JWT_SECRET); +} + +const verifyToken = (token) => { + return jwt.verify(token, JWT_SECRET); +} + + +module.exports = {signToken, verifyToken}; \ No newline at end of file diff --git a/server/middlewares/errorHandler.js b/server/middlewares/errorHandler.js new file mode 100644 index 0000000..60238f5 --- /dev/null +++ b/server/middlewares/errorHandler.js @@ -0,0 +1,19 @@ +module.exports = function errorHandler(error, req, res, next) { + + switch (error.name) { + case "SequelizeValidationError": + case "SequelizeUniqueConstraintError": + res.status(400).json({ message: error.errors[0].message }); + break; + + case "JsonWebTokenError": + case "Unauthenticated": + res.status(401).json({ message: error.message ?? "Unauthenticated" }); + break; + + default: + res.status(500).json({ message: "Internal Server Error" }); + break; + } + +} \ No newline at end of file diff --git a/server/package.json b/server/package.json index 678c787..99c4dc4 100644 --- a/server/package.json +++ b/server/package.json @@ -4,7 +4,7 @@ "description": "", "main": "index.js", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", + "test": "npx jest --detectOpenHandles --forceExit --runInBand --coverage --silent", "start": "nodemon bin/www" }, "keywords": [], diff --git a/server/routes/index.js b/server/routes/index.js new file mode 100644 index 0000000..164b2de --- /dev/null +++ b/server/routes/index.js @@ -0,0 +1,14 @@ +const errorHandler = require('../middlewares/errorHandler'); + +const router = require('express').Router(); + +router.post("/login", UserController.login); //login + +router.post("/register", UserController.register); //register + + + +router.use(errorHandler); + + +module.exports = router; \ No newline at end of file From 7e1dde9029099c65da2de04f1ed73b98c588b702 Mon Sep 17 00:00:00 2001 From: pramesthias Date: Tue, 14 Nov 2023 16:51:16 +0700 Subject: [PATCH 03/28] feat: add authentication --- server/middlewares/authentication.js | 29 ++++++++++++++++++++++++++++ server/routes/index.js | 2 ++ 2 files changed, 31 insertions(+) create mode 100644 server/middlewares/authentication.js diff --git a/server/middlewares/authentication.js b/server/middlewares/authentication.js new file mode 100644 index 0000000..f8bc76e --- /dev/null +++ b/server/middlewares/authentication.js @@ -0,0 +1,29 @@ +const { User } = require("../models"); +const { verifyToken } = require("../helpers/jwt"); + +async function authentication(req, res, next){ + try { + let access_token = req.headers.authorization; + + if(!access_token) throw ({name: "Unauthenticated"}); + + const { id } = verifyToken(access_token.slice(7)); + + let user = await User.findByPk(id); + + if(!user) throw ({name: "Unauthenticated"}); + + req.user = { + id: user.id + } + + console.log(req.user) + next(); + + } catch (error) { + next(error); + console.log(error) + } +} + +module.exports = authentication \ No newline at end of file diff --git a/server/routes/index.js b/server/routes/index.js index 164b2de..de9d2b5 100644 --- a/server/routes/index.js +++ b/server/routes/index.js @@ -1,3 +1,4 @@ +const authentication = require('../middlewares/authentication'); const errorHandler = require('../middlewares/errorHandler'); const router = require('express').Router(); @@ -6,6 +7,7 @@ router.post("/login", UserController.login); //login router.post("/register", UserController.register); //register +router.use(authentication) router.use(errorHandler); From 6ff298642a6929c73d515af98234dd56299e3de5 Mon Sep 17 00:00:00 2001 From: pramesthias Date: Tue, 14 Nov 2023 18:26:50 +0700 Subject: [PATCH 04/28] fix --- server/controllers/UserController.js | 4 +++- server/routes/index.js | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/server/controllers/UserController.js b/server/controllers/UserController.js index 1e7ca08..bb1ee27 100644 --- a/server/controllers/UserController.js +++ b/server/controllers/UserController.js @@ -11,6 +11,7 @@ module.exports = class UserController { res.status(201).json({ "id": user.id, + "username": user.username }); } catch (error) { @@ -49,7 +50,8 @@ module.exports = class UserController { res.status(200).json({ access_token }); } catch (error) { - next(error); + // next(error); + console.log(error) } } diff --git a/server/routes/index.js b/server/routes/index.js index de9d2b5..959b393 100644 --- a/server/routes/index.js +++ b/server/routes/index.js @@ -1,3 +1,4 @@ +const UserController = require('../controllers/UserController'); const authentication = require('../middlewares/authentication'); const errorHandler = require('../middlewares/errorHandler'); From 7b9f1be1a557820c812f6d12471e916b9036d8a2 Mon Sep 17 00:00:00 2001 From: pramesthias Date: Tue, 14 Nov 2023 21:23:52 +0700 Subject: [PATCH 05/28] feat: get data from 3rd party API --- server/.env.example | 3 +- server/app.js | 6 +- server/controllers/RecipeController.js | 59 +++ server/controllers/UserController.js | 2 +- server/data/recipe.json | 670 +++++++++++++++++++++++++ server/package-lock.json | 41 +- server/package.json | 1 + server/routes/index.js | 5 +- 8 files changed, 776 insertions(+), 11 deletions(-) create mode 100644 server/controllers/RecipeController.js create mode 100644 server/data/recipe.json diff --git a/server/.env.example b/server/.env.example index 0183256..91672fb 100644 --- a/server/.env.example +++ b/server/.env.example @@ -1,2 +1,3 @@ JWT_SECRET= -PORT= \ No newline at end of file +PORT= +RECIPES_API_KEY= \ No newline at end of file diff --git a/server/app.js b/server/app.js index c3a3cec..f9d60f9 100644 --- a/server/app.js +++ b/server/app.js @@ -1,6 +1,6 @@ -// if(process.env.NODE_ENV !== "production"){ -// require('dotenv').config() //selalu ditulis di awal app.js & di pakai tahap dev -// } +if(process.env.NODE_ENV !== "production"){ + require('dotenv').config() //selalu ditulis di awal app.js & di pakai tahap dev + } const express = require('express'); const app = express(); diff --git a/server/controllers/RecipeController.js b/server/controllers/RecipeController.js new file mode 100644 index 0000000..01b02d0 --- /dev/null +++ b/server/controllers/RecipeController.js @@ -0,0 +1,59 @@ +const axios = require('axios'); + +module.exports = class RecipeController { + + + static async recipes(req, res, next) { + + const options = { + method: 'GET', + url: 'https://low-carb-recipes.p.rapidapi.com/search', + params: { + maxPrepareTime: '10', + maxCookTime: '20', + maxCalories: '500', + maxNetCarbs: '5', + maxSugar: '3', + maxAddedSugar: '0', + limit: '20' + }, + headers: { + 'X-RapidAPI-Key': `${process.env.RECIPES_API_KEY}`, + 'X-RapidAPI-Host': 'low-carb-recipes.p.rapidapi.com' + } + }; + + try { + const response = await axios.request(options); + const recipes = response.data; + const mappedRecipes = recipes.map((recipe, index) => { + return { + id: ++index, // Increment the counter and assign the new ID + name: recipe.name, + description: recipe.description, + prepareTime: recipe.prepareTime, + cookTime: recipe.cookTime, + ingredients: recipe.ingredients.map(ingredient => `${ingredient.name} ${ingredient.servingSize.desc}`), + steps: recipe.steps, + nutrients: { + caloriesKCal: recipe.nutrients.caloriesKCal, + totalCarbs: recipe.nutrients.totalCarbs, + sugar: recipe.nutrients.sugar, + protein: recipe.nutrients.protein, + fat: recipe.nutrients.fat, + cholesterol: recipe.nutrients.cholesterol, + alcohol: recipe.nutrients.alcohol, + gluten: recipe.nutrients.gluten, + }, + image: recipe.image, + } + }) + + res.status(200).json(mappedRecipes); + } catch (error) { + console.error(error); + } + } + + +} \ No newline at end of file diff --git a/server/controllers/UserController.js b/server/controllers/UserController.js index bb1ee27..5b6b0b1 100644 --- a/server/controllers/UserController.js +++ b/server/controllers/UserController.js @@ -50,7 +50,7 @@ module.exports = class UserController { res.status(200).json({ access_token }); } catch (error) { - // next(error); + next(error); console.log(error) } } diff --git a/server/data/recipe.json b/server/data/recipe.json new file mode 100644 index 0000000..88e5a01 --- /dev/null +++ b/server/data/recipe.json @@ -0,0 +1,670 @@ +[ + { + "id": 1, + "name": "Keto Sesame Tuna and Egg Salad", + "description": "This quick and easy tuna and egg salad is combined with aromatic scallions and parsley and coated in a sesame, lemon mayo.\n\nThis is a great option for stuffing lettuce wraps or topping low carb crackers.", + "prepareTime": 8, + "cookTime": 0, + "ingredients": [ + "Hard Boiled Egg 2 medium", + "Scallions 2 medium - 4 1/8\" long", + "Canned Tuna 1-¾ ounce", + "Lemon Juice, Fresh 1 teaspoon", + "Parsley, Fresh 1 tablespoon", + "Sesame Oil 1 teaspoon", + "Mayonnaise 1 tablespoon", + "Sesame Seeds, Hulled, Toasted, Unsalted 1 teaspoon, whole pieces", + "Salt, Sea Salt ⅛ teaspoon", + "Black Pepper ⅛ teaspoon" + ], + "steps": [ + "Peel and roughly chop the eggs and add to a mixing bowl.", + "Thinly slice the scallions and roughly chop the parsley. Add to the mixing bowl along with the tuna and stir well to combine.", + "Add the mayonnaise to a mixing bowl with the lemon juice, sesame oil, salt and pepper. Whisk together until smooth.", + "Spoon the dressing over the salad and stir well to coat.", + "Sprinkle over the sesame seeds to serve." + ], + "nutrients": { + "caloriesKCal": 170.313, + "totalCarbs": 2.366, + "sugar": 1.166, + "protein": 11.004, + "fat": 13.018, + "cholesterol": 175.938, + "alcohol": 0, + "gluten": 0 + }, + "image": "https://tinyurl.com/2p82zzca/133fcdec-4595-44be-a302-02df4c209451.png" + }, + { + "id": 2, + "name": "Keto Vanilla Butter Latte", + "description": "This creamy Keto vanilla BULLETPROOF® coffee recipe is rich in fats, frothy plant-based milk, and a hint of sweet vanilla. This Keto vanilla butter coffee makes a great fat-fueled breakfast option to kickstart your day. Alternatively, this is perfect served with a slice of low-carb cake as an afternoon treat!\n\n### What ingredients are in this Keto vanilla butter latte?\n\nThis Keto vanilla BULLETPROOF® coffee is prepared with hot almond milk infused with sweet vanilla extract. The milk is then blended with instant coffee and a hearty helping of unsalted butter, before being blitzed until super frothy! We have used a handheld stick blender to froth and blend our Keto coffee, however, you may use a freestanding blender for this if preferred.\n\n### Here are some tips to make Keto vanilla butter coffee?\n\nThis Keto BULLETPROOF® coffee provides a generous serving of fats from unsalted butter. If preferred you may swap the butter for coconut oil or BULLETPROOF® brain octane oil (MCT Oil) or even a blend of fats. You may also swap the erythritol for your preference in low-carb sweetener or omit it entirely if desired. Please be sure to adjust your macros for any changes made.\n\n### Can you use freshly brewed coffee in this Keto recipe?\n\nWhile this recipe calls for instant coffee, you can definitely substitute it for espresso or freshly brewed coffee, just make sure its strong.\n\nBULLETPROOF® is a registered trademark owned by Bulletproof Digital, Inc.", + "prepareTime": 8, + "cookTime": 3, + "ingredients": [ + "Butter, Unsalted 1-½ tablespoon", + "Almond Milk, Vanilla Or Other Flavors, Unsweetened 1-½ cup", + "Instant Coffee 1 teaspoon", + "Vanilla Extract ¼ teaspoon", + "100% Pure Erythritol by Now 1 tsp" + ], + "steps": [ + "Add the almond milk and vanilla extract to a small saucepan over low/medium heat. Stir to combine. Heat the milk through until piping hot but not boiling.", + "Add the instant coffee to a large mug. Add the granulated sweetener. Add the hot milk and stir well to combine, dissolving the coffee and sweetener.", + "Add the butter to the mug. Stir well to combine. Let the butter completely melt into the milky coffee.", + "Carefully use a hand held stick blender or milk frother and blend the coffee and butter. Continue blending until you have a thick frothy layer on the top of your coffee. Serve hot." + ], + "nutrients": { + "caloriesKCal": 203.555, + "totalCarbs": 5.903, + "sugar": 0.572, + "protein": 1.779, + "fat": 21.015, + "cholesterol": 45.755, + "alcohol": 0.631, + "gluten": 0 + }, + "image": "https://tinyurl.com/2p82zzca/ccef957b-351f-4c14-b33c-ef050045b877.png" + }, + { + "id": 3, + "name": "Keto Bird Nests", + "description": "This deliciously low-carb breakfast is light to eat and totally instagram-worthy! Spiralized zucchini noodles are gently seasoned and are each a vibrant bed to a single egg. Feel free to expand on this recipe and add additional seasonings or herbs. You can even add a little cheese for more fat and protein!\n\nJessica L.", + "prepareTime": 10, + "cookTime": 20, + "ingredients": [ + "Zucchini 8-½ ounce", + "Olive Oil 2-½ teaspoon", + "Raw Egg 4 large", + "Salt 1 teaspoon", + "Black Pepper 1 teaspoon" + ], + "steps": [ + "Preheat an oven to 350 degrees and line a sheet tray with aluminum foil or simply grease it. Spiralize a large zucchini, and break up the spirals slightly with your fingers.", + "Divide the zucchini into 4 sections (just slightly over 2 oz. each), and arrange them into “nests” on your sheet tray. Create a small space in the center of each nest. Then, drizzle the olive oil gently across the zucchini.", + "Crack an egg into each zucchini nest. Sprinkle about ¼ tsp each of the salt and pepper over each bird nest.", + "Bake the bird nests for approx 20 minutes. This is enough time for the eggs to fully cook and the zucchini to turn just slightly golden on the edges. Allow the tray to cool for 2-3 minutes before transferring the finished recipe to plates with a spatula.", + "You may cook the bird nests for less than 20 minutes, if you want runnier eggs!" + ], + "nutrients": { + "caloriesKCal": 114.047, + "totalCarbs": 2.801, + "sugar": 2.07, + "protein": 7.079, + "fat": 8.329, + "cholesterol": 186.5, + "alcohol": 0, + "gluten": 0 + }, + "image": "https://tinyurl.com/2p82zzca/348b12ac-3cf3-44a7-b193-4c686a07c0f2.png" + }, + { + "id": 4, + "name": "Keto Thai Spicy Peanut Chicken With Asian Slaw", + "description": "Do you love Thai food? This Thai-inspired chicken dish is a little spicy, a little nutty from some peanut butter, and tossed with fresh herbs. A cool and creamy Asian slaw is paired on the side to combat any heat from the chili sauce. You can have your lunch or dinner ready in 20 minutes or less! You can double this recipe for 4 days of meal prep as well.\n\nJessica L.", + "prepareTime": 10, + "cookTime": 8, + "ingredients": [ + "Packaged Salads Fresh Favorites Colorful Coleslaw by Dole 1 cups", + "Salt 0.25 teaspoon", + "Rice Vinegar 1 teaspoon", + "Mayo 1 tablespoon", + "Sesame Seeds ¼ teaspoon, whole pieces", + "Lime Juice, Fresh 1 wedge - juice from one wedge or slice", + "Boneless Skinless Chicken Breast 10 ounce", + "Olive Oil ½ tablespoon", + "Black Pepper 0.25 teaspoon", + "Peanut Butter 1 tablespoon", + "Sambal Oelek Paste Of Chili by Rooster 1 tablespoon", + "Soy Sauce 2 tablespoon", + "Cilantro ½ tablespoon, chopped", + "Basil 4 leaf", + "Lime Juice, Fresh 2 wedge - juice from one wedge or slice" + ], + "steps": [ + "In a bowl, combine the slaw mix, salt, vinegar, mayo, sesame seeds, and first amount of lime juice. Set this slaw mixture aside for later, giving the salt time to draw out the juices.", + "Trim any unwanted fat from your chicken, and chop the chicken into 1-inch cubes.", + "Heat the olive oil in a medium sized pan on medium-high heat. Toss in the chicken and black pepper, and cook these ingredients until the chicken just cooks through.", + "Use a wooden spoon to move the chicken the the outer edges of the pan and turn the heat to very low. Place the peanut butter, chili sauce, and soy sauce in the exposed center of the pan.", + "Let the heat melt the peanut butter slightly, then stir and combine the chicken and sauce into a consistent and creamy mixture.", + "Finally, chop the cilantro and basil and toss these with the chicken.", + "Serve the hot chicken with the cool slaw and two lime wedges. You can add extra cilantro as a garnish at your discretion. Enjoy!" + ], + "nutrients": { + "caloriesKCal": 394.486, + "totalCarbs": 5.723, + "sugar": 2.351, + "protein": 47.457, + "fat": 19.403, + "cholesterol": 123.372, + "alcohol": 0, + "gluten": 0 + }, + "image": "https://tinyurl.com/2p82zzca/2b3146d9-f265-48f1-8142-6d445ad8f4a8.png" + }, + { + "id": 5, + "name": "Keto Green Beans and Scallions", + "description": "These aromatic green beans are cooked with tender scallions, spicy chili flakes, a hint of zesty lime and sesame oil.\n\nThese are a great accompaniment to Asian inspired dishes or grilled fish.", + "prepareTime": 5, + "cookTime": 12, + "ingredients": [ + "Green Beans (string Beans), Raw 5 ounce", + "Lime Juice, Fresh 2 teaspoon", + "Scallions 2 medium - 4 1/8\" long", + "Olive Oil 1-½ tablespoon", + "Hot Chili Pepper, Dried, With Seeds 1 teaspoon", + "Sesame Oil 1 teaspoon", + "Salt, Sea Salt ¼ teaspoon", + "Black Pepper ⅛ teaspoon" + ], + "steps": [ + "Add the green beans to a pan of boiling water and simmer for 4-5 minutes until tender. Drain completely.", + "Thinly slice the scallions. Heat a tablespoon of olive oil in a skillet over a low/medium heat. Add the scallions and sauté for 2-3 minutes until tender.", + "Whisk together the remaining olive oil, sesame oil, lime juice, chili flakes, salt and pepper. Add the green beans to the pan and drizzle over the spiced oil. Stir well to combine and cook for a further minute or two to heat the beans through." + ], + "nutrients": { + "caloriesKCal": 70.591, + "totalCarbs": 3.578, + "sugar": 1.619, + "protein": 0.869, + "fat": 6.341, + "cholesterol": 0, + "alcohol": 0, + "gluten": 0 + }, + "image": "https://tinyurl.com/2p82zzca/1a69a125-7c33-4664-b4c2-f4fdddab8781.png" + }, + { + "id": 6, + "name": "Low Carb Spiced Bok Choy and Broccoli Soup", + "description": "This warming soup is packed with low carb broccoli and Bok choy, aromatic cumin, fiery ginger and topped with spicy chili flakes to serve.\n\nThis makes a great lunch or dinner option served with a slice of keto bread.", + "prepareTime": 8, + "cookTime": 13, + "ingredients": [ + "Vegetable Broth, Bouillon Or Consomme 4 cup", + "Cilantro 2 tablespoon", + "Bok Choy 1 cup", + "Olive Oil 1 tablespoon", + "Ginger Root, Raw 1 slice - 1\" diameter", + "Broccoli Florets 1 cups", + "Shallot 1 tablespoon, chopped", + "Garlic 1 clove", + "Hot Chili Pepper, Dried, With Seeds 1 teaspoon", + "Cumin, Seed ½ teaspoon" + ], + "steps": [ + "Thinly slice the shallot and garlic. Heat the oil in a large saucepan over a low/medium heat. Add the garlic and shallots and sweat gently until tender.", + "Slice the tough base from the Bok choy to separate the individual stalks. Cut the leaves from the stalks and reserve to one side. Grate the ginger and add to the pan along with the broccoli florets, Bok choy stalks and cumin seeds. Stir to combine and cook for 2-3 minutes to soften the vegetables and toast the cumin seeds.", + "Add the stock and bring to a gentle boil. Simmer for 5-6 minutes or until the vegetables are fork tender.", + "Add the Bok choy leaves and stir until just wilted. Transfer to a blender or use a handheld stick blender and process until smooth.", + "Scatter with dried chili flakes and fresh cilantro to serve." + ], + "nutrients": { + "caloriesKCal": 68, + "totalCarbs": 6.005, + "sugar": 1.978, + "protein": 2.983, + "fat": 3.912, + "cholesterol": 0, + "alcohol": 0, + "gluten": 0 + }, + "image": "https://tinyurl.com/2p82zzca/682207ec-7c88-4eb4-84d8-ddd1703d9346.png" + }, + { + "id": 7, + "name": "Keto Blue Cheese Dressing", + "description": "This quick and easy blue cheese dressing is rich, creamy and perfect for adding a boost of fats and flavor to your Keto meals. Simple to prepare, this blue cheese dressing does not require any cooking and is ready in just a matter of minutes. A great dressing to have on hand in the fridge, ready to drizzle over salads and meats or for dipping low carb vegetables.\n\n### Is Blue Cheese Dressing Keto?\n\nIt’s very simple to make your own Keto-friendly blue cheese dressing. Store-bought versions may contain added fillers and sugars that are not Keto-friendly, but our low carb dressing is so quick to prepare there’s no need to reach for shop-bought again! Our quick and easy Keto dressing combines natural yogurt, mayonnaise, a hint of lemon, a touch of garlic and tangy blue cheese. The ingredients are mixed together until creamy, creating a delicious and pourable dressing.\n\n### Serving Suggestions\n\nThis thick and creamy dressing is versatile in its uses. The dressing can be thinned as desired, perfect for drizzling over your favorite Keto salads. Alternatively, you can keep the dressing slightly thicker, perfect served with meats. We love to pair our blue cheese dressing with Keto burgers. It is also delicious added to salads with crispy bacon.", + "prepareTime": 8, + "cookTime": 0, + "ingredients": [ + "Water 1 tablespoon", + "Lemon Juice 1 teaspoon", + "Dannon All Natural Yogurt, Plain ⅓ cup", + "Mayonnaise ⅓ cup", + "Blue Cheese ⅓ cup", + "Black Pepper ⅛ teaspoon", + "Garlic, Powder ¼ teaspoon" + ], + "steps": [ + "Add the yogurt, mayonnaise and lemon juice to a mixing bowl. You may use a homemade or sugar-free store-bought mayonnaise. You may also use any plain yogurt you desire. Be sure to adjust your macros for any changes made. Whisk the yogurt, mayonnaise and lemon juice together to combine until smooth.", + "Crumble the blue cheese into the mayo and yogurt mix. You can crumble the cheese roughly so you have a mixture of large chunks and smaller pieces. Add the garlic powder and black pepper. You can omit or adjust the quantities of these seasonings as desired. Mix together well to combine, distributing the cheese evenly throughout the dressing. If you would prefer an entirely smooth dressing you can prepare this in a food processor, blending until smooth.", + "Add the water to the dressing and whisk well to combine. This can be omitted or increased to suit your personal preference. Add more water for a thinner salad dressing or omit entirely if you want a thicker dressing for topping meats and burgers. Refrigerate in an airtight container until ready to serve." + ], + "nutrients": { + "caloriesKCal": 211.721, + "totalCarbs": 1.823, + "sugar": 1.197, + "protein": 5.384, + "fat": 20.375, + "cholesterol": 25.964, + "alcohol": 0, + "gluten": 0 + }, + "image": "https://tinyurl.com/2p82zzca/2a4fac4d-b935-4360-b72c-934e34895681.png" + }, + { + "id": 8, + "name": "Keto One Pan Garlic Butter Chicken Thighs", + "description": "These simple one pan chicken thighs are cooked in a generous helping of butter alongside aromatic garlic, rosemary and lemon.\n\nPerfect for a quick and easy weeknight meal with minimal washing up!", + "prepareTime": 5, + "cookTime": 20, + "ingredients": [ + "Chicken Thigh, Skin Removed Before Cooking 4 medium", + "Garlic 3 clove", + "Unsalted Butter 2 tablespoon", + "Lemon Peel Or Zest Raw 2 tsp", + "Olive Oil 1 tablespoon", + "Rosemary 1 x 1 x sprig", + "Salt, Sea Salt ¼ teaspoon", + "Black Pepper ⅛ teaspoon" + ], + "steps": [ + "Heat the olive oil in a large frying pan or skillet over a medium heat and add the chicken thighs. Fry the thighs until they just start to colour.", + "Add the butter to the pan and continue to cook the chicken until golden brown all over. Set aside.", + "Reduce the heat, slice the garlic and add to the pan cooking gently in the butter until fragrant, taking care not to burn the butter or garlic.", + "Return the chicken thighs to the pan along with the lemon zest and tear in the rosemary.", + "Continue to cook over a medium heat, spooning the hot butter over the chicken until completely cooked through and no longer pink. Serve with steamed green beans or broccoli." + ], + "nutrients": { + "caloriesKCal": 418.52, + "totalCarbs": 1.96, + "sugar": 0.138, + "protein": 40.354, + "fat": 26.988, + "cholesterol": 214.822, + "alcohol": 0, + "gluten": 0 + }, + "image": "https://tinyurl.com/2p82zzca/1f11e666-3601-4f21-9cf2-c9c7427481b7.png" + }, + { + "id": 9, + "name": "Keto Tahini, Tamari Mushrooms", + "description": "These simple mushrooms are pan fried until tender then tossed with creamy tahini and tangy tamari sauce.\n\nThese make a great side dish to Asian inspired meals.", + "prepareTime": 3, + "cookTime": 8, + "ingredients": [ + "Mushrooms 6 medium", + "Coconut Oil 1 tablespoon", + "Tahini 1 teaspoon", + "Tamari Sauce 1 tablespoon" + ], + "steps": [ + "Melt the coconut oil in a skillet over a medium heat. Thinly slice the mushrooms and add to the skillet, cook through for 6-7 minutes until browned all over and tender.", + "Add the tahini and tamari and stir well, melting the tahini into the mushrooms. Cook briefly until the mushrooms are golden and coated in tamari and tahini. Serve hot. " + ], + "nutrients": { + "caloriesKCal": 91.746, + "totalCarbs": 2.683, + "sugar": 1.15, + "protein": 2.742, + "fat": 8.322, + "cholesterol": 0, + "alcohol": 0, + "gluten": 0.036 + }, + "image": "https://tinyurl.com/2p82zzca/e77c981f-193e-42d6-b250-47150568c661.png" + }, + { + "id": 10, + "name": "Keto Chocolate Cake Donuts", + "description": "I love how soft, moist and chocolatey these donuts are! They are full of rich chocolate flavor and great with a morning cup of coffee!", + "prepareTime": 10, + "cookTime": 15, + "ingredients": [ + "Almond Flour 1-¼ cup", + "Brown Sugar Replacement by Swerve ⅓ cup", + "Baking Powder 1 teaspoon", + "100% Cocoa Special Dark by Hershey's 3 tbsp", + "Extra Virgin Olive Oil 1-½ tablespoon", + "Original Coconut Milk Unsweetened by Silk 2 tablespoon", + "Raw Egg 2 large", + "Vanilla Extract 1 teaspoon", + "Coarse Kosher Salt by Morton ⅛ tsp", + "The Ultimate Icing Sugar Replacement by Swerve ⅓ cup", + "Original Coconut Milk Unsweetened by Silk 3 tablespoon", + "Vanilla Extract ¼ teaspoon" + ], + "steps": [ + "Preheat an oven to 350 F. Prepare a donut pan with nonstick cooking spray.", + "In a food processor, combine almond flour, sweetener, dark cocoa powder and baking powder together. Pulse to combine.", + "Add in the olive oil, milk, eggs, vanilla extract and ⅛ t kosher salt. Pulse to turn into a smooth batter. ", + "Spoon and smooth batter evenly into the donut pan to make 6 donuts. Being careful not to overfill the pan so as they rise the dough will not close the center. You can wipe the centers of the donuts to keep them free of batter.", + "Bake for 15 minutes or until cooked through. ", + "Allow donuts to cool in the pan for 15 minutes before removing to a cooling rack.", + "In the meantime, prepare the glaze by combining the rest of the Swerve, 3 tablespoons of coconut milk, ¼ t vanilla extract, and a pinch of salt. Dip the top of each donut into the glaze and then set aside on the rack to dry. Keep donuts stored in an airtight container for up to one week." + ], + "nutrients": { + "caloriesKCal": 207.871, + "totalCarbs": 14.628, + "sugar": 1.382, + "protein": 7.474, + "fat": 17.364, + "cholesterol": 62.167, + "alcohol": 0.311, + "gluten": 0 + }, + "image": "https://tinyurl.com/2p82zzca/fe58600a-a0b3-4b72-b0f1-691bd85f3198.png" + }, + { + "id": 11, + "name": "Keto Scrambled Egg and Tuna Avocado Boats", + "description": "As a delicious breakfast or lunch, soft avocado is overflowing with scrambled eggs and tender tuna. Despite the over-the-top tuna filling, melted layers of cheese act as the ‘glue’ to hold everything in place. Dig in with a spoon or fork!\n\nJessica L.", + "prepareTime": 8, + "cookTime": 9, + "ingredients": [ + "Raw Egg 2 large", + "Heavy Cream 1 tablespoon", + "Salt ⅛ teaspoon", + "Black Pepper, Ground ⅛ teaspoon", + "Onion Powder ⅛ teaspoon", + "Paprika ¼ teaspoon", + "Canned Tuna 1 can - each 5 ounce net weight", + "Avocado 4-½ ounce", + "Salt ⅛ teaspoon", + "Provolone Cheese 2 slice- each 3/4 ounce" + ], + "steps": [ + "Preheat an oven to 350 degrees. In a bowl, whisk together the egg, cream, first amount of salt, pepper, onion powder, and paprika. Set this aside near the stove for later.", + "In a small pan over medium heat, place in the drained tuna. Continually cook and toss the tuna until it starts to turn golden brown and dries out a little.", + "Turn the stove heat to low and pour the egg mixture into the pan. Slowly stir the egg and tuna until the egg scrambles and is evenly distributed with the tuna. You may wish to leave the egg slightly undercooked, since it will be cooking further in the oven for a few minutes later on.", + "Prepare a whole avocado by slicing it in half, removing the pit, and scooping out 1-2 tablespoons of fruit from the middle of each half. Sprinkle the second amount of salt over the avocado. Next, set the avocado halves on a sheet tray lined with foil or parchment paper. Use a spoon to fill each avocado half with the scrambled egg tuna filling. Gently pack the filling down, as it piles high. Finally, layer a slice of provolone cheese over each avocado half.", + "Bake the tray for just a few minutes - until the cheese is melted and the avocado has been warmed." + ], + "nutrients": { + "caloriesKCal": 340.14, + "totalCarbs": 7.084, + "sugar": 1.126, + "protein": 25.731, + "fat": 24.069, + "cholesterol": 232.615, + "alcohol": 0, + "gluten": 0 + }, + "image": "https://tinyurl.com/2p82zzca/a336c4de-5d90-4a89-90ce-e0bbee045c62.png" + }, + { + "id": 12, + "name": "Keto Instant Pot Beef and Onion Soup", + "description": "This easy Keto instant pot soup is prepared with just a handful of flavorsome ingredients and pressure cooked in only a matter of minutes. Both low in carbs and Paleo, this beef and onion soup makes a great warming lunch or dinner option. Perfect served with a slice of Keto bread for dipping.\n\n### How to Make Keto Soup\n\nKeto soup is a great addition to your weekly meal rotation. In order to create the best Keto soups, it is best to combine low carb veggies with a source of Keto friendly fats. For our Keto beef and onion soup we have used beef steak and olive oil to provide our fats and complemented this with onion and cauliflower for our veggies. The beef and onion are browned in the instant pot, then pressure cooked in stock for just 8 minutes until the beef is perfectly tender.\n\n### Serving Suggestions\n\nOur Paleo friendly instant pot soup makes a great lunch or light dinner option. We love our soup served with sliced and buttered Keto bread. This is also delicious accompanied by warm Keto bread rolls for dipping.", + "prepareTime": 5, + "cookTime": 8, + "ingredients": [ + "Beef Steak, Porterhouse, Visible Fat Eaten 9 ounce", + "Beef Broth, Bouillon Or Consomme, Ready-to-serve Can, Low Sodium 4 cup", + "Olive Oil 2 tablespoon", + "Cauliflower 2 cup", + "Onion 1 large", + "Garlic 1 clove" + ], + "steps": [ + "Add the olive oil to your instant pot. Set to sauté and allow the oil to get hot. Whilst the oil is heating, roughly slice the steak into half inch strips. Carefully add the steak strips to the instant pot. Using the sauté function, brown the beef all over.", + "Whilst the steak browns, thinly slice the onion and 1 large garlic clove. Add the sliced onion and garlic to the instant pot with the steak. Stir well to combine. Sweat the onion and garlic until tender and lightly golden. Take care not to burn the onion or garlic as this will impair the flavor of the soup.", + "Whilst the onion cooks, dice the cauliflower into bite sized chunks. Add the cauliflower pieces and the stock to the instant pot and stir well to combine. At this point you may season with salt and pepper if you wish. This will depend on the level of seasoning already in your stock. You may also use chicken or vegetable stock if preferred but be sure to adjust your macros for any changes.", + "Place the lid on the instant pot with the valve set to ‘sealing’ and set to high pressure. Set the time for 8 minutes. After this time, do a quick release to release the steam. Once the pressure has been released and it is safe to do so, open the lid and portion out the soup for serving. Taste and adjust seasonings if required." + ], + "nutrients": { + "caloriesKCal": 292.466, + "totalCarbs": 6.395, + "sugar": 2.887, + "protein": 20.826, + "fat": 20.381, + "cholesterol": 61.873, + "alcohol": 0, + "gluten": 0 + }, + "image": "https://tinyurl.com/2p82zzca/2d1b92c4-84fd-48ed-ba94-dcb6148170e9.png" + }, + { + "id": 13, + "name": "Keto Vegan Cilantro Lime Rice", + "description": "Keto Cilantro Lime Rice is the perfect Keto side dish for any Keto or Low Carb Mexican dinner recipe. Tender, sweet cauliflower rice is tossed with freshly squeezed lime juice and chopped cilantro. The best part is it is heated entirely in the microwave, which means this side dish only takes 5 minutes to prep and cook. Keto Cilantro Lime Rice is a great side dish to have ready-to-go in the fridge whenever hunger strikes; simply pair it with any protein of your choice.\n\n### What kind of cauliflower rice should I use?\n\nFeel free to make homemade cauliflower rice, but store-bought is the quickest way to put together this side dish with minimal effort.\n\nhttps://www.carbmanager.com/recipe-detail/ug:9bfcc61a-9ac3-c122-f2e4-bfaa142c0d6f/cauliflower-rice\n\n### What do I do if I do not have a microwave?\n\nYou can heat the cauliflower rice over medium-low heat until hot, and then add the lime and cilantro to the dish at the very end.\n\nWhat other flavors can I add to the dish?\n\n### Utilizing spices like dark chili powder or chipotle powder can spice up this side dish.\n\nServing suggestions\n\n### Keto Cilantro Lime Rice would be delicious with Keto Baja Pork Shoulder:\n\nhttps://www.carbmanager.com/recipe-detail/ug:3b26040619934242853ba66dfe696353/keto-baja-pork-shoulder", + "prepareTime": 0, + "cookTime": 0, + "ingredients": [ + "Cauliflower rice undefined", + "Cilantro undefined", + "Lime undefined", + "Salt, sea salt undefined" + ], + "steps": [ + "Place cauliflower rice into a medium-sized microwave-safe bowl. Microwave on high for 4 minutes. \n", + "In the meantime, wash and chop the cilantro leaves. Add them to the cauliflower rice once heated. Then squeeze on the lime juice and add the salt. Give the mixture a good toss. Taste adding more lime and salt, as desired. Serve immediately.\n" + ], + "nutrients": { + "caloriesKCal": 19.12, + "totalCarbs": 3.844, + "sugar": 1.628, + "protein": 1.391, + "fat": 0.343, + "cholesterol": 0, + "alcohol": 0, + "gluten": 0 + }, + "image": "https://tinyurl.com/2p82zzca/df9fd021-4abe-4c34-8f46-484e74e71ac5.png" + }, + { + "id": 14, + "name": "Low Carb Simple Ginger and Garlic Fried Cabbage", + "description": "This quick and easy cabbage is pan fried with tender red onion, aromatic garlic and fiery fresh ginger, drizzled with salty tamari sauce to serve.\n\nThis makes a great side to Asian inspired chicken, pork or fish.", + "prepareTime": 6, + "cookTime": 8, + "ingredients": [ + "Olive Oil 2 tablespoon", + "Tamari Sauce 1 tablespoon", + "Red Cabbage 1 cup", + "Cabbage, Green, Raw 1 cup", + "Ginger Root, Raw 1 slice - 1\" diameter", + "Garlic 1 clove", + "Red Onion ¼ medium - 2 1/2\" diameter" + ], + "steps": [ + "Thinly slice the onion and cabbage. Heat the oil in a skillet or wok over a medium/high heat and add the cabbage and onion, cooking for about three minutes to soften.", + "Grate the ginger and thinly slice the garlic. Add to the pan and stir well to combine. Pan fry over a medium heat for a further 2-3 minutes until the garlic is tender and fragrant and the cabbage is soft.", + "Stir through the tamari sauce to serve." + ], + "nutrients": { + "caloriesKCal": 78.549, + "totalCarbs": 4.115, + "sugar": 1.889, + "protein": 1.061, + "fat": 6.845, + "cholesterol": 0, + "alcohol": 0, + "gluten": 0.018 + }, + "image": "https://tinyurl.com/2p82zzca/dce25a9d-8be4-4c4b-afaa-f994ef227f06.png" + }, + { + "id": 15, + "name": "Keto Stir Fried Bell Peppers with Capers and Olives", + "description": "These simple stir fried peppers are cooked until soft and tender with salty capers and olives, aromatic garlic and a little twist of lemon.\n\nThis is a great topping for grilled meats or fish or added to salads.", + "prepareTime": 5, + "cookTime": 8, + "ingredients": [ + "Black Olives 8 medium", + "Olive Oil 1-½ tablespoon", + "Garlic 1 clove", + "Capers 1 tablespoon, whole pieces", + "Lemon Peel Or Zest Raw 1 tsp", + "Yellow Bell Peppers, Raw 1 medium - 2 1/2\" diameter x 2 3/4\"", + "Red Bell Peppers, Raw ½ medium - 2 1/2\" diameter x 2 3/4\"" + ], + "steps": [ + "Slice the bell peppers into thin strips. Heat the oil in a skillet over a medium heat and add the pepper strips, cooking for 3-4 minutes to soften.", + "Crush the garlic, slice the olives and roughly chop the capers. Add to the pan along with the lemon zest and stir well to combine. Cook for a further 2-3 minutes until the peppers are soft and the garlic is tender." + ], + "nutrients": { + "caloriesKCal": 91.258, + "totalCarbs": 4.948, + "sugar": 2.959, + "protein": 0.824, + "fat": 8.064, + "cholesterol": 0, + "alcohol": 0, + "gluten": 0 + }, + "image": "https://tinyurl.com/2p82zzca/01c3c96f-5d3b-4d0b-b870-9f29c20eef10.png" + }, + { + "id": 16, + "name": "Keto Parmesan, Almond and Chorizo Chips", + "description": "These quick and easy keto chips are loaded with smokey Spanish chorizo, aromatic rosemary, crunchy flaked almonds and crispy baked parmesan.\n\nThese are an excellent option for snacking and sharing.", + "prepareTime": 6, + "cookTime": 6, + "ingredients": [ + "Almonds, Raw 1-½ tablespoon, sliced", + "Fresh Food Spanish Chorizo by Sainsbury's 20 gram", + "Rosemary, Fresh 1 teaspoon", + "Parmesan Cheese ⅔ cup, grated", + "Black Pepper ⅛ teaspoon" + ], + "steps": [ + "Preheat the oven to 375 degrees Fahrenheit and line a shallow oven tray with baking paper.", + "Finely dice the chorizo (1.5 tbsp) and finely chop the rosemary. Add to a mixing bowl along with the parmesan, almonds and black pepper. Mix well to combine.", + "Spoon a heaped tablespoon of the mixture onto the lined baking tray then repeat 5 more times so you have six piles of mixture. Leave a gap between each and press the mixture into a rough circle shape with your hands.", + "Transfer to the oven to bake for 6 minutes or until hot through, golden and bubbling. Set aside to cool completely and firm." + ], + "nutrients": { + "caloriesKCal": 137.236, + "totalCarbs": 1.729, + "sugar": 0.453, + "protein": 11.068, + "fat": 9.603, + "cholesterol": 17.076, + "alcohol": 0, + "gluten": 0 + }, + "image": "https://tinyurl.com/2p82zzca/28f03845-6fec-427c-ae69-f537403d0ff4.png" + }, + { + "id": 17, + "name": "Keto Curried Cabbage", + "description": "This tender cabbage is loaded with aromatic spices, creamy butter and sweet red onion.\n\nThis is a great side to accompany curried meats, rotisserie chicken or lamb chops.", + "prepareTime": 6, + "cookTime": 16, + "ingredients": [ + "Cabbage 2 cup", + "Unsalted Butter 2 tablespoon", + "Garlic 1 clove", + "Cumin, Seed 1 teaspoon", + "Curry Powder 1 tablespoon", + "Vegetable Broth, Bouillon Or Consomme ½ cup", + "Red Onion ¼ medium - 2 1/2\" diameter" + ], + "steps": [ + "Heat a tablespoon of butter in a large skillet over a low/medium heat. Finely slice the onion and add to the skillet, cooking gently until tender.", + "Thinly slice the garlic and add to the pan along with the cumin seeds. Cook for a further minute or two until fragrant.", + "Thinly slice the cabbage and add to the skillet with the remaining butter and curry powder. Stir well to combine and cook over a medium heat for 4-5 minutes until the cabbage is just tender.", + "Add the vegetable stock and simmer for 5-6 minutes until all the liquid has evaporated and the cabbage is soft." + ], + "nutrients": { + "caloriesKCal": 80.354, + "totalCarbs": 6.385, + "sugar": 2.601, + "protein": 1.517, + "fat": 6.161, + "cholesterol": 15.252, + "alcohol": 0, + "gluten": 0 + }, + "image": "https://tinyurl.com/2p82zzca/f36344ed-2a4d-4627-9dd7-9fb604ce0f3d.png" + }, + { + "id": 18, + "name": "Keto Parmesan and Prosciutto Baked Avocado", + "description": "These simple baked avocado are rolled in grated parmesan and wrapped with prosciutto before cooking until crisp and golden.\n\nThese make a great snack between meals or party appetizer!", + "prepareTime": 7, + "cookTime": 10, + "ingredients": [ + "Prosciutto 4 slice", + "Avocado 1 each", + "Parmesan Cheese 1 tablespoon, grated" + ], + "steps": [ + "Preheat the oven to 400 degrees Fahrenheit.", + "Slice the avocado into 8 even wedges.", + "Sprinkle over the grated Parmesan and roll the avocado slices in the cheese, pressing gently so it sticks.", + "Tear each prosciutto slice in half and wrap a half around each avocado slice. Transfer to the oven and bake for 10 minutes until the prosciutto is crisp and golden." + ], + "nutrients": { + "caloriesKCal": 160.154, + "totalCarbs": 5.99, + "sugar": 0.232, + "protein": 6.756, + "fat": 13.05, + "cholesterol": 13.257, + "alcohol": 0, + "gluten": 0 + }, + "image": "https://tinyurl.com/2p82zzca/346c9866-26a3-46d2-8d8e-4259a84f6c26.png" + }, + { + "id": 19, + "name": "Keto Everything Celery Sticks", + "description": "This 5-minute snack is crunchy, creamy, and flavorful. Everything but the bagel is here in a snack, and the bagel itself is replaced with crunchy, refreshing celery sticks!\n\nJessica L.", + "prepareTime": 4, + "cookTime": 1, + "ingredients": [ + "Celery 4-½ ounce", + "Cream Cheese 3 ounce", + "Everything But The Bagel Sesame Seasoning Blend by Trader Joe's 2 tsp" + ], + "steps": [ + "Slice away the leaves and hard white base of 2 celery stalks. Cut each stalk in half to yield 4 pieces.", + "Microwave your cream cheese for 7-10 seconds to get warm. Mix the cream cheese with the everything seasoning.", + "Spoon the cream cheese mixture into each celery stick. Enjoy!" + ], + "nutrients": { + "caloriesKCal": 179.041, + "totalCarbs": 4.242, + "sugar": 2.453, + "protein": 3.056, + "fat": 14.755, + "cholesterol": 42.95, + "alcohol": 0, + "gluten": 0 + }, + "image": "https://tinyurl.com/2p82zzca/67b63b11-b5a2-44a8-92ec-33f3180a289e.png" + }, + { + "id": 20, + "name": "Low Carb Autumn Vegetable Medley", + "description": "There is so much delicious and colorful produce that is in peak season, come Autumn! Many of it can be enjoyed while on the keto diet. This recipe avoids any squash, but the other seasonal vegetables are bright, juicy, and flavorful. A pinch of nutmeg really sets the tone for a chilly atmosphere.\n\nJessica L.", + "prepareTime": 10, + "cookTime": 10, + "ingredients": [ + "Baby Carrots 2 ounce", + "Brussel Sprouts by Great Value 3 ounce", + "Cauliflower, Raw 4 ounce", + "Kale 2-¼ ounce", + "Butter 2 tablespoon", + "Salt 0.5 teaspoon", + "Black Pepper 0.5 teaspoon", + "Italian Seasoning 1 teaspoon", + "Garlic Powder 0.25 teaspoon", + "Onion Powder 0.25 teaspoon", + "Spices Nutmeg Ground ¼ tsp" + ], + "steps": [ + "Prepare all the vegetables first. Give the carrots a small chop. Quarter the brussel sprouts, removing any hard bottoms. Chop the cauliflower. You can chop the kale or simply rip it up with your hands, removing the stem.", + "Melt the butter in a large skillet over medium-low heat. Toss in the carrots, sprouts, and cauliflower. Leave the kale aside. Coat the veggies in the butter, then cover with a lid and cook on low for about 5 minutes. The firmer vegetables should be tender and the brussel sprouts should be bright green.", + "Sprinkle all the seasonings over the skillet. Then, place the kale on top. Replace the lid, then let the kale cook down on its own for 2-3 minutes on a low heat. Remove the lid, and continually stir and cook the ingredients until the kale has cooked down significantly and the vegetables have browned edges.", + "Serve hot!" + ], + "nutrients": { + "caloriesKCal": 83.614, + "totalCarbs": 6.582, + "sugar": 2.141, + "protein": 2.288, + "fat": 6.096, + "cholesterol": 15.252, + "alcohol": 0, + "gluten": 0 + }, + "image": "https://tinyurl.com/2p82zzca/1a7a2a70-b217-42d5-9a57-a103c089543b.png" + } +] \ No newline at end of file diff --git a/server/package-lock.json b/server/package-lock.json index fadbdfa..24393ef 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -9,6 +9,7 @@ "version": "1.0.0", "license": "ISC", "dependencies": { + "axios": "^1.6.1", "bcryptjs": "^2.4.3", "express": "^4.18.2", "jsonwebtoken": "^9.0.2", @@ -1404,8 +1405,7 @@ "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "node_modules/at-least-node": { "version": "1.0.0", @@ -1416,6 +1416,16 @@ "node": ">= 4.0.0" } }, + "node_modules/axios": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.1.tgz", + "integrity": "sha512-vfBmhDpKafglh0EldBEbVuoe7DyAavGSLWhuSm5ZSEKQnHhBf0xAAwybbNH1IkrJNGnS/VG4I5yxig1pCEXE4g==", + "dependencies": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/babel-jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", @@ -1968,7 +1978,6 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, "dependencies": { "delayed-stream": "~1.0.0" }, @@ -2144,7 +2153,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true, "engines": { "node": ">=0.4.0" } @@ -2564,6 +2572,25 @@ "node": ">=8" } }, + "node_modules/follow-redirects": { + "version": "1.15.3", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz", + "integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/foreground-child": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", @@ -2584,7 +2611,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dev": true, "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -4874,6 +4900,11 @@ "node": ">= 0.10" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/pstree.remy": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", diff --git a/server/package.json b/server/package.json index 99c4dc4..d5d9b3b 100644 --- a/server/package.json +++ b/server/package.json @@ -11,6 +11,7 @@ "author": "", "license": "ISC", "dependencies": { + "axios": "^1.6.1", "bcryptjs": "^2.4.3", "express": "^4.18.2", "jsonwebtoken": "^9.0.2", diff --git a/server/routes/index.js b/server/routes/index.js index 959b393..027e92d 100644 --- a/server/routes/index.js +++ b/server/routes/index.js @@ -1,15 +1,18 @@ +const RecipeController = require('../controllers/RecipeController'); const UserController = require('../controllers/UserController'); const authentication = require('../middlewares/authentication'); const errorHandler = require('../middlewares/errorHandler'); const router = require('express').Router(); -router.post("/login", UserController.login); //login router.post("/register", UserController.register); //register +router.post("/login", UserController.login); //login + router.use(authentication) +router.get("/recipes", RecipeController.recipes); router.use(errorHandler); From ade694804fa27e69e5a96fce4b6858d3399ab41c Mon Sep 17 00:00:00 2001 From: pramesthias Date: Wed, 15 Nov 2023 05:37:59 +0700 Subject: [PATCH 06/28] add READ All and by Id from DB --- server/controllers/RecipeController.js | 183 +++++-- server/data/recipe.json | 469 ++++++------------ server/middlewares/errorHandler.js | 4 + .../20231114142906-create-recipe.js | 49 ++ server/models/recipe.js | 28 ++ server/routes/index.js | 8 + server/seeders/20231114143111-seed-recipes.js | 22 + 7 files changed, 387 insertions(+), 376 deletions(-) create mode 100644 server/migrations/20231114142906-create-recipe.js create mode 100644 server/models/recipe.js create mode 100644 server/seeders/20231114143111-seed-recipes.js diff --git a/server/controllers/RecipeController.js b/server/controllers/RecipeController.js index 01b02d0..474b543 100644 --- a/server/controllers/RecipeController.js +++ b/server/controllers/RecipeController.js @@ -1,59 +1,148 @@ const axios = require('axios'); +const { Recipe } = require("../models"); //DEVELOPMENT module.exports = class RecipeController { + static async recipes(req, res, next) { - static async recipes(req, res, next) { + const options = { + method: 'GET', + url: 'https://low-carb-recipes.p.rapidapi.com/search', + params: { + maxPrepareTime: '10', + maxCookTime: '20', + maxCalories: '500', + maxNetCarbs: '5', + maxSugar: '3', + maxAddedSugar: '0', + limit: '20' + }, + headers: { + 'X-RapidAPI-Key': `${process.env.RECIPES_API_KEY}`, + 'X-RapidAPI-Host': 'low-carb-recipes.p.rapidapi.com' + } + }; - const options = { - method: 'GET', - url: 'https://low-carb-recipes.p.rapidapi.com/search', - params: { - maxPrepareTime: '10', - maxCookTime: '20', - maxCalories: '500', - maxNetCarbs: '5', - maxSugar: '3', - maxAddedSugar: '0', - limit: '20' + try { + const response = await axios.request(options); + const recipes = response.data; + const mappedRecipes = recipes.map((recipe, index) => { + return { + id: ++index, // Increment the counter and assign the new ID + name: recipe.name, + description: recipe.description, + prepareTime: recipe.prepareTime, + cookTime: recipe.cookTime, + ingredients: recipe.ingredients.map(ingredient => `${ingredient.name} ${ingredient.servingSize.desc}`), + steps: recipe.steps, + nutrients: { + caloriesKCal: recipe.nutrients.caloriesKCal, + totalCarbs: recipe.nutrients.totalCarbs, + sugar: recipe.nutrients.sugar, + protein: recipe.nutrients.protein, + fat: recipe.nutrients.fat, + cholesterol: recipe.nutrients.cholesterol, + alcohol: recipe.nutrients.alcohol, + gluten: recipe.nutrients.gluten, }, - headers: { - 'X-RapidAPI-Key': `${process.env.RECIPES_API_KEY}`, - 'X-RapidAPI-Host': 'low-carb-recipes.p.rapidapi.com' - } - }; - - try { - const response = await axios.request(options); - const recipes = response.data; - const mappedRecipes = recipes.map((recipe, index) => { - return { - id: ++index, // Increment the counter and assign the new ID - name: recipe.name, - description: recipe.description, - prepareTime: recipe.prepareTime, - cookTime: recipe.cookTime, - ingredients: recipe.ingredients.map(ingredient => `${ingredient.name} ${ingredient.servingSize.desc}`), - steps: recipe.steps, - nutrients: { - caloriesKCal: recipe.nutrients.caloriesKCal, - totalCarbs: recipe.nutrients.totalCarbs, - sugar: recipe.nutrients.sugar, - protein: recipe.nutrients.protein, - fat: recipe.nutrients.fat, - cholesterol: recipe.nutrients.cholesterol, - alcohol: recipe.nutrients.alcohol, - gluten: recipe.nutrients.gluten, - }, - image: recipe.image, - } - }) - - res.status(200).json(mappedRecipes); - } catch (error) { - console.error(error); + image: recipe.image, } + }) + + res.status(200).json(mappedRecipes); + } catch (error) { + next(error); + } + } + + + static async recipeById(req, res, next) { + + const options = { + method: 'GET', + url: 'https://low-carb-recipes.p.rapidapi.com/search', + params: { + maxPrepareTime: '10', + maxCookTime: '20', + maxCalories: '500', + maxNetCarbs: '5', + maxSugar: '3', + maxAddedSugar: '0', + limit: '20' + }, + headers: { + 'X-RapidAPI-Key': `${process.env.RECIPES_API_KEY}`, + 'X-RapidAPI-Host': 'low-carb-recipes.p.rapidapi.com' + } + }; + + try { + const response = await axios.request(options); + const recipes = response.data; + const mappedRecipes = recipes.map((recipe, index) => { + return { + id: ++index, // Increment the counter and assign the new ID + name: recipe.name, + description: recipe.description, + prepareTime: recipe.prepareTime, + cookTime: recipe.cookTime, + ingredients: recipe.ingredients.map(ingredient => `${ingredient.name} ${ingredient.servingSize.desc}`), + steps: recipe.steps, + nutrients: { + caloriesKCal: recipe.nutrients.caloriesKCal, + totalCarbs: recipe.nutrients.totalCarbs, + sugar: recipe.nutrients.sugar, + protein: recipe.nutrients.protein, + fat: recipe.nutrients.fat, + cholesterol: recipe.nutrients.cholesterol, + alcohol: recipe.nutrients.alcohol, + gluten: recipe.nutrients.gluten, + }, + image: recipe.image, + } + }) + + const recipe = await mappedRecipes.findByPk(req.params.id) + console.log(recipe) + res.status(200).json(recipe); + } catch (error) { + // next(error); + console.log(error) + } + } + + + + + + /* + ==================== DEVELOPMENT ======================== + */ + static async getRecDb(req, res, next) { + try { + const recipes = await Recipe.findAll(); + res.status(200).json(recipes); + } catch (error) { + next(error); + } + } + + static async getRecDbId(req, res, next) { + try { + const { id } = req.params; + const recipes = await Recipe.findByPk(id); + + if (!recipes) { + next({ name: 'NotFound', message: "Recipe not found" }); + return; + } + + res.status(200).json(recipes); + } catch (error) { + next(error); } + } +//================================================================ } \ No newline at end of file diff --git a/server/data/recipe.json b/server/data/recipe.json index 88e5a01..4415c87 100644 --- a/server/data/recipe.json +++ b/server/data/recipe.json @@ -24,16 +24,16 @@ "Spoon the dressing over the salad and stir well to coat.", "Sprinkle over the sesame seeds to serve." ], - "nutrients": { - "caloriesKCal": 170.313, - "totalCarbs": 2.366, - "sugar": 1.166, - "protein": 11.004, - "fat": 13.018, - "cholesterol": 175.938, - "alcohol": 0, - "gluten": 0 - }, + "nutrients": [ + "caloriesKCal", + "totalCarbs", + "sugar", + "protein", + "fat", + "cholesterol", + "alcohol", + "gluten" + ], "image": "https://tinyurl.com/2p82zzca/133fcdec-4595-44be-a302-02df4c209451.png" }, { @@ -55,16 +55,16 @@ "Add the butter to the mug. Stir well to combine. Let the butter completely melt into the milky coffee.", "Carefully use a hand held stick blender or milk frother and blend the coffee and butter. Continue blending until you have a thick frothy layer on the top of your coffee. Serve hot." ], - "nutrients": { - "caloriesKCal": 203.555, - "totalCarbs": 5.903, - "sugar": 0.572, - "protein": 1.779, - "fat": 21.015, - "cholesterol": 45.755, - "alcohol": 0.631, - "gluten": 0 - }, + "nutrients": [ + "caloriesKCal", + "totalCarbs", + "sugar", + "protein", + "fat", + "cholesterol", + "alcohol", + "gluten" + ], "image": "https://tinyurl.com/2p82zzca/ccef957b-351f-4c14-b33c-ef050045b877.png" }, { @@ -87,16 +87,16 @@ "Bake the bird nests for approx 20 minutes. This is enough time for the eggs to fully cook and the zucchini to turn just slightly golden on the edges. Allow the tray to cool for 2-3 minutes before transferring the finished recipe to plates with a spatula.", "You may cook the bird nests for less than 20 minutes, if you want runnier eggs!" ], - "nutrients": { - "caloriesKCal": 114.047, - "totalCarbs": 2.801, - "sugar": 2.07, - "protein": 7.079, - "fat": 8.329, - "cholesterol": 186.5, - "alcohol": 0, - "gluten": 0 - }, + "nutrients": [ + "caloriesKCal", + "totalCarbs", + "sugar", + "protein", + "fat", + "cholesterol", + "alcohol", + "gluten" + ], "image": "https://tinyurl.com/2p82zzca/348b12ac-3cf3-44a7-b193-4c686a07c0f2.png" }, { @@ -131,16 +131,16 @@ "Finally, chop the cilantro and basil and toss these with the chicken.", "Serve the hot chicken with the cool slaw and two lime wedges. You can add extra cilantro as a garnish at your discretion. Enjoy!" ], - "nutrients": { - "caloriesKCal": 394.486, - "totalCarbs": 5.723, - "sugar": 2.351, - "protein": 47.457, - "fat": 19.403, - "cholesterol": 123.372, - "alcohol": 0, - "gluten": 0 - }, + "nutrients": [ + "caloriesKCal", + "totalCarbs", + "sugar", + "protein", + "fat", + "cholesterol", + "alcohol", + "gluten" + ], "image": "https://tinyurl.com/2p82zzca/2b3146d9-f265-48f1-8142-6d445ad8f4a8.png" }, { @@ -164,16 +164,16 @@ "Thinly slice the scallions. Heat a tablespoon of olive oil in a skillet over a low/medium heat. Add the scallions and sauté for 2-3 minutes until tender.", "Whisk together the remaining olive oil, sesame oil, lime juice, chili flakes, salt and pepper. Add the green beans to the pan and drizzle over the spiced oil. Stir well to combine and cook for a further minute or two to heat the beans through." ], - "nutrients": { - "caloriesKCal": 70.591, - "totalCarbs": 3.578, - "sugar": 1.619, - "protein": 0.869, - "fat": 6.341, - "cholesterol": 0, - "alcohol": 0, - "gluten": 0 - }, + "nutrients": [ + "caloriesKCal", + "totalCarbs", + "sugar", + "protein", + "fat", + "cholesterol", + "alcohol", + "gluten" + ], "image": "https://tinyurl.com/2p82zzca/1a69a125-7c33-4664-b4c2-f4fdddab8781.png" }, { @@ -201,16 +201,16 @@ "Add the Bok choy leaves and stir until just wilted. Transfer to a blender or use a handheld stick blender and process until smooth.", "Scatter with dried chili flakes and fresh cilantro to serve." ], - "nutrients": { - "caloriesKCal": 68, - "totalCarbs": 6.005, - "sugar": 1.978, - "protein": 2.983, - "fat": 3.912, - "cholesterol": 0, - "alcohol": 0, - "gluten": 0 - }, + "nutrients": [ + "caloriesKCal", + "totalCarbs", + "sugar", + "protein", + "fat", + "cholesterol", + "alcohol", + "gluten" + ], "image": "https://tinyurl.com/2p82zzca/682207ec-7c88-4eb4-84d8-ddd1703d9346.png" }, { @@ -233,16 +233,16 @@ "Crumble the blue cheese into the mayo and yogurt mix. You can crumble the cheese roughly so you have a mixture of large chunks and smaller pieces. Add the garlic powder and black pepper. You can omit or adjust the quantities of these seasonings as desired. Mix together well to combine, distributing the cheese evenly throughout the dressing. If you would prefer an entirely smooth dressing you can prepare this in a food processor, blending until smooth.", "Add the water to the dressing and whisk well to combine. This can be omitted or increased to suit your personal preference. Add more water for a thinner salad dressing or omit entirely if you want a thicker dressing for topping meats and burgers. Refrigerate in an airtight container until ready to serve." ], - "nutrients": { - "caloriesKCal": 211.721, - "totalCarbs": 1.823, - "sugar": 1.197, - "protein": 5.384, - "fat": 20.375, - "cholesterol": 25.964, - "alcohol": 0, - "gluten": 0 - }, + "nutrients": [ + "caloriesKCal", + "totalCarbs", + "sugar", + "protein", + "fat", + "cholesterol", + "alcohol", + "gluten" + ], "image": "https://tinyurl.com/2p82zzca/2a4fac4d-b935-4360-b72c-934e34895681.png" }, { @@ -268,16 +268,16 @@ "Return the chicken thighs to the pan along with the lemon zest and tear in the rosemary.", "Continue to cook over a medium heat, spooning the hot butter over the chicken until completely cooked through and no longer pink. Serve with steamed green beans or broccoli." ], - "nutrients": { - "caloriesKCal": 418.52, - "totalCarbs": 1.96, - "sugar": 0.138, - "protein": 40.354, - "fat": 26.988, - "cholesterol": 214.822, - "alcohol": 0, - "gluten": 0 - }, + "nutrients": [ + "caloriesKCal", + "totalCarbs", + "sugar", + "protein", + "fat", + "cholesterol", + "alcohol", + "gluten" + ], "image": "https://tinyurl.com/2p82zzca/1f11e666-3601-4f21-9cf2-c9c7427481b7.png" }, { @@ -296,16 +296,16 @@ "Melt the coconut oil in a skillet over a medium heat. Thinly slice the mushrooms and add to the skillet, cook through for 6-7 minutes until browned all over and tender.", "Add the tahini and tamari and stir well, melting the tahini into the mushrooms. Cook briefly until the mushrooms are golden and coated in tamari and tahini. Serve hot. " ], - "nutrients": { - "caloriesKCal": 91.746, - "totalCarbs": 2.683, - "sugar": 1.15, - "protein": 2.742, - "fat": 8.322, - "cholesterol": 0, - "alcohol": 0, - "gluten": 0.036 - }, + "nutrients": [ + "caloriesKCal", + "totalCarbs", + "sugar", + "protein", + "fat", + "cholesterol", + "alcohol", + "gluten" + ], "image": "https://tinyurl.com/2p82zzca/e77c981f-193e-42d6-b250-47150568c661.png" }, { @@ -337,16 +337,16 @@ "Allow donuts to cool in the pan for 15 minutes before removing to a cooling rack.", "In the meantime, prepare the glaze by combining the rest of the Swerve, 3 tablespoons of coconut milk, ¼ t vanilla extract, and a pinch of salt. Dip the top of each donut into the glaze and then set aside on the rack to dry. Keep donuts stored in an airtight container for up to one week." ], - "nutrients": { - "caloriesKCal": 207.871, - "totalCarbs": 14.628, - "sugar": 1.382, - "protein": 7.474, - "fat": 17.364, - "cholesterol": 62.167, - "alcohol": 0.311, - "gluten": 0 - }, + "nutrients": [ + "caloriesKCal", + "totalCarbs", + "sugar", + "protein", + "fat", + "cholesterol", + "alcohol", + "gluten" + ], "image": "https://tinyurl.com/2p82zzca/fe58600a-a0b3-4b72-b0f1-691bd85f3198.png" }, { @@ -374,16 +374,16 @@ "Prepare a whole avocado by slicing it in half, removing the pit, and scooping out 1-2 tablespoons of fruit from the middle of each half. Sprinkle the second amount of salt over the avocado. Next, set the avocado halves on a sheet tray lined with foil or parchment paper. Use a spoon to fill each avocado half with the scrambled egg tuna filling. Gently pack the filling down, as it piles high. Finally, layer a slice of provolone cheese over each avocado half.", "Bake the tray for just a few minutes - until the cheese is melted and the avocado has been warmed." ], - "nutrients": { - "caloriesKCal": 340.14, - "totalCarbs": 7.084, - "sugar": 1.126, - "protein": 25.731, - "fat": 24.069, - "cholesterol": 232.615, - "alcohol": 0, - "gluten": 0 - }, + "nutrients": [ + "caloriesKCal", + "totalCarbs", + "sugar", + "protein", + "fat", + "cholesterol", + "alcohol", + "gluten" + ], "image": "https://tinyurl.com/2p82zzca/a336c4de-5d90-4a89-90ce-e0bbee045c62.png" }, { @@ -406,16 +406,16 @@ "Whilst the onion cooks, dice the cauliflower into bite sized chunks. Add the cauliflower pieces and the stock to the instant pot and stir well to combine. At this point you may season with salt and pepper if you wish. This will depend on the level of seasoning already in your stock. You may also use chicken or vegetable stock if preferred but be sure to adjust your macros for any changes.", "Place the lid on the instant pot with the valve set to ‘sealing’ and set to high pressure. Set the time for 8 minutes. After this time, do a quick release to release the steam. Once the pressure has been released and it is safe to do so, open the lid and portion out the soup for serving. Taste and adjust seasonings if required." ], - "nutrients": { - "caloriesKCal": 292.466, - "totalCarbs": 6.395, - "sugar": 2.887, - "protein": 20.826, - "fat": 20.381, - "cholesterol": 61.873, - "alcohol": 0, - "gluten": 0 - }, + "nutrients": [ + "caloriesKCal", + "totalCarbs", + "sugar", + "protein", + "fat", + "cholesterol", + "alcohol", + "gluten" + ], "image": "https://tinyurl.com/2p82zzca/2d1b92c4-84fd-48ed-ba94-dcb6148170e9.png" }, { @@ -434,16 +434,16 @@ "Place cauliflower rice into a medium-sized microwave-safe bowl. Microwave on high for 4 minutes. \n", "In the meantime, wash and chop the cilantro leaves. Add them to the cauliflower rice once heated. Then squeeze on the lime juice and add the salt. Give the mixture a good toss. Taste adding more lime and salt, as desired. Serve immediately.\n" ], - "nutrients": { - "caloriesKCal": 19.12, - "totalCarbs": 3.844, - "sugar": 1.628, - "protein": 1.391, - "fat": 0.343, - "cholesterol": 0, - "alcohol": 0, - "gluten": 0 - }, + "nutrients": [ + "caloriesKCal", + "totalCarbs", + "sugar", + "protein", + "fat", + "cholesterol", + "alcohol", + "gluten" + ], "image": "https://tinyurl.com/2p82zzca/df9fd021-4abe-4c34-8f46-484e74e71ac5.png" }, { @@ -466,205 +466,16 @@ "Grate the ginger and thinly slice the garlic. Add to the pan and stir well to combine. Pan fry over a medium heat for a further 2-3 minutes until the garlic is tender and fragrant and the cabbage is soft.", "Stir through the tamari sauce to serve." ], - "nutrients": { - "caloriesKCal": 78.549, - "totalCarbs": 4.115, - "sugar": 1.889, - "protein": 1.061, - "fat": 6.845, - "cholesterol": 0, - "alcohol": 0, - "gluten": 0.018 - }, - "image": "https://tinyurl.com/2p82zzca/dce25a9d-8be4-4c4b-afaa-f994ef227f06.png" - }, - { - "id": 15, - "name": "Keto Stir Fried Bell Peppers with Capers and Olives", - "description": "These simple stir fried peppers are cooked until soft and tender with salty capers and olives, aromatic garlic and a little twist of lemon.\n\nThis is a great topping for grilled meats or fish or added to salads.", - "prepareTime": 5, - "cookTime": 8, - "ingredients": [ - "Black Olives 8 medium", - "Olive Oil 1-½ tablespoon", - "Garlic 1 clove", - "Capers 1 tablespoon, whole pieces", - "Lemon Peel Or Zest Raw 1 tsp", - "Yellow Bell Peppers, Raw 1 medium - 2 1/2\" diameter x 2 3/4\"", - "Red Bell Peppers, Raw ½ medium - 2 1/2\" diameter x 2 3/4\"" - ], - "steps": [ - "Slice the bell peppers into thin strips. Heat the oil in a skillet over a medium heat and add the pepper strips, cooking for 3-4 minutes to soften.", - "Crush the garlic, slice the olives and roughly chop the capers. Add to the pan along with the lemon zest and stir well to combine. Cook for a further 2-3 minutes until the peppers are soft and the garlic is tender." - ], - "nutrients": { - "caloriesKCal": 91.258, - "totalCarbs": 4.948, - "sugar": 2.959, - "protein": 0.824, - "fat": 8.064, - "cholesterol": 0, - "alcohol": 0, - "gluten": 0 - }, - "image": "https://tinyurl.com/2p82zzca/01c3c96f-5d3b-4d0b-b870-9f29c20eef10.png" - }, - { - "id": 16, - "name": "Keto Parmesan, Almond and Chorizo Chips", - "description": "These quick and easy keto chips are loaded with smokey Spanish chorizo, aromatic rosemary, crunchy flaked almonds and crispy baked parmesan.\n\nThese are an excellent option for snacking and sharing.", - "prepareTime": 6, - "cookTime": 6, - "ingredients": [ - "Almonds, Raw 1-½ tablespoon, sliced", - "Fresh Food Spanish Chorizo by Sainsbury's 20 gram", - "Rosemary, Fresh 1 teaspoon", - "Parmesan Cheese ⅔ cup, grated", - "Black Pepper ⅛ teaspoon" + "nutrients": [ + "caloriesKCal", + "totalCarbs", + "sugar", + "protein", + "fat", + "cholesterol", + "alcohol", + "gluten" ], - "steps": [ - "Preheat the oven to 375 degrees Fahrenheit and line a shallow oven tray with baking paper.", - "Finely dice the chorizo (1.5 tbsp) and finely chop the rosemary. Add to a mixing bowl along with the parmesan, almonds and black pepper. Mix well to combine.", - "Spoon a heaped tablespoon of the mixture onto the lined baking tray then repeat 5 more times so you have six piles of mixture. Leave a gap between each and press the mixture into a rough circle shape with your hands.", - "Transfer to the oven to bake for 6 minutes or until hot through, golden and bubbling. Set aside to cool completely and firm." - ], - "nutrients": { - "caloriesKCal": 137.236, - "totalCarbs": 1.729, - "sugar": 0.453, - "protein": 11.068, - "fat": 9.603, - "cholesterol": 17.076, - "alcohol": 0, - "gluten": 0 - }, - "image": "https://tinyurl.com/2p82zzca/28f03845-6fec-427c-ae69-f537403d0ff4.png" - }, - { - "id": 17, - "name": "Keto Curried Cabbage", - "description": "This tender cabbage is loaded with aromatic spices, creamy butter and sweet red onion.\n\nThis is a great side to accompany curried meats, rotisserie chicken or lamb chops.", - "prepareTime": 6, - "cookTime": 16, - "ingredients": [ - "Cabbage 2 cup", - "Unsalted Butter 2 tablespoon", - "Garlic 1 clove", - "Cumin, Seed 1 teaspoon", - "Curry Powder 1 tablespoon", - "Vegetable Broth, Bouillon Or Consomme ½ cup", - "Red Onion ¼ medium - 2 1/2\" diameter" - ], - "steps": [ - "Heat a tablespoon of butter in a large skillet over a low/medium heat. Finely slice the onion and add to the skillet, cooking gently until tender.", - "Thinly slice the garlic and add to the pan along with the cumin seeds. Cook for a further minute or two until fragrant.", - "Thinly slice the cabbage and add to the skillet with the remaining butter and curry powder. Stir well to combine and cook over a medium heat for 4-5 minutes until the cabbage is just tender.", - "Add the vegetable stock and simmer for 5-6 minutes until all the liquid has evaporated and the cabbage is soft." - ], - "nutrients": { - "caloriesKCal": 80.354, - "totalCarbs": 6.385, - "sugar": 2.601, - "protein": 1.517, - "fat": 6.161, - "cholesterol": 15.252, - "alcohol": 0, - "gluten": 0 - }, - "image": "https://tinyurl.com/2p82zzca/f36344ed-2a4d-4627-9dd7-9fb604ce0f3d.png" - }, - { - "id": 18, - "name": "Keto Parmesan and Prosciutto Baked Avocado", - "description": "These simple baked avocado are rolled in grated parmesan and wrapped with prosciutto before cooking until crisp and golden.\n\nThese make a great snack between meals or party appetizer!", - "prepareTime": 7, - "cookTime": 10, - "ingredients": [ - "Prosciutto 4 slice", - "Avocado 1 each", - "Parmesan Cheese 1 tablespoon, grated" - ], - "steps": [ - "Preheat the oven to 400 degrees Fahrenheit.", - "Slice the avocado into 8 even wedges.", - "Sprinkle over the grated Parmesan and roll the avocado slices in the cheese, pressing gently so it sticks.", - "Tear each prosciutto slice in half and wrap a half around each avocado slice. Transfer to the oven and bake for 10 minutes until the prosciutto is crisp and golden." - ], - "nutrients": { - "caloriesKCal": 160.154, - "totalCarbs": 5.99, - "sugar": 0.232, - "protein": 6.756, - "fat": 13.05, - "cholesterol": 13.257, - "alcohol": 0, - "gluten": 0 - }, - "image": "https://tinyurl.com/2p82zzca/346c9866-26a3-46d2-8d8e-4259a84f6c26.png" - }, - { - "id": 19, - "name": "Keto Everything Celery Sticks", - "description": "This 5-minute snack is crunchy, creamy, and flavorful. Everything but the bagel is here in a snack, and the bagel itself is replaced with crunchy, refreshing celery sticks!\n\nJessica L.", - "prepareTime": 4, - "cookTime": 1, - "ingredients": [ - "Celery 4-½ ounce", - "Cream Cheese 3 ounce", - "Everything But The Bagel Sesame Seasoning Blend by Trader Joe's 2 tsp" - ], - "steps": [ - "Slice away the leaves and hard white base of 2 celery stalks. Cut each stalk in half to yield 4 pieces.", - "Microwave your cream cheese for 7-10 seconds to get warm. Mix the cream cheese with the everything seasoning.", - "Spoon the cream cheese mixture into each celery stick. Enjoy!" - ], - "nutrients": { - "caloriesKCal": 179.041, - "totalCarbs": 4.242, - "sugar": 2.453, - "protein": 3.056, - "fat": 14.755, - "cholesterol": 42.95, - "alcohol": 0, - "gluten": 0 - }, - "image": "https://tinyurl.com/2p82zzca/67b63b11-b5a2-44a8-92ec-33f3180a289e.png" - }, - { - "id": 20, - "name": "Low Carb Autumn Vegetable Medley", - "description": "There is so much delicious and colorful produce that is in peak season, come Autumn! Many of it can be enjoyed while on the keto diet. This recipe avoids any squash, but the other seasonal vegetables are bright, juicy, and flavorful. A pinch of nutmeg really sets the tone for a chilly atmosphere.\n\nJessica L.", - "prepareTime": 10, - "cookTime": 10, - "ingredients": [ - "Baby Carrots 2 ounce", - "Brussel Sprouts by Great Value 3 ounce", - "Cauliflower, Raw 4 ounce", - "Kale 2-¼ ounce", - "Butter 2 tablespoon", - "Salt 0.5 teaspoon", - "Black Pepper 0.5 teaspoon", - "Italian Seasoning 1 teaspoon", - "Garlic Powder 0.25 teaspoon", - "Onion Powder 0.25 teaspoon", - "Spices Nutmeg Ground ¼ tsp" - ], - "steps": [ - "Prepare all the vegetables first. Give the carrots a small chop. Quarter the brussel sprouts, removing any hard bottoms. Chop the cauliflower. You can chop the kale or simply rip it up with your hands, removing the stem.", - "Melt the butter in a large skillet over medium-low heat. Toss in the carrots, sprouts, and cauliflower. Leave the kale aside. Coat the veggies in the butter, then cover with a lid and cook on low for about 5 minutes. The firmer vegetables should be tender and the brussel sprouts should be bright green.", - "Sprinkle all the seasonings over the skillet. Then, place the kale on top. Replace the lid, then let the kale cook down on its own for 2-3 minutes on a low heat. Remove the lid, and continually stir and cook the ingredients until the kale has cooked down significantly and the vegetables have browned edges.", - "Serve hot!" - ], - "nutrients": { - "caloriesKCal": 83.614, - "totalCarbs": 6.582, - "sugar": 2.141, - "protein": 2.288, - "fat": 6.096, - "cholesterol": 15.252, - "alcohol": 0, - "gluten": 0 - }, - "image": "https://tinyurl.com/2p82zzca/1a7a2a70-b217-42d5-9a57-a103c089543b.png" + "image": "https://tinyurl.com/2p82zzca/dce25a9d-8be4-4c4b-afaa-f994ef227f06.png" } ] \ No newline at end of file diff --git a/server/middlewares/errorHandler.js b/server/middlewares/errorHandler.js index 60238f5..1b9202a 100644 --- a/server/middlewares/errorHandler.js +++ b/server/middlewares/errorHandler.js @@ -11,6 +11,10 @@ module.exports = function errorHandler(error, req, res, next) { res.status(401).json({ message: error.message ?? "Unauthenticated" }); break; + case "NotFound": + res.status(404).json({ message: error.message ?? "Data not found" }); //cusine -> error + break; + default: res.status(500).json({ message: "Internal Server Error" }); break; diff --git a/server/migrations/20231114142906-create-recipe.js b/server/migrations/20231114142906-create-recipe.js new file mode 100644 index 0000000..ee7a6dd --- /dev/null +++ b/server/migrations/20231114142906-create-recipe.js @@ -0,0 +1,49 @@ +'use strict'; +/** @type {import('sequelize-cli').Migration} */ +module.exports = { + async up(queryInterface, Sequelize) { + await queryInterface.createTable('Recipes', { + id: { + allowNull: false, + autoIncrement: true, + primaryKey: true, + type: Sequelize.INTEGER + }, + name: { + type: Sequelize.STRING + }, + description: { + type: Sequelize.STRING(10000) + }, + prepareTime: { + type: Sequelize.INTEGER + }, + cookTime: { + type: Sequelize.INTEGER + }, + ingredients: { + type: Sequelize.STRING(10000) + }, + steps: { + type: Sequelize.STRING(10000) + }, + nutrients: { + type: Sequelize.STRING + }, + image: { + type: Sequelize.STRING(10000) + }, + createdAt: { + allowNull: false, + type: Sequelize.DATE + }, + updatedAt: { + allowNull: false, + type: Sequelize.DATE + } + }); + }, + async down(queryInterface, Sequelize) { + await queryInterface.dropTable('Recipes'); + } +}; \ No newline at end of file diff --git a/server/models/recipe.js b/server/models/recipe.js new file mode 100644 index 0000000..26d55a8 --- /dev/null +++ b/server/models/recipe.js @@ -0,0 +1,28 @@ +'use strict'; +const { Model } = require('sequelize'); +module.exports = (sequelize, DataTypes) => { + class Recipe extends Model { + /** + * Helper method for defining associations. + * This method is not a part of Sequelize lifecycle. + * The `models/index` file will call this method automatically. + */ + static associate(models) { + // define association here + } + } + Recipe.init({ + name: DataTypes.STRING, + description: DataTypes.STRING(10000), + prepareTime: DataTypes.INTEGER, + cookTime: DataTypes.INTEGER, + ingredients: DataTypes.STRING(10000), + steps: DataTypes.STRING(10000), + nutrients: DataTypes.STRING, + image: DataTypes.STRING(10000) + }, { + sequelize, + modelName: 'Recipe', + }); + return Recipe; +}; \ No newline at end of file diff --git a/server/routes/index.js b/server/routes/index.js index 027e92d..da6e65d 100644 --- a/server/routes/index.js +++ b/server/routes/index.js @@ -14,6 +14,14 @@ router.use(authentication) router.get("/recipes", RecipeController.recipes); +router.get("/recipes/:id", RecipeController.recipeById); + + +// =============== DEVELOPMENT ==================== +router.get("/recDB", RecipeController.getRecDb); +router.get("/recDB/:id", RecipeController.getRecDbId); +// ================================================ + router.use(errorHandler); diff --git a/server/seeders/20231114143111-seed-recipes.js b/server/seeders/20231114143111-seed-recipes.js new file mode 100644 index 0000000..7e0d97f --- /dev/null +++ b/server/seeders/20231114143111-seed-recipes.js @@ -0,0 +1,22 @@ +'use strict'; + +/** @type {import('sequelize-cli').Migration} */ +module.exports = { + async up (queryInterface, Sequelize) { + const recipes = require("../data/recipe.json"); + recipes.forEach((el) => { + delete el.id; + el.createdAt = el.updatedAt = new Date(); + }) + + await queryInterface.bulkInsert("Recipes", recipes); + }, + + async down (queryInterface, Sequelize) { + await queryInterface.bulkDelete("Recipes", null, { + truncate: true, + cascade: true, + restartIdentity: true + }); + } +}; From 623b30e0ec5aa26e1e756a9e2d17cdf7b90d68c7 Mon Sep 17 00:00:00 2001 From: pramesthias Date: Wed, 15 Nov 2023 05:56:19 +0700 Subject: [PATCH 07/28] feat: add Read by Id for data from 3rd Party API --- server/controllers/RecipeController.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/server/controllers/RecipeController.js b/server/controllers/RecipeController.js index 474b543..ff18d71 100644 --- a/server/controllers/RecipeController.js +++ b/server/controllers/RecipeController.js @@ -102,8 +102,9 @@ module.exports = class RecipeController { } }) - const recipe = await mappedRecipes.findByPk(req.params.id) - console.log(recipe) + const recipe = mappedRecipes.find((recipe) => recipe.id === req.params.id) + console.log(mappedRecipes) + console.log(recipe) // ======= undefined res.status(200).json(recipe); } catch (error) { // next(error); From bce3006a41c1d26ba470d8bf1779be3394bc2783 Mon Sep 17 00:00:00 2001 From: pramesthias Date: Wed, 15 Nov 2023 05:57:46 +0700 Subject: [PATCH 08/28] docs: add API docs for login, register, read all feature --- README.md | 201 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 201 insertions(+) diff --git a/README.md b/README.md index 13c0ac4..1fe7117 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,204 @@ [![Review Assignment Due Date](https://classroom.github.com/assets/deadline-readme-button-24ddc0f5d75046c5622901739e7c5dd533143b0c8e959d652212380cedb1ea36.svg)](https://classroom.github.com/a/IrWObaQs) [![Open in Visual Studio Code](https://classroom.github.com/assets/open-in-vscode-718a45dd9cf7e7f842a935f5ebbe5719a5e09af4491e668f4dbf3b35d5cca122.svg)](https://classroom.github.com/online_ide?assignment_repo_id=12856485&assignment_repo_type=AssignmentRepo) # Individual Project Phase 2 + +CYTO is my Individual Project application developed for assignment purposes. +It is an application ___ +This app has : +* RESTful endpoint for asset's CRUD operation +* JSON formatted response + +  + +# RESTful endpoints + + +## Users + +### POST /register + +> Register new user + +_Request Header_ +``` +not needed +``` + +_URL Params_ +``` +not needed +``` + +_Data Params_ +``` +not needed +``` + +_Request Body_ +``` +{ + "username": "string", + "email": "string", + "password": "string", +} +``` + +_Response (201 - Created)_ +``` +{ + "id": integer, + "username": "string" +} +``` + +_Response (400 - Bad Request)_ +``` +{ + "message": "Email cannot be Empty!" + OR + "message": "Email should be written in email format!" + OR + "message": "Password cannot be Empty!" + OR + "message": "Minimum password length is 8" +} +``` +--- +### POST /login + +> User login + +_Request Header_ +``` +not needed +``` + +_URL Params_ +``` +not needed +``` + +_Data Params_ +``` +not needed +``` + +_Request Body_ +``` +{ + "email": "string", + "password": "string" +} +``` + +_Response (200 - OK)_ +``` +{ + "access_token": "string" +} +``` + +_Response (400 - Bad Request)_ +``` +{ + "message": "Error invalid email or password" +} +``` + +_Response (401 - Unauthorized)_ +``` +{ + "message": "User not found or Password not matched" +} +``` +--- + +## Recipes +### GET /recipes + +> Get all Recipes + +_Request Header_ +``` +{"Authorization": "Bearer "} +``` + +_URL Params_ +``` +not needed +``` + +_Data Params_ +``` +not needed +``` + +_Request Body_ +``` +not needed +``` + +_Response (200 - OK)_ +``` +[ + { + "id": integer, + "name": "string", + "description": "string", + "prepareTime": integer, + "cookTime": integer, + "ingredients": [ + "string", + "string", + "string", + "string", + "string", + "string", + "string", + "string", + "string", + "string" + ], + "steps": [ + "string", + "string", + "string", + "string", + "string", + "string", + "string", + ], + "nutrients": { + "string": integer, + "string": integer, + "string": integer, + "string": integer, + "string": integer, + "string": integer, + "string": integer, + "string": integer + }, + "image": "string" + }, + ... +] +``` + + + +--- +### Global Error + +_Response (401 - Unauthorized)_ +``` +{ + "message": "Unauthenticated" +} +``` + +_Response (500 - Internal Server Error)_ +``` +{ + "message": "Internal Server Error" +} +``` \ No newline at end of file From 2fca1658f27aebb6a91944df43d18c6016476fac Mon Sep 17 00:00:00 2001 From: pramesthias Date: Wed, 15 Nov 2023 15:56:14 +0700 Subject: [PATCH 09/28] feat: login via google (socmed login) --- client/.eslintrc.cjs | 20 + client/.gitignore | 24 + client/README.md | 8 + client/index.html | 27 + client/package-lock.json | 4204 ++++++++++++++++++++++++++ client/package.json | 31 + client/public/vite.svg | 1 + client/src/App.css | 42 + client/src/App.jsx | 18 + client/src/assets/react.svg | 1 + client/src/index.css | 69 + client/src/main.jsx | 14 + client/src/router/index.jsx | 26 + client/src/views/Home.jsx | 23 + client/src/views/Landing.jsx | 0 client/src/views/Login.jsx | 114 + client/src/views/Register.jsx | 0 client/vite.config.js | 7 + server/.env.example | 4 +- server/app.js | 4 +- server/controllers/UserController.js | 76 +- server/models/user.js | 4 - server/package-lock.json | 258 +- server/package.json | 2 + server/routes/index.js | 2 + 25 files changed, 4948 insertions(+), 31 deletions(-) create mode 100644 client/.eslintrc.cjs create mode 100644 client/.gitignore create mode 100644 client/README.md create mode 100644 client/index.html create mode 100644 client/package-lock.json create mode 100644 client/package.json create mode 100644 client/public/vite.svg create mode 100644 client/src/App.css create mode 100644 client/src/App.jsx create mode 100644 client/src/assets/react.svg create mode 100644 client/src/index.css create mode 100644 client/src/main.jsx create mode 100644 client/src/router/index.jsx create mode 100644 client/src/views/Home.jsx create mode 100644 client/src/views/Landing.jsx create mode 100644 client/src/views/Login.jsx create mode 100644 client/src/views/Register.jsx create mode 100644 client/vite.config.js diff --git a/client/.eslintrc.cjs b/client/.eslintrc.cjs new file mode 100644 index 0000000..4dcb439 --- /dev/null +++ b/client/.eslintrc.cjs @@ -0,0 +1,20 @@ +module.exports = { + root: true, + env: { browser: true, es2020: true }, + extends: [ + 'eslint:recommended', + 'plugin:react/recommended', + 'plugin:react/jsx-runtime', + 'plugin:react-hooks/recommended', + ], + ignorePatterns: ['dist', '.eslintrc.cjs'], + parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, + settings: { react: { version: '18.2' } }, + plugins: ['react-refresh'], + rules: { + 'react-refresh/only-export-components': [ + 'warn', + { allowConstantExport: true }, + ], + }, +} diff --git a/client/.gitignore b/client/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/client/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/client/README.md b/client/README.md new file mode 100644 index 0000000..f768e33 --- /dev/null +++ b/client/README.md @@ -0,0 +1,8 @@ +# React + Vite + +This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. + +Currently, two official plugins are available: + +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh diff --git a/client/index.html b/client/index.html new file mode 100644 index 0000000..5781c9b --- /dev/null +++ b/client/index.html @@ -0,0 +1,27 @@ + + + + + + + + + + + CYTO + + + + + + + + +
+ + + + \ No newline at end of file diff --git a/client/package-lock.json b/client/package-lock.json new file mode 100644 index 0000000..dd42455 --- /dev/null +++ b/client/package-lock.json @@ -0,0 +1,4204 @@ +{ + "name": "client", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "client", + "version": "0.0.0", + "dependencies": { + "@react-oauth/google": "^0.11.1", + "axios": "^1.6.2", + "google-auth-library": "^9.2.0", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-router-dom": "^6.18.0", + "react-toastify": "^9.1.3" + }, + "devDependencies": { + "@types/react": "^18.2.15", + "@types/react-dom": "^18.2.7", + "@vitejs/plugin-react": "^4.0.3", + "eslint": "^8.45.0", + "eslint-plugin-react": "^7.32.2", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-refresh": "^0.4.3", + "vite": "^4.4.5" + } + }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", + "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.22.13", + "chalk": "^2.4.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.3.tgz", + "integrity": "sha512-BmR4bWbDIoFJmJ9z2cZ8Gmm2MXgEDgjdWgpKmKWUt54UGFJdlj31ECtbaDvCG/qVdG3AQ1SfpZEs01lUFbzLOQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.3.tgz", + "integrity": "sha512-Jg+msLuNuCJDyBvFv5+OKOUjWMZgd85bKjbICd3zWrKAo+bJ49HJufi7CQE0q0uR8NGyO6xkCACScNqyjHSZew==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.3", + "@babel/helper-compilation-targets": "^7.22.15", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helpers": "^7.23.2", + "@babel/parser": "^7.23.3", + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.3", + "@babel/types": "^7.23.3", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.3.tgz", + "integrity": "sha512-keeZWAV4LU3tW0qRi19HRpabC/ilM0HRBBzf9/k8FFiG4KVpiv0FIy4hHfLfFQZNhziCTPTmd59zoyv6DNISzg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.23.3", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz", + "integrity": "sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.22.9", + "@babel/helper-validator-option": "^7.22.15", + "browserslist": "^4.21.9", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", + "dev": true, + "dependencies": { + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", + "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", + "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-simple-access": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/helper-validator-identifier": "^7.22.20" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", + "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", + "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz", + "integrity": "sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.2.tgz", + "integrity": "sha512-lzchcp8SjTSVe/fPmLwtWVBFC7+Tbn8LGHDVfDp9JGxpAY5opSaEFgt8UQvrnECWOTdji2mOWMz1rOhkHscmGQ==", + "dev": true, + "dependencies": { + "@babel/template": "^7.22.15", + "@babel/traverse": "^7.23.2", + "@babel/types": "^7.23.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", + "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.3.tgz", + "integrity": "sha512-uVsWNvlVsIninV2prNz/3lHCb+5CJ+e+IUBfbjToAHODtfGYLfCFuY4AU7TskI+dAKk+njsPiBjq1gKTvZOBaw==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.23.3.tgz", + "integrity": "sha512-qXRvbeKDSfwnlJnanVRp0SfuWE5DQhwQr5xtLBzp56Wabyo+4CMosF6Kfp+eOD/4FYpql64XVJ2W0pVLlJZxOQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.23.3.tgz", + "integrity": "sha512-91RS0MDnAWDNvGC6Wio5XYkyWI39FMFO+JK9+4AlgaTH+yWwVTsw7/sn6LK0lH7c5F+TFkpv/3LfCJ1Ydwof/g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.3.tgz", + "integrity": "sha512-+K0yF1/9yR0oHdE0StHuEj3uTPzwwbrLGfNOndVJVV2TqA5+j3oljJUb4nmB954FLGjNem976+B+eDuLIjesiQ==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.3", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.3", + "@babel/types": "^7.23.3", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.3.tgz", + "integrity": "sha512-OZnvoH2l8PK5eUvEcUyCt/sXgr/h+UWpVuBbOljwcrAgUl6lpchoQ++PHGyQy1AtYnVA6CEq3y5xeEI10brpXw==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", + "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", + "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", + "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", + "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", + "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", + "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", + "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", + "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", + "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", + "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", + "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", + "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", + "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", + "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", + "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", + "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", + "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", + "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", + "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", + "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", + "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", + "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.3.tgz", + "integrity": "sha512-yZzuIG+jnVu6hNSzFEN07e8BxF3uAzYtQb6uDkaYZLo6oYZDCq454c5kB8zxnzfCYyP4MIuyBn10L0DqwujTmA==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "13.23.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz", + "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "8.53.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.53.0.tgz", + "integrity": "sha512-Kn7K8dx/5U6+cT1yEhpX1w4PCSg0M+XyRILPgvwcEBjerFWCwQj5sbr3/VmxqV0JGHCBCzyd6LxypEuehypY1w==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.13", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", + "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", + "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", + "dev": true + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.20", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", + "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@react-oauth/google": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/@react-oauth/google/-/google-0.11.1.tgz", + "integrity": "sha512-tywZisXbsdaRBVbEu0VX6dRbOSL2I6DgY97woq5NMOOOz+xtDsm418vqq+Vx10KMtra3kdHMRMf0hXLWrk2RMg==", + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@remix-run/router": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.11.0.tgz", + "integrity": "sha512-BHdhcWgeiudl91HvVa2wxqZjSHbheSgIiDvxrF1VjFzBzpTtuDPkOdOi3Iqvc08kXtFkLjhbS+ML9aM8mJS+wQ==", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.4", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.4.tgz", + "integrity": "sha512-mLnSC22IC4vcWiuObSRjrLd9XcBTGf59vUSoq2jkQDJ/QQ8PMI9rSuzE+aEV8karUMbskw07bKYoUJCKTUaygg==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.7", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.7.tgz", + "integrity": "sha512-6Sfsq+EaaLrw4RmdFWE9Onp63TOUue71AWb4Gpa6JxzgTYtimbM086WnYTy2U67AofR++QKCo08ZP6pwx8YFHQ==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.4", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.4.tgz", + "integrity": "sha512-mSM/iKUk5fDDrEV/e83qY+Cr3I1+Q3qqTuEn++HAWYjEa1+NxZr6CNrcJGf2ZTnq4HoFGC3zaTPZTobCzCFukA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/prop-types": { + "version": "15.7.10", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.10.tgz", + "integrity": "sha512-mxSnDQxPqsZxmeShFH+uwQ4kO4gcJcGahjjMFeLbKE95IAZiiZyiEepGZjtXJ7hN/yfu0bu9xN2ajcU0JcxX6A==", + "dev": true + }, + "node_modules/@types/react": { + "version": "18.2.37", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.37.tgz", + "integrity": "sha512-RGAYMi2bhRgEXT3f4B92WTohopH6bIXw05FuGlmJEnv/omEn190+QYEIYxIAuIBdKgboYYdVved2p1AxZVQnaw==", + "dev": true, + "dependencies": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.2.15", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.15.tgz", + "integrity": "sha512-HWMdW+7r7MR5+PZqJF6YFNSCtjz1T0dsvo/f1BV6HkV+6erD/nA7wd9NM00KVG83zf2nJ7uATPO9ttdIPvi3gg==", + "dev": true, + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/scheduler": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.6.tgz", + "integrity": "sha512-Vlktnchmkylvc9SnwwwozTv04L/e1NykF5vgoQ0XTmI8DD+wxfjQuHuvHS3p0r2jz2x2ghPs2h1FVeDirIteWA==", + "dev": true + }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.1.1.tgz", + "integrity": "sha512-Jie2HERK+uh27e+ORXXwEP5h0Y2lS9T2PRGbfebiHGlwzDO0dEnd2aNtOR/qjBlPb1YgxwAONeblL1xqLikLag==", + "dev": true, + "dependencies": { + "@babel/core": "^7.23.2", + "@babel/plugin-transform-react-jsx-self": "^7.22.5", + "@babel/plugin-transform-react-jsx-source": "^7.22.5", + "@types/babel__core": "^7.20.3", + "react-refresh": "^0.14.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0" + } + }, + "node_modules/acorn": { + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", + "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", + "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", + "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "is-array-buffer": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-includes": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.7.tgz", + "integrity": "sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", + "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", + "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.tosorted": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.2.tgz", + "integrity": "sha512-HuQCHOlk1Weat5jzStICBCd83NxiIMwqDg/dHEsoefabn/hJRj5pVdWcPUSpRrwhwxZOsQassMpgN/xRYFBMIg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0", + "get-intrinsic": "^1.2.1" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz", + "integrity": "sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", + "is-array-buffer": "^3.0.2", + "is-shared-array-buffer": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/asynciterator.prototype": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/asynciterator.prototype/-/asynciterator.prototype-1.0.0.tgz", + "integrity": "sha512-wwHYEIS0Q80f5mosx3L/dfG5t5rjEa9Ft51GTaNt862EnpyGHpgz2RkZvLPp1oF5TnAiTohkEKVEu8pQPJI7Vg==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.3" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/axios": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz", + "integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==", + "dependencies": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/bignumber.js": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", + "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==", + "engines": { + "node": "*" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/browserslist": { + "version": "4.22.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.1.tgz", + "integrity": "sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001541", + "electron-to-chromium": "^1.4.535", + "node-releases": "^2.0.13", + "update-browserslist-db": "^1.0.13" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, + "node_modules/call-bind": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", + "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.1", + "set-function-length": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001562", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001562.tgz", + "integrity": "sha512-kfte3Hym//51EdX4239i+Rmp20EsLIYGdPkERegTgU19hQWCRhsRFGKHTliUlsry53tv17K7n077Kqa0WJU4ng==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/clsx": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", + "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/csstype": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", + "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==", + "dev": true + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/define-data-property": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", + "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.4.583", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.583.tgz", + "integrity": "sha512-93y1gcONABZ7uqYe/JWDVQP/Pj/sQSunF0HVAPdlg/pfBnOyBMLlQUxWvkqcljJg1+W6cjvPuYD+r1Th9Tn8mA==", + "dev": true + }, + "node_modules/es-abstract": { + "version": "1.22.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz", + "integrity": "sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "arraybuffer.prototype.slice": "^1.0.2", + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.5", + "es-set-tostringtag": "^2.0.1", + "es-to-primitive": "^1.2.1", + "function.prototype.name": "^1.1.6", + "get-intrinsic": "^1.2.2", + "get-symbol-description": "^1.0.0", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0", + "internal-slot": "^1.0.5", + "is-array-buffer": "^3.0.2", + "is-callable": "^1.2.7", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-typed-array": "^1.1.12", + "is-weakref": "^1.0.2", + "object-inspect": "^1.13.1", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.5.1", + "safe-array-concat": "^1.0.1", + "safe-regex-test": "^1.0.0", + "string.prototype.trim": "^1.2.8", + "string.prototype.trimend": "^1.0.7", + "string.prototype.trimstart": "^1.0.7", + "typed-array-buffer": "^1.0.0", + "typed-array-byte-length": "^1.0.0", + "typed-array-byte-offset": "^1.0.0", + "typed-array-length": "^1.0.4", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-iterator-helpers": { + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.15.tgz", + "integrity": "sha512-GhoY8uYqd6iwUl2kgjTm4CZAf6oo5mHK7BPqx3rKgx893YSsy0LGHV6gfqqQvZt/8xM8xeOnfXBCfqclMKkJ5g==", + "dev": true, + "dependencies": { + "asynciterator.prototype": "^1.0.0", + "call-bind": "^1.0.2", + "define-properties": "^1.2.1", + "es-abstract": "^1.22.1", + "es-set-tostringtag": "^2.0.1", + "function-bind": "^1.1.1", + "get-intrinsic": "^1.2.1", + "globalthis": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.5", + "iterator.prototype": "^1.1.2", + "safe-array-concat": "^1.0.1" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz", + "integrity": "sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.2", + "has-tostringtag": "^1.0.0", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", + "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" + } + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/esbuild": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", + "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.18.20", + "@esbuild/android-arm64": "0.18.20", + "@esbuild/android-x64": "0.18.20", + "@esbuild/darwin-arm64": "0.18.20", + "@esbuild/darwin-x64": "0.18.20", + "@esbuild/freebsd-arm64": "0.18.20", + "@esbuild/freebsd-x64": "0.18.20", + "@esbuild/linux-arm": "0.18.20", + "@esbuild/linux-arm64": "0.18.20", + "@esbuild/linux-ia32": "0.18.20", + "@esbuild/linux-loong64": "0.18.20", + "@esbuild/linux-mips64el": "0.18.20", + "@esbuild/linux-ppc64": "0.18.20", + "@esbuild/linux-riscv64": "0.18.20", + "@esbuild/linux-s390x": "0.18.20", + "@esbuild/linux-x64": "0.18.20", + "@esbuild/netbsd-x64": "0.18.20", + "@esbuild/openbsd-x64": "0.18.20", + "@esbuild/sunos-x64": "0.18.20", + "@esbuild/win32-arm64": "0.18.20", + "@esbuild/win32-ia32": "0.18.20", + "@esbuild/win32-x64": "0.18.20" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/eslint": { + "version": "8.53.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.53.0.tgz", + "integrity": "sha512-N4VuiPjXDUa4xVeV/GC/RV3hQW9Nw+Y463lkWaKKXKYMvmRiRDAtfpuPFLN+E1/6ZhyR8J2ig+eVREnYgUsiag==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.3", + "@eslint/js": "8.53.0", + "@humanwhocodes/config-array": "^0.11.13", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-plugin-react": { + "version": "7.33.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.33.2.tgz", + "integrity": "sha512-73QQMKALArI8/7xGLNI/3LylrEYrlKZSb5C9+q3OtOewTnMQi5cT+aE9E41sLCmli3I9PGGmD1yiZydyo4FEPw==", + "dev": true, + "dependencies": { + "array-includes": "^3.1.6", + "array.prototype.flatmap": "^1.3.1", + "array.prototype.tosorted": "^1.1.1", + "doctrine": "^2.1.0", + "es-iterator-helpers": "^1.0.12", + "estraverse": "^5.3.0", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.1.2", + "object.entries": "^1.1.6", + "object.fromentries": "^2.0.6", + "object.hasown": "^1.1.2", + "object.values": "^1.1.6", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.4", + "semver": "^6.3.1", + "string.prototype.matchall": "^4.0.8" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz", + "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==", + "dev": true, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" + } + }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.4.tgz", + "integrity": "sha512-eD83+65e8YPVg6603Om2iCIwcQJf/y7++MWm4tACtEswFLYMwxwVWAfwN+e19f5Ad/FOyyNg9Dfi5lXhH3Y3rA==", + "dev": true, + "peerDependencies": { + "eslint": ">=7" + } + }, + "node_modules/eslint-plugin-react/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/eslint/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/eslint/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/globals": { + "version": "13.23.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz", + "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", + "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", + "dev": true + }, + "node_modules/follow-redirects": { + "version": "1.15.3", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz", + "integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.3" + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", + "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "functions-have-names": "^1.2.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gaxios": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.1.1.tgz", + "integrity": "sha512-bw8smrX+XlAoo9o1JAksBwX+hi/RG15J+NTSxmNPIclKC3ZVK6C2afwY8OSdRvOK0+ZLecUJYtj2MmjOt3Dm0w==", + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^7.0.1", + "is-stream": "^2.0.0", + "node-fetch": "^2.6.9" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/gcp-metadata": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.0.tgz", + "integrity": "sha512-Jh/AIwwgaxan+7ZUUmRLCjtchyDiqh4KjBJ5tW3plBZb5iL/BPcso8A5DlzeD9qlw0duCamnNdpFjxwaT0KyKg==", + "dependencies": { + "gaxios": "^6.0.0", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", + "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/globalthis": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", + "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "dev": true, + "dependencies": { + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/google-auth-library": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.2.0.tgz", + "integrity": "sha512-1oV3p0JhNEhVbj26eF3FAJcv9MXXQt4S0wcvKZaDbl4oHq5V3UJoSbsGZGQNcjoCdhW4kDSwOs11wLlHog3fgQ==", + "dependencies": { + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "gaxios": "^6.0.0", + "gcp-metadata": "^6.0.0", + "gtoken": "^7.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/gtoken": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.0.1.tgz", + "integrity": "sha512-KcFVtoP1CVFtQu0aSk3AyAt2og66PFhZAlkUOuWKwzMLoulHXG5W5wE5xAnHb+yl3/wEFoqGW7/cDGMU8igDZQ==", + "dependencies": { + "gaxios": "^6.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", + "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", + "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz", + "integrity": "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/internal-slot": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.6.tgz", + "integrity": "sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.2", + "hasown": "^2.0.0", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", + "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.0", + "is-typed-array": "^1.1.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-async-function": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz", + "integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-finalizationregistry": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.0.2.tgz", + "integrity": "sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-map": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", + "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", + "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", + "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", + "dev": true, + "dependencies": { + "which-typed-array": "^1.1.11" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakmap": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", + "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz", + "integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/iterator.prototype": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.2.tgz", + "integrity": "sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==", + "dev": true, + "dependencies": { + "define-properties": "^1.2.1", + "get-intrinsic": "^1.2.1", + "has-symbols": "^1.0.3", + "reflect.getprototypeof": "^1.0.4", + "set-function-name": "^2.0.1" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "dependencies": { + "bignumber.js": "^9.0.0" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsx-ast-utils": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", + "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", + "dev": true, + "dependencies": { + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "object.assign": "^4.1.4", + "object.values": "^1.1.6" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/jwa": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", + "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", + "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "dependencies": { + "jwa": "^2.0.0", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-releases": { + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", + "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", + "dev": true + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.entries": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.7.tgz", + "integrity": "sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.7.tgz", + "integrity": "sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.hasown": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.3.tgz", + "integrity": "sha512-fFI4VcYpRHvSLXxP7yiZOMAd331cPfd2p7PFDVbgUsYOfCT3tICVqXWngbjr4m49OvsBwUBQ6O2uQoJvy3RexA==", + "dev": true, + "dependencies": { + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.values": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.7.tgz", + "integrity": "sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dev": true, + "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dev": true, + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/react": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", + "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", + "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.0" + }, + "peerDependencies": { + "react": "^18.2.0" + } + }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true + }, + "node_modules/react-refresh": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", + "integrity": "sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-router": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.18.0.tgz", + "integrity": "sha512-vk2y7Dsy8wI02eRRaRmOs9g2o+aE72YCx5q9VasT1N9v+lrdB79tIqrjMfByHiY5+6aYkH2rUa5X839nwWGPDg==", + "dependencies": { + "@remix-run/router": "1.11.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/react-router-dom": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.18.0.tgz", + "integrity": "sha512-Ubrue4+Ercc/BoDkFQfc6og5zRQ4A8YxSO3Knsne+eRbZ+IepAsK249XBH/XaFuOYOYr3L3r13CXTLvYt5JDjw==", + "dependencies": { + "@remix-run/router": "1.11.0", + "react-router": "6.18.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, + "node_modules/react-toastify": { + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-9.1.3.tgz", + "integrity": "sha512-fPfb8ghtn/XMxw3LkxQBk3IyagNpF/LIKjOBflbexr2AWxAH1MJgvnESwEwBn9liLFXgTKWgBSdZpw9m4OTHTg==", + "dependencies": { + "clsx": "^1.1.1" + }, + "peerDependencies": { + "react": ">=16", + "react-dom": ">=16" + } + }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.4.tgz", + "integrity": "sha512-ECkTw8TmJwW60lOTR+ZkODISW6RQ8+2CL3COqtiJKLd6MmB45hN51HprHFziKLGkAuTGQhBb91V8cy+KHlaCjw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", + "globalthis": "^1.0.3", + "which-builtin-type": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz", + "integrity": "sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "set-function-name": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve": { + "version": "2.0.0-next.5", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", + "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", + "dev": true, + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rollup": { + "version": "3.29.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz", + "integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==", + "dev": true, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=14.18.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-array-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.1.tgz", + "integrity": "sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1", + "has-symbols": "^1.0.3", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safe-regex-test": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", + "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-regex": "^1.1.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/scheduler": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", + "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/set-function-length": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", + "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.1", + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz", + "integrity": "sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==", + "dev": true, + "dependencies": { + "define-data-property": "^1.0.1", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string.prototype.matchall": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.10.tgz", + "integrity": "sha512-rGXbGmOEosIQi6Qva94HUjgPs9vKW+dkG7Y8Q5O2OYkWL6wFaTRZO8zM4mhP94uX55wgyrXzfS2aGtGzUL7EJQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.5", + "regexp.prototype.flags": "^1.5.0", + "set-function-name": "^2.0.0", + "side-channel": "^1.0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", + "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz", + "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz", + "integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz", + "integrity": "sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz", + "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz", + "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", + "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "is-typed-array": "^1.1.9" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/vite": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.0.tgz", + "integrity": "sha512-ulr8rNLA6rkyFAlVWw2q5YJ91v098AFQ2R0PRFwPzREXOUJQPtFUG0t+/ZikhaOCDqFoDhN6/v8Sq0o4araFAw==", + "dev": true, + "dependencies": { + "esbuild": "^0.18.10", + "postcss": "^8.4.27", + "rollup": "^3.27.1" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + }, + "peerDependencies": { + "@types/node": ">= 14", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.1.3.tgz", + "integrity": "sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==", + "dev": true, + "dependencies": { + "function.prototype.name": "^1.1.5", + "has-tostringtag": "^1.0.0", + "is-async-function": "^2.0.0", + "is-date-object": "^1.0.5", + "is-finalizationregistry": "^1.0.2", + "is-generator-function": "^1.0.10", + "is-regex": "^1.1.4", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.0.2", + "which-collection": "^1.0.1", + "which-typed-array": "^1.1.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz", + "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==", + "dev": true, + "dependencies": { + "is-map": "^2.0.1", + "is-set": "^2.0.1", + "is-weakmap": "^2.0.1", + "is-weakset": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.13.tgz", + "integrity": "sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.4", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/client/package.json b/client/package.json new file mode 100644 index 0000000..ba22669 --- /dev/null +++ b/client/package.json @@ -0,0 +1,31 @@ +{ + "name": "client", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", + "preview": "vite preview" + }, + "dependencies": { + "@react-oauth/google": "^0.11.1", + "axios": "^1.6.2", + "google-auth-library": "^9.2.0", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-router-dom": "^6.18.0", + "react-toastify": "^9.1.3" + }, + "devDependencies": { + "@types/react": "^18.2.15", + "@types/react-dom": "^18.2.7", + "@vitejs/plugin-react": "^4.0.3", + "eslint": "^8.45.0", + "eslint-plugin-react": "^7.32.2", + "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-refresh": "^0.4.3", + "vite": "^4.4.5" + } +} diff --git a/client/public/vite.svg b/client/public/vite.svg new file mode 100644 index 0000000..e7b8dfb --- /dev/null +++ b/client/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/src/App.css b/client/src/App.css new file mode 100644 index 0000000..b9d355d --- /dev/null +++ b/client/src/App.css @@ -0,0 +1,42 @@ +#root { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; + transition: filter 300ms; +} +.logo:hover { + filter: drop-shadow(0 0 2em #646cffaa); +} +.logo.react:hover { + filter: drop-shadow(0 0 2em #61dafbaa); +} + +@keyframes logo-spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +@media (prefers-reduced-motion: no-preference) { + a:nth-of-type(2) .logo { + animation: logo-spin infinite 20s linear; + } +} + +.card { + padding: 2em; +} + +.read-the-docs { + color: #888; +} diff --git a/client/src/App.jsx b/client/src/App.jsx new file mode 100644 index 0000000..63f5b7a --- /dev/null +++ b/client/src/App.jsx @@ -0,0 +1,18 @@ +// import './App.css' + +import { RouterProvider } from "react-router-dom"; +import router from "./router"; +import { GoogleOAuthProvider } from '@react-oauth/google'; + + +function App() { + + return ( + + + + ) + +} + +export default App diff --git a/client/src/assets/react.svg b/client/src/assets/react.svg new file mode 100644 index 0000000..6c87de9 --- /dev/null +++ b/client/src/assets/react.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/src/index.css b/client/src/index.css new file mode 100644 index 0000000..2c3fac6 --- /dev/null +++ b/client/src/index.css @@ -0,0 +1,69 @@ +:root { + font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; + line-height: 1.5; + font-weight: 400; + + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: #242424; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-text-size-adjust: 100%; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} +a:hover { + color: #535bf2; +} + +body { + margin: 0; + display: flex; + place-items: center; + min-width: 320px; + min-height: 100vh; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +button { + border-radius: 8px; + border: 1px solid transparent; + padding: 0.6em 1.2em; + font-size: 1em; + font-weight: 500; + font-family: inherit; + background-color: #1a1a1a; + cursor: pointer; + transition: border-color 0.25s; +} +button:hover { + border-color: #646cff; +} +button:focus, +button:focus-visible { + outline: 4px auto -webkit-focus-ring-color; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + a:hover { + color: #747bff; + } + button { + background-color: #f9f9f9; + } +} diff --git a/client/src/main.jsx b/client/src/main.jsx new file mode 100644 index 0000000..5a08c1f --- /dev/null +++ b/client/src/main.jsx @@ -0,0 +1,14 @@ +import React from 'react' +import ReactDOM from 'react-dom/client' +import App from './App.jsx' +import { ToastContainer } from 'react-toastify'; +import 'react-toastify/dist/ReactToastify.css'; + +import './index.css' + +ReactDOM.createRoot(document.getElementById('root')).render( + + + + , +) diff --git a/client/src/router/index.jsx b/client/src/router/index.jsx new file mode 100644 index 0000000..7d79403 --- /dev/null +++ b/client/src/router/index.jsx @@ -0,0 +1,26 @@ +import { createBrowserRouter, redirect } from "react-router-dom"; +import Login from "../views/Login"; +import Home from "../views/Home"; + +const router = createBrowserRouter([ + // { + // path: "/", + // element: , + // }, + { + path: "/", + element: , + }, + { + path: "/home", + element: , + }, + {/* { + + path: "/", + element:
, + loader: () => localStorage.access_token ? null : redirect ("/") + } */} +]); + +export default router \ No newline at end of file diff --git a/client/src/views/Home.jsx b/client/src/views/Home.jsx new file mode 100644 index 0000000..ba69d55 --- /dev/null +++ b/client/src/views/Home.jsx @@ -0,0 +1,23 @@ +import { useNavigate } from "react-router-dom"; + + +export default function Home() { + const navigate = useNavigate(); + + + const handleLogout = async () => { + localStorage.clear(); + // toast.info('🖐️ SEE YOU LATER 🖐️', { + // position: "top-center", + // autoClose: 5000, + // hideProgressBar: false, + // closeOnClick: true, + // pauseOnHover: true, + // draggable: true, + // progress: undefined, + // theme: "light", + // }); + navigate("/landing"); + } + +} \ No newline at end of file diff --git a/client/src/views/Landing.jsx b/client/src/views/Landing.jsx new file mode 100644 index 0000000..e69de29 diff --git a/client/src/views/Login.jsx b/client/src/views/Login.jsx new file mode 100644 index 0000000..3a40459 --- /dev/null +++ b/client/src/views/Login.jsx @@ -0,0 +1,114 @@ +import { useEffect } from "react"; +import axios from "axios"; +import { useNavigate } from "react-router-dom" +import { GoogleLogin } from '@react-oauth/google'; + + +export default function Login() { + const navigate = useNavigate(); + + async function handleCredentialResponse(response) { + console.log("Encoded JWT ID token: " + response.credential); + try { + let {data} = await axios.post("http://localhost:3000/google-login", null, { + headers: { + g_token: response.credential + } + }) + localStorage.setItem("access_token", data.access_token) + navigate("/home") + } catch (error) { + console.log(error) + } + } + + + return ( + +
+
+
+
+
+
+
+
+ +

Welcome back!

+ +
+ +
+ +
+ {/* */} + +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+
OR
+
+ +
+ + +
+ {/*
*/} + { + handleCredentialResponse(credentialResponse); + }} + onError={() => { + console.log('Login Failed'); + }} + />; +
+ +
+
+ + Sample image + +
+
+
+
+
+
+
+
+ + //
+ //
+ //
+ //
+ //
+ //
+ //
or sign-in with
+ //
+ //
+ //
+ // + // + //
+ //
+ //
+ + ) +} \ No newline at end of file diff --git a/client/src/views/Register.jsx b/client/src/views/Register.jsx new file mode 100644 index 0000000..e69de29 diff --git a/client/vite.config.js b/client/vite.config.js new file mode 100644 index 0000000..5a33944 --- /dev/null +++ b/client/vite.config.js @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [react()], +}) diff --git a/server/.env.example b/server/.env.example index 91672fb..9c2b61d 100644 --- a/server/.env.example +++ b/server/.env.example @@ -1,3 +1,5 @@ JWT_SECRET= PORT= -RECIPES_API_KEY= \ No newline at end of file +RECIPES_API_KEY= +G_CLIENT_ID= +G_SECRET= \ No newline at end of file diff --git a/server/app.js b/server/app.js index f9d60f9..9e6834f 100644 --- a/server/app.js +++ b/server/app.js @@ -4,9 +4,9 @@ if(process.env.NODE_ENV !== "production"){ const express = require('express'); const app = express(); -// const cors = require('cors'); //DISTRIBUTION + const cors = require('cors'); //DISTRIBUTION -// app.use(cors()) // DEPLOY +app.use(cors()) // DEPLOY app.use(express.urlencoded({ extended: true })); diff --git a/server/controllers/UserController.js b/server/controllers/UserController.js index 5b6b0b1..728dbc0 100644 --- a/server/controllers/UserController.js +++ b/server/controllers/UserController.js @@ -1,13 +1,16 @@ const { comparePassword } = require("../helpers/bcryptjs"); const { signToken } = require("../helpers/jwt"); const { User } = require("../models"); +const { OAuth2Client } = require('google-auth-library'); +const client = new OAuth2Client(); + module.exports = class UserController { - static async register(req, res, next){ - try { - const {username, email, password} = req.body - const user = await User.create({username, email, password}); + static async register(req, res, next) { + try { + const { username, email, password } = req.body + const user = await User.create({ username, email, password }); res.status(201).json({ "id": user.id, @@ -16,42 +19,69 @@ module.exports = class UserController { } catch (error) { next(error); - } + } } - - static async login(req, res, next){ + + static async login(req, res, next) { try { - const {email, password} = req.body; + const { email, password } = req.body; - if(!email) { - res.status(400).json({message: "Error invalid email or password"}); - return; + if (!email) { + res.status(400).json({ message: "Error invalid email or password" }); + return; } - - if(!password) { - res.status(400).json({message: "Error invalid email or password"}); - return; + + if (!password) { + res.status(400).json({ message: "Error invalid email or password" }); + return; } - const user = await User.findOne({ where: {email} }); - if(!user){ - next({name: 'Unauthenticated', message: "User not found or Password not matched"}); + const user = await User.findOne({ where: { email } }); + if (!user) { + next({ name: 'Unauthenticated', message: "User not found or Password not matched" }); return; } const isValidPassword = comparePassword(password, user.password); - if(!isValidPassword){ - next({name: 'Unauthenticated', message: "User not found or Password not matched"}); + if (!isValidPassword) { + next({ name: 'Unauthenticated', message: "User not found or Password not matched" }); return; } const access_token = signToken({ id: user.id }); - res.status(200).json({ access_token }); - + res.status(200).json({ access_token }); + + } catch (error) { + next(error); + } + } + + + static async googleLogin(req, res, next) { + console.log(req.headers.g_token) + try { + const ticket = await client.verifyIdToken({ + idToken: req.headers.g_token, + audience: process.env.G_CLIENT_ID, + }); + const payload = ticket.getPayload(); + + const [user, isNewRecord] = await User.findOrCreate({ + where: { + email: payload.email + }, + defaults: { + username: payload.name, + password: String(Math.random()) + } + }) + + const access_token = signToken({ id: user.id }); + res.status(isNewRecord ? 201 : 200).json({ access_token }); + } catch (error) { next(error); - console.log(error) } } diff --git a/server/models/user.js b/server/models/user.js index f8823a5..86a459c 100644 --- a/server/models/user.js +++ b/server/models/user.js @@ -47,10 +47,6 @@ module.exports = (sequelize, DataTypes) => { }, notEmpty: { msg: "Password cannot be Empty!" - }, - len: { - args: [8], - msg: "Minimum password length is 8" } } } diff --git a/server/package-lock.json b/server/package-lock.json index 24393ef..638cd41 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -11,7 +11,9 @@ "dependencies": { "axios": "^1.6.1", "bcryptjs": "^2.4.3", + "cors": "^2.8.5", "express": "^4.18.2", + "google-auth-library": "^9.2.0", "jsonwebtoken": "^9.0.2", "pg": "^8.11.3", "sequelize": "^6.35.0" @@ -1330,6 +1332,38 @@ "node": ">= 0.6" } }, + "node_modules/agent-base": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", + "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/agent-base/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/agent-base/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, "node_modules/ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -1548,11 +1582,38 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/bcryptjs": { "version": "2.4.3", "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==" }, + "node_modules/bignumber.js": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", + "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==", + "engines": { + "node": "*" + } + }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -2060,6 +2121,18 @@ "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", "dev": true }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/create-jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", @@ -2509,6 +2582,11 @@ "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==", "dev": true }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -2694,6 +2772,32 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/gaxios": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.1.1.tgz", + "integrity": "sha512-bw8smrX+XlAoo9o1JAksBwX+hi/RG15J+NTSxmNPIclKC3ZVK6C2afwY8OSdRvOK0+ZLecUJYtj2MmjOt3Dm0w==", + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^7.0.1", + "is-stream": "^2.0.0", + "node-fetch": "^2.6.9" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/gcp-metadata": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.0.tgz", + "integrity": "sha512-Jh/AIwwgaxan+7ZUUmRLCjtchyDiqh4KjBJ5tW3plBZb5iL/BPcso8A5DlzeD9qlw0duCamnNdpFjxwaT0KyKg==", + "dependencies": { + "gaxios": "^6.0.0", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=14" + } + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -2790,6 +2894,41 @@ "node": ">=4" } }, + "node_modules/google-auth-library": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.2.0.tgz", + "integrity": "sha512-1oV3p0JhNEhVbj26eF3FAJcv9MXXQt4S0wcvKZaDbl4oHq5V3UJoSbsGZGQNcjoCdhW4kDSwOs11wLlHog3fgQ==", + "dependencies": { + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "gaxios": "^6.0.0", + "gcp-metadata": "^6.0.0", + "gtoken": "^7.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/google-auth-library/node_modules/jwa": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", + "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/google-auth-library/node_modules/jws": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", + "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "dependencies": { + "jwa": "^2.0.0", + "safe-buffer": "^5.0.1" + } + }, "node_modules/gopd": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", @@ -2807,6 +2946,37 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true }, + "node_modules/gtoken": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.0.1.tgz", + "integrity": "sha512-KcFVtoP1CVFtQu0aSk3AyAt2og66PFhZAlkUOuWKwzMLoulHXG5W5wE5xAnHb+yl3/wEFoqGW7/cDGMU8igDZQ==", + "dependencies": { + "gaxios": "^6.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/gtoken/node_modules/jwa": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", + "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/gtoken/node_modules/jws": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", + "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "dependencies": { + "jwa": "^2.0.0", + "safe-buffer": "^5.0.1" + } + }, "node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -2890,6 +3060,39 @@ "node": ">= 0.8" } }, + "node_modules/https-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz", + "integrity": "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/https-proxy-agent/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, "node_modules/human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", @@ -3069,7 +3272,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true, "engines": { "node": ">=8" }, @@ -4024,6 +4226,14 @@ "node": ">=4" } }, + "node_modules/json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "dependencies": { + "bignumber.js": "^9.0.0" + } + }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", @@ -4382,6 +4592,25 @@ "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==", "dev": true }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -4495,6 +4724,14 @@ "node": ">=8" } }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/object-inspect": { "version": "1.13.1", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", @@ -5771,6 +6008,11 @@ "node": "*" } }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, "node_modules/type": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", @@ -5935,6 +6177,20 @@ "makeerror": "1.0.12" } }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/server/package.json b/server/package.json index d5d9b3b..535c6e8 100644 --- a/server/package.json +++ b/server/package.json @@ -13,7 +13,9 @@ "dependencies": { "axios": "^1.6.1", "bcryptjs": "^2.4.3", + "cors": "^2.8.5", "express": "^4.18.2", + "google-auth-library": "^9.2.0", "jsonwebtoken": "^9.0.2", "pg": "^8.11.3", "sequelize": "^6.35.0" diff --git a/server/routes/index.js b/server/routes/index.js index da6e65d..d3d6e12 100644 --- a/server/routes/index.js +++ b/server/routes/index.js @@ -10,6 +10,8 @@ router.post("/register", UserController.register); //register router.post("/login", UserController.login); //login +router.post("/google-login", UserController.googleLogin); //GOOGLE LOGIN + router.use(authentication) router.get("/recipes", RecipeController.recipes); From 4c1033a4a70aa60e689adfff4593173fb88c1865 Mon Sep 17 00:00:00 2001 From: pramesthias Date: Wed, 15 Nov 2023 20:57:37 +0700 Subject: [PATCH 10/28] fix: get data from 3rd party API --- server/controllers/RecipeController.js | 91 +++++++++++++------------- server/middlewares/errorHandler.js | 4 +- 2 files changed, 49 insertions(+), 46 deletions(-) diff --git a/server/controllers/RecipeController.js b/server/controllers/RecipeController.js index ff18d71..4d8126c 100644 --- a/server/controllers/RecipeController.js +++ b/server/controllers/RecipeController.js @@ -5,6 +5,8 @@ module.exports = class RecipeController { static async recipes(req, res, next) { + const { search, filter, page } = req.query; + const options = { method: 'GET', url: 'https://low-carb-recipes.p.rapidapi.com/search', @@ -23,16 +25,26 @@ module.exports = class RecipeController { } }; + if (search) { + options.params.name = search; + } + + if (filter) { + options.params.includeIngredients = filter.slice(',').join(';'); + console.log(options.params.includeIngredients) + } + try { const response = await axios.request(options); const recipes = response.data; const mappedRecipes = recipes.map((recipe, index) => { return { - id: ++index, // Increment the counter and assign the new ID + number: ++index, + id: recipe.id, name: recipe.name, description: recipe.description, - prepareTime: recipe.prepareTime, - cookTime: recipe.cookTime, + prepareTime: `${recipe.prepareTime} minutes`, + cookTime: `${recipe.cookTime} minutes`, ingredients: recipe.ingredients.map(ingredient => `${ingredient.name} ${ingredient.servingSize.desc}`), steps: recipe.steps, nutrients: { @@ -49,7 +61,11 @@ module.exports = class RecipeController { } }) - res.status(200).json(mappedRecipes); + const offset = !Number(page) ? 0 : 8 * (page - 1) + const newPage = offset + 8 + const result = mappedRecipes.slice(offset, newPage) + + res.status(200).json(result); } catch (error) { next(error); } @@ -58,18 +74,11 @@ module.exports = class RecipeController { static async recipeById(req, res, next) { + const {id} = req.params + const options = { method: 'GET', - url: 'https://low-carb-recipes.p.rapidapi.com/search', - params: { - maxPrepareTime: '10', - maxCookTime: '20', - maxCalories: '500', - maxNetCarbs: '5', - maxSugar: '3', - maxAddedSugar: '0', - limit: '20' - }, + url: `https://low-carb-recipes.p.rapidapi.com/recipes/${id}`, headers: { 'X-RapidAPI-Key': `${process.env.RECIPES_API_KEY}`, 'X-RapidAPI-Host': 'low-carb-recipes.p.rapidapi.com' @@ -78,37 +87,31 @@ module.exports = class RecipeController { try { const response = await axios.request(options); - const recipes = response.data; - const mappedRecipes = recipes.map((recipe, index) => { - return { - id: ++index, // Increment the counter and assign the new ID - name: recipe.name, - description: recipe.description, - prepareTime: recipe.prepareTime, - cookTime: recipe.cookTime, - ingredients: recipe.ingredients.map(ingredient => `${ingredient.name} ${ingredient.servingSize.desc}`), - steps: recipe.steps, - nutrients: { - caloriesKCal: recipe.nutrients.caloriesKCal, - totalCarbs: recipe.nutrients.totalCarbs, - sugar: recipe.nutrients.sugar, - protein: recipe.nutrients.protein, - fat: recipe.nutrients.fat, - cholesterol: recipe.nutrients.cholesterol, - alcohol: recipe.nutrients.alcohol, - gluten: recipe.nutrients.gluten, - }, - image: recipe.image, - } - }) + const recipe = response.data; + + res.status(200).json({ + id: recipe.id, + name: recipe.name, + description: recipe.description, + prepareTime: recipe.prepareTime, + cookTime: recipe.cookTime, + ingredients: recipe.ingredients.map(ingredient => `${ingredient.name} ${ingredient.servingSize.desc}`), + steps: recipe.steps, + nutrients: { + caloriesKCal: recipe.nutrients.caloriesKCal, + totalCarbs: recipe.nutrients.totalCarbs, + sugar: recipe.nutrients.sugar, + protein: recipe.nutrients.protein, + fat: recipe.nutrients.fat, + cholesterol: recipe.nutrients.cholesterol, + alcohol: recipe.nutrients.alcohol, + gluten: recipe.nutrients.gluten, + }, + image: recipe.image, + }); - const recipe = mappedRecipes.find((recipe) => recipe.id === req.params.id) - console.log(mappedRecipes) - console.log(recipe) // ======= undefined - res.status(200).json(recipe); } catch (error) { - // next(error); - console.log(error) + next(error); } } @@ -144,6 +147,6 @@ module.exports = class RecipeController { } } -//================================================================ + //================================================================ } \ No newline at end of file diff --git a/server/middlewares/errorHandler.js b/server/middlewares/errorHandler.js index 1b9202a..5b50644 100644 --- a/server/middlewares/errorHandler.js +++ b/server/middlewares/errorHandler.js @@ -11,8 +11,8 @@ module.exports = function errorHandler(error, req, res, next) { res.status(401).json({ message: error.message ?? "Unauthenticated" }); break; - case "NotFound": - res.status(404).json({ message: error.message ?? "Data not found" }); //cusine -> error + case "AxiosError": + res.status(404).json({ message: "Recipe not found" }); break; default: From 9f6cb4f736261567f82f9d10a54e889a89fe06c0 Mon Sep 17 00:00:00 2001 From: pramesthias Date: Wed, 15 Nov 2023 21:55:15 +0700 Subject: [PATCH 11/28] feat: mail sending using nodemailer --- server/.env.example | 5 +++- server/app.js | 2 +- server/controllers/MailController.js | 37 ++++++++++++++++++++++++++ server/controllers/RecipeController.js | 33 ----------------------- server/package-lock.json | 9 +++++++ server/package.json | 1 + server/routes/index.js | 7 +++-- 7 files changed, 55 insertions(+), 39 deletions(-) create mode 100644 server/controllers/MailController.js diff --git a/server/.env.example b/server/.env.example index 9c2b61d..d31847a 100644 --- a/server/.env.example +++ b/server/.env.example @@ -2,4 +2,7 @@ JWT_SECRET= PORT= RECIPES_API_KEY= G_CLIENT_ID= -G_SECRET= \ No newline at end of file +G_SECRET= +E_SENDER= +E_PASS= +E_RECEIVER= \ No newline at end of file diff --git a/server/app.js b/server/app.js index 9e6834f..8ca4a61 100644 --- a/server/app.js +++ b/server/app.js @@ -4,7 +4,7 @@ if(process.env.NODE_ENV !== "production"){ const express = require('express'); const app = express(); - const cors = require('cors'); //DISTRIBUTION +const cors = require('cors'); //DISTRIBUTION app.use(cors()) // DEPLOY diff --git a/server/controllers/MailController.js b/server/controllers/MailController.js new file mode 100644 index 0000000..03b67d3 --- /dev/null +++ b/server/controllers/MailController.js @@ -0,0 +1,37 @@ +const nodemailer = require('nodemailer'); + +module.exports = class MailController { + + static async sendMail(req, res, next) { + try { + const { username, email, message } = req.body; + + // console.log(username, email, message) + + const transporter = nodemailer.createTransport({ + service: 'gmail', + auth: { + user: `${process.env.E_SENDER}`, + pass: `${process.env.E_PASS}`, + }, + }); + + const mailOptions = { + from: `${email}`, + to: `${process.env.E_RECEIVER}`, + subject: `New Message from our dearest ${username},`, + text: `${message}`, + }; + + await transporter.sendMail(mailOptions); + + res.status(200).json({ message: "Your message sent successfully" }); + + } catch (error) { + next(error); + console.log(error) + } + } + + +} \ No newline at end of file diff --git a/server/controllers/RecipeController.js b/server/controllers/RecipeController.js index 4d8126c..7f02dee 100644 --- a/server/controllers/RecipeController.js +++ b/server/controllers/RecipeController.js @@ -116,37 +116,4 @@ module.exports = class RecipeController { } - - - - /* - ==================== DEVELOPMENT ======================== - */ - static async getRecDb(req, res, next) { - try { - const recipes = await Recipe.findAll(); - res.status(200).json(recipes); - } catch (error) { - next(error); - } - } - - static async getRecDbId(req, res, next) { - try { - const { id } = req.params; - const recipes = await Recipe.findByPk(id); - - if (!recipes) { - next({ name: 'NotFound', message: "Recipe not found" }); - return; - } - - res.status(200).json(recipes); - } catch (error) { - next(error); - } - } - - //================================================================ - } \ No newline at end of file diff --git a/server/package-lock.json b/server/package-lock.json index 638cd41..b522a30 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -15,6 +15,7 @@ "express": "^4.18.2", "google-auth-library": "^9.2.0", "jsonwebtoken": "^9.0.2", + "nodemailer": "^6.9.7", "pg": "^8.11.3", "sequelize": "^6.35.0" }, @@ -4623,6 +4624,14 @@ "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", "dev": true }, + "node_modules/nodemailer": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.7.tgz", + "integrity": "sha512-rUtR77ksqex/eZRLmQ21LKVH5nAAsVicAtAYudK7JgwenEDZ0UIQ1adUGqErz7sMkWYxWTTU1aeP2Jga6WQyJw==", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/nodemon": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.0.1.tgz", diff --git a/server/package.json b/server/package.json index 535c6e8..0e91f59 100644 --- a/server/package.json +++ b/server/package.json @@ -17,6 +17,7 @@ "express": "^4.18.2", "google-auth-library": "^9.2.0", "jsonwebtoken": "^9.0.2", + "nodemailer": "^6.9.7", "pg": "^8.11.3", "sequelize": "^6.35.0" }, diff --git a/server/routes/index.js b/server/routes/index.js index d3d6e12..dbea220 100644 --- a/server/routes/index.js +++ b/server/routes/index.js @@ -1,3 +1,4 @@ +const MailController = require('../controllers/MailController'); const RecipeController = require('../controllers/RecipeController'); const UserController = require('../controllers/UserController'); const authentication = require('../middlewares/authentication'); @@ -18,11 +19,9 @@ router.get("/recipes", RecipeController.recipes); router.get("/recipes/:id", RecipeController.recipeById); +router.post("/contact", MailController.sendMail); + -// =============== DEVELOPMENT ==================== -router.get("/recDB", RecipeController.getRecDb); -router.get("/recDB/:id", RecipeController.getRecDbId); -// ================================================ router.use(errorHandler); From 1a5b587141dcbea28e7d3d8404c55bd59591972d Mon Sep 17 00:00:00 2001 From: pramesthias Date: Thu, 16 Nov 2023 04:35:07 +0700 Subject: [PATCH 12/28] feat: mail sending in client side --- README.md | 83 +++++++++++++++- client/src/router/index.jsx | 5 + client/src/views/Contact.jsx | 134 ++++++++++++++++++++++++++ server/__tests__/contact.test.js | 0 server/__tests__/google-login.test.js | 0 server/__tests__/login.test.js | 0 server/__tests__/recipe-id.test.js | 0 server/__tests__/recipes.test.js | 0 server/__tests__/register.test.js | 0 server/controllers/MailController.js | 6 +- server/middlewares/errorHandler.js | 3 +- 11 files changed, 224 insertions(+), 7 deletions(-) create mode 100644 client/src/views/Contact.jsx create mode 100644 server/__tests__/contact.test.js create mode 100644 server/__tests__/google-login.test.js create mode 100644 server/__tests__/login.test.js create mode 100644 server/__tests__/recipe-id.test.js create mode 100644 server/__tests__/recipes.test.js create mode 100644 server/__tests__/register.test.js diff --git a/README.md b/README.md index 1fe7117..1e54a13 100644 --- a/README.md +++ b/README.md @@ -116,7 +116,7 @@ _Response (401 - Unauthorized)_ ## Recipes ### GET /recipes -> Get all Recipes +> Get All Recipes _Request Header_ ``` @@ -142,11 +142,11 @@ _Response (200 - OK)_ ``` [ { - "id": integer, + "id": "string", "name": "string", "description": "string", - "prepareTime": integer, - "cookTime": integer, + "prepareTime": "string", + "cookTime": "string", "ingredients": [ "string", "string", @@ -184,7 +184,82 @@ _Response (200 - OK)_ ] ``` +--- +### GET /recipes/:id + +> Get One Recipe by Id + +_Request Header_ +``` +{"Authorization": "Bearer "} +``` + +_URL Params_ +``` +id: integer [required] +``` + +_Data Params_ +``` +not needed +``` + +_Request Body_ +``` +not needed +``` +_Response (200 - OK)_ +``` + +{ + "id": "string", + "name": "string", + "description": "string", + "prepareTime": "string", + "cookTime": "string", + "ingredients": [ + "string", + "string", + "string", + "string", + "string", + "string", + "string", + "string", + "string", + "string" + ], + "steps": [ + "string", + "string", + "string", + "string", + "string", + "string", + "string", + ], + "nutrients": { + "string": integer, + "string": integer, + "string": integer, + "string": integer, + "string": integer, + "string": integer, + "string": integer, + "string": integer + }, + "image": "string" +} + +``` + +_Response (404 - Not Found)_ +``` +{ + "message": "Recipe not found" +} +``` --- ### Global Error diff --git a/client/src/router/index.jsx b/client/src/router/index.jsx index 7d79403..d0b89c0 100644 --- a/client/src/router/index.jsx +++ b/client/src/router/index.jsx @@ -1,6 +1,7 @@ import { createBrowserRouter, redirect } from "react-router-dom"; import Login from "../views/Login"; import Home from "../views/Home"; +import Contact from "../views/Contact"; const router = createBrowserRouter([ // { @@ -15,6 +16,10 @@ const router = createBrowserRouter([ path: "/home", element: , }, + { + path: "/contact", + element: , + }, {/* { path: "/", diff --git a/client/src/views/Contact.jsx b/client/src/views/Contact.jsx new file mode 100644 index 0000000..118cb5f --- /dev/null +++ b/client/src/views/Contact.jsx @@ -0,0 +1,134 @@ +import { useState } from "react"; +import { Link, useNavigate } from "react-router-dom"; +import { toast } from 'react-toastify'; +import axios from "axios"; + +export default function Contact() { + + const navigate = useNavigate(); + const token = localStorage.getItem("access_token") + const [formData, setData] = useState({ + username: "", + email: "", + message: "" + }) + + const handleChange = (event) => { + const { name, value } = event.target + setData({ ...formData, [name]: value }); + }; + + const handleSubmit = async (event) => { + event.preventDefault(); + try { + const { data } = await axios({ + url: "http://localhost:3000/contact", + method: "post", + headers: { + Authorization: `Bearer ${token}` + }, + data: + { + username: formData.username, + email: formData.email, + message: formData.message, + } + }) + // console.log(data) + toast.success('Your message sent successfully 📩', { + position: "top-center", + autoClose: 2000, + hideProgressBar: true, + closeOnClick: true, + pauseOnHover: true, + draggable: true, + progress: undefined, + theme: "dark", + }); + + navigate("/home"); + + } catch ({ response }) { + + toast.error("Unauthenticated", { + position: "top-center", + autoClose: 2000, + hideProgressBar: true, + closeOnClick: true, + pauseOnHover: true, + draggable: true, + progress: undefined, + theme: "dark", + }); + console.log({response}) + } + } + + return ( +
+
+
+
+
+
+
+
+ +

Welcome back!

+ +
+ + +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ +
+ +
+ + + +
+ +
+ +
+
+ + Sample image + +
+
+
+
+
+
+
+
+ + ) +} \ No newline at end of file diff --git a/server/__tests__/contact.test.js b/server/__tests__/contact.test.js new file mode 100644 index 0000000..e69de29 diff --git a/server/__tests__/google-login.test.js b/server/__tests__/google-login.test.js new file mode 100644 index 0000000..e69de29 diff --git a/server/__tests__/login.test.js b/server/__tests__/login.test.js new file mode 100644 index 0000000..e69de29 diff --git a/server/__tests__/recipe-id.test.js b/server/__tests__/recipe-id.test.js new file mode 100644 index 0000000..e69de29 diff --git a/server/__tests__/recipes.test.js b/server/__tests__/recipes.test.js new file mode 100644 index 0000000..e69de29 diff --git a/server/__tests__/register.test.js b/server/__tests__/register.test.js new file mode 100644 index 0000000..e69de29 diff --git a/server/controllers/MailController.js b/server/controllers/MailController.js index 03b67d3..44f3760 100644 --- a/server/controllers/MailController.js +++ b/server/controllers/MailController.js @@ -8,6 +8,8 @@ module.exports = class MailController { // console.log(username, email, message) + if(!username || !email || !message) throw ({name: "IncompleteData", message: "Please fill the required form!"}) + const transporter = nodemailer.createTransport({ service: 'gmail', auth: { @@ -19,8 +21,8 @@ module.exports = class MailController { const mailOptions = { from: `${email}`, to: `${process.env.E_RECEIVER}`, - subject: `New Message from our dearest ${username},`, - text: `${message}`, + subject: `New Message from our dearest ViCYTOr - ${username},`, + text: `From: ${username} \nEmail: ${email} \nMessage: ${message}`, }; await transporter.sendMail(mailOptions); diff --git a/server/middlewares/errorHandler.js b/server/middlewares/errorHandler.js index 5b50644..daa9296 100644 --- a/server/middlewares/errorHandler.js +++ b/server/middlewares/errorHandler.js @@ -3,7 +3,8 @@ module.exports = function errorHandler(error, req, res, next) { switch (error.name) { case "SequelizeValidationError": case "SequelizeUniqueConstraintError": - res.status(400).json({ message: error.errors[0].message }); + case "IncompleteData": + res.status(400).json({ message: error.message ?? error.errors[0].message }); break; case "JsonWebTokenError": From ab367b2768d8511f3673e1fb9694f618a6f93ac1 Mon Sep 17 00:00:00 2001 From: pramesthias Date: Thu, 16 Nov 2023 05:17:28 +0700 Subject: [PATCH 13/28] feat: login and register in client side --- client/src/router/index.jsx | 7 +- client/src/views/Login.jsx | 79 ++++++++++++++++-- client/src/views/Register.jsx | 128 +++++++++++++++++++++++++++++ server/middlewares/errorHandler.js | 5 +- 4 files changed, 209 insertions(+), 10 deletions(-) diff --git a/client/src/router/index.jsx b/client/src/router/index.jsx index d0b89c0..d786379 100644 --- a/client/src/router/index.jsx +++ b/client/src/router/index.jsx @@ -2,6 +2,7 @@ import { createBrowserRouter, redirect } from "react-router-dom"; import Login from "../views/Login"; import Home from "../views/Home"; import Contact from "../views/Contact"; +import Register from "../views/Register"; const router = createBrowserRouter([ // { @@ -9,7 +10,11 @@ const router = createBrowserRouter([ // element: , // }, { - path: "/", + path: "/register", + element: , + }, + { + path: "/login", element: , }, { diff --git a/client/src/views/Login.jsx b/client/src/views/Login.jsx index 3a40459..a272a43 100644 --- a/client/src/views/Login.jsx +++ b/client/src/views/Login.jsx @@ -1,16 +1,73 @@ -import { useEffect } from "react"; import axios from "axios"; -import { useNavigate } from "react-router-dom" +import { Link, useNavigate } from "react-router-dom" import { GoogleLogin } from '@react-oauth/google'; +import { useState } from "react"; +import { toast } from "react-toastify"; export default function Login() { const navigate = useNavigate(); + const [formData, setData] = useState({ + email: "", + password: "" + }) + + const handleChange = (event) => { + const { name, value } = event.target + setData({ ...formData, [name]: value }); + }; + + const handleLogin = async (event) => { + event.preventDefault(); + try { + const { data } = await axios({ + url: "http://localhost:3000/login", + method: "post", + data: + { + email: formData.email, + password: formData.password, + } + }) + + localStorage.setItem("access_token", data.access_token) + + toast.success('Welcome Home ViCYTOr ❣️', { + position: "top-center", + autoClose: 2000, + hideProgressBar: true, + closeOnClick: true, + pauseOnHover: true, + draggable: true, + progress: undefined, + theme: "dark", + }); + + navigate("/home"); + + } catch ({ response }) { + + toast.error(response.data.message, { + position: "top-center", + autoClose: 2000, + hideProgressBar: true, + closeOnClick: true, + pauseOnHover: true, + draggable: true, + progress: undefined, + theme: "dark", + }); + + console.log({ response }) + } + } + + // GOOGLE LOGIN async function handleCredentialResponse(response) { console.log("Encoded JWT ID token: " + response.credential); try { - let {data} = await axios.post("http://localhost:3000/google-login", null, { + let { data } = await axios.post("http://localhost:3000/google-login", null, { headers: { g_token: response.credential } @@ -36,13 +93,12 @@ export default function Login() {

Welcome back!

-
+
-
- {/* */} -
@@ -50,7 +106,8 @@ export default function Login() {
-
@@ -59,6 +116,12 @@ export default function Login() { +
+ + + +
+

OR
diff --git a/client/src/views/Register.jsx b/client/src/views/Register.jsx index e69de29..5447cc3 100644 --- a/client/src/views/Register.jsx +++ b/client/src/views/Register.jsx @@ -0,0 +1,128 @@ +import axios from "axios"; +import { useState } from "react" +import { Link, useNavigate } from "react-router-dom"; +import { toast } from 'react-toastify'; + +export default function Register() { + + const navigate = useNavigate(); + const [formData, setData] = useState({ + username: "", + email: "", + password: "", + }) + + const handleInput = (event) => { + const { name, value } = event.target + setData({ ...formData, [name]: value }); + }; + + + const handleRegister = async (event) => { + event.preventDefault(); + try { + const { data } = await axios({ + url: "http://localhost:3000/register", + method: "post", + data: + { + username: formData.username, + email: formData.email, + password: formData.password + } + }) + console.log(data) + + toast.success('Thank You For Joining', { + position: "top-center", + autoClose: 2000, + hideProgressBar: true, + closeOnClick: true, + pauseOnHover: true, + draggable: true, + progress: undefined, + theme: "dark", + }); + + navigate("/login"); + + } catch ({ response }) { + + toast.error(response.data.message, { + position: "top-center", + autoClose: 2000, + hideProgressBar: true, + closeOnClick: true, + pauseOnHover: true, + draggable: true, + progress: undefined, + theme: "dark", + }); + + console.log({ response }) + } + } + + + return ( +
+
+
+
+
+
+
+
+ +

Register

+ + + +
+
+ + +
+
+ +
+
+ + +
+
+ +
+
+ + +
+
+ + +
+ + + + +
+ + + +
+
+ + Sample image + +
+
+
+
+
+
+
+
+ + ) +} \ No newline at end of file diff --git a/server/middlewares/errorHandler.js b/server/middlewares/errorHandler.js index daa9296..5b3a530 100644 --- a/server/middlewares/errorHandler.js +++ b/server/middlewares/errorHandler.js @@ -3,8 +3,11 @@ module.exports = function errorHandler(error, req, res, next) { switch (error.name) { case "SequelizeValidationError": case "SequelizeUniqueConstraintError": + res.status(400).json({ message: error.errors[0].message}); + break; + case "IncompleteData": - res.status(400).json({ message: error.message ?? error.errors[0].message }); + res.status(400).json({ message: error.message }); break; case "JsonWebTokenError": From 9c81fb5649129bca8636ce77627b833ab2140ab2 Mon Sep 17 00:00:00 2001 From: pramesthias Date: Thu, 16 Nov 2023 06:11:59 +0700 Subject: [PATCH 14/28] docs: add api docs for google login and mail sender --- README.md | 85 +++- client/src/components/Navbar.jsx | 1 + client/src/router/index.jsx | 11 +- client/src/views/Contact.jsx | 4 +- client/src/views/Home.jsx | 6 + client/src/views/Landing.jsx | 128 +++++ client/src/views/Register.jsx | 2 +- server/controllers/UserController.js | 3 + server/data/recipe.json | 481 ------------------ .../20231114142906-create-recipe.js | 49 -- server/models/recipe.js | 28 - server/routes/index.js | 2 +- server/seeders/20231114143111-seed-recipes.js | 22 - 13 files changed, 230 insertions(+), 592 deletions(-) create mode 100644 client/src/components/Navbar.jsx delete mode 100644 server/data/recipe.json delete mode 100644 server/migrations/20231114142906-create-recipe.js delete mode 100644 server/models/recipe.js delete mode 100644 server/seeders/20231114143111-seed-recipes.js diff --git a/README.md b/README.md index 1e54a13..c010188 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![Open in Visual Studio Code](https://classroom.github.com/assets/open-in-vscode-718a45dd9cf7e7f842a935f5ebbe5719a5e09af4491e668f4dbf3b35d5cca122.svg)](https://classroom.github.com/online_ide?assignment_repo_id=12856485&assignment_repo_type=AssignmentRepo) # Individual Project Phase 2 -CYTO is my Individual Project application developed for assignment purposes. +CYTO is my Individual Project developed for assignment purposes. It is an application ___ This app has : * RESTful endpoint for asset's CRUD operation @@ -113,6 +113,42 @@ _Response (401 - Unauthorized)_ ``` --- +### POST /google-login + +> User login via Google Account + +_Request Header_ +``` +"g_token" +``` + +_URL Params_ +``` +not needed +``` + +_Data Params_ +``` +not needed +``` + +_Request Body_ +``` +{ + "email": "string", + "username": "string", + "password": "string" +} +``` + +_Response (200 - OK)_ +``` +{ + "access_token": "string" +} +``` +--- + ## Recipes ### GET /recipes @@ -211,7 +247,6 @@ not needed _Response (200 - OK)_ ``` - { "id": "string", "name": "string", @@ -251,7 +286,6 @@ _Response (200 - OK)_ }, "image": "string" } - ``` _Response (404 - Not Found)_ @@ -262,6 +296,51 @@ _Response (404 - Not Found)_ ``` --- +### POST /contact-mail + +> Send Message to the CYTO team + +_Request Header_ +``` +{"Authorization": "Bearer "} +``` + +_URL Params_ +``` +not needed +``` + +_Data Params_ +``` +not needed +``` + +_Request Body_ +``` +{ + "username":"string", + "email": "string", + "message": "string" +} +``` + +_Response (200 - OK)_ +``` +{ + "message": "Your message sent successfully" +} + +``` + +_Response (400 - Bad Request)_ +``` +{ + "message": "Please fill the required form!" +} +``` + +--- + ### Global Error _Response (401 - Unauthorized)_ diff --git a/client/src/components/Navbar.jsx b/client/src/components/Navbar.jsx new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/client/src/components/Navbar.jsx @@ -0,0 +1 @@ + diff --git a/client/src/router/index.jsx b/client/src/router/index.jsx index d786379..a87c890 100644 --- a/client/src/router/index.jsx +++ b/client/src/router/index.jsx @@ -3,12 +3,13 @@ import Login from "../views/Login"; import Home from "../views/Home"; import Contact from "../views/Contact"; import Register from "../views/Register"; +import Landing from "../views/Landing"; const router = createBrowserRouter([ - // { - // path: "/", - // element: , - // }, + { + path: "/", + element: , + }, { path: "/register", element: , @@ -22,7 +23,7 @@ const router = createBrowserRouter([ element: , }, { - path: "/contact", + path: "/contact-mail", element: , }, {/* { diff --git a/client/src/views/Contact.jsx b/client/src/views/Contact.jsx index 118cb5f..3f30463 100644 --- a/client/src/views/Contact.jsx +++ b/client/src/views/Contact.jsx @@ -22,7 +22,7 @@ export default function Contact() { event.preventDefault(); try { const { data } = await axios({ - url: "http://localhost:3000/contact", + url: "http://localhost:3000/contact-mail", method: "post", headers: { Authorization: `Bearer ${token}` @@ -50,7 +50,7 @@ export default function Contact() { } catch ({ response }) { - toast.error("Unauthenticated", { + toast.error(response.data.message, { position: "top-center", autoClose: 2000, hideProgressBar: true, diff --git a/client/src/views/Home.jsx b/client/src/views/Home.jsx index ba69d55..a21c4cc 100644 --- a/client/src/views/Home.jsx +++ b/client/src/views/Home.jsx @@ -20,4 +20,10 @@ export default function Home() { navigate("/landing"); } + + return( + <> + + ) + } \ No newline at end of file diff --git a/client/src/views/Landing.jsx b/client/src/views/Landing.jsx index e69de29..de95db3 100644 --- a/client/src/views/Landing.jsx +++ b/client/src/views/Landing.jsx @@ -0,0 +1,128 @@ + + +export default function Landing() { + + return ( + +
+ + + + + +
+ +
    +
  1. +
  2. +
  3. +
+ + +
+ +
+
+
+
+

Learn Bootstrap 5 with MDB

+
Best & free guide of responsive web design
+ Start tutorial + Download MDB UI KIT +
+
+
+
+ + +
+
+
+
+

You can place here any content

+
+
+
+
+ + +
+
+
+
+

And cover it with any mask

+ Learn + about masks +
+
+
+
+
+ + + + Previous + + + + Next + +
+ +
+ + ) +} \ No newline at end of file diff --git a/client/src/views/Register.jsx b/client/src/views/Register.jsx index 5447cc3..204e159 100644 --- a/client/src/views/Register.jsx +++ b/client/src/views/Register.jsx @@ -33,7 +33,7 @@ export default function Register() { }) console.log(data) - toast.success('Thank You For Joining', { + toast.success('Thank You For Joining 🥰', { position: "top-center", autoClose: 2000, hideProgressBar: true, diff --git a/server/controllers/UserController.js b/server/controllers/UserController.js index 728dbc0..a6239fb 100644 --- a/server/controllers/UserController.js +++ b/server/controllers/UserController.js @@ -60,6 +60,7 @@ module.exports = class UserController { static async googleLogin(req, res, next) { console.log(req.headers.g_token) + try { const ticket = await client.verifyIdToken({ idToken: req.headers.g_token, @@ -67,6 +68,8 @@ module.exports = class UserController { }); const payload = ticket.getPayload(); + console.log(payload) + const [user, isNewRecord] = await User.findOrCreate({ where: { email: payload.email diff --git a/server/data/recipe.json b/server/data/recipe.json deleted file mode 100644 index 4415c87..0000000 --- a/server/data/recipe.json +++ /dev/null @@ -1,481 +0,0 @@ -[ - { - "id": 1, - "name": "Keto Sesame Tuna and Egg Salad", - "description": "This quick and easy tuna and egg salad is combined with aromatic scallions and parsley and coated in a sesame, lemon mayo.\n\nThis is a great option for stuffing lettuce wraps or topping low carb crackers.", - "prepareTime": 8, - "cookTime": 0, - "ingredients": [ - "Hard Boiled Egg 2 medium", - "Scallions 2 medium - 4 1/8\" long", - "Canned Tuna 1-¾ ounce", - "Lemon Juice, Fresh 1 teaspoon", - "Parsley, Fresh 1 tablespoon", - "Sesame Oil 1 teaspoon", - "Mayonnaise 1 tablespoon", - "Sesame Seeds, Hulled, Toasted, Unsalted 1 teaspoon, whole pieces", - "Salt, Sea Salt ⅛ teaspoon", - "Black Pepper ⅛ teaspoon" - ], - "steps": [ - "Peel and roughly chop the eggs and add to a mixing bowl.", - "Thinly slice the scallions and roughly chop the parsley. Add to the mixing bowl along with the tuna and stir well to combine.", - "Add the mayonnaise to a mixing bowl with the lemon juice, sesame oil, salt and pepper. Whisk together until smooth.", - "Spoon the dressing over the salad and stir well to coat.", - "Sprinkle over the sesame seeds to serve." - ], - "nutrients": [ - "caloriesKCal", - "totalCarbs", - "sugar", - "protein", - "fat", - "cholesterol", - "alcohol", - "gluten" - ], - "image": "https://tinyurl.com/2p82zzca/133fcdec-4595-44be-a302-02df4c209451.png" - }, - { - "id": 2, - "name": "Keto Vanilla Butter Latte", - "description": "This creamy Keto vanilla BULLETPROOF® coffee recipe is rich in fats, frothy plant-based milk, and a hint of sweet vanilla. This Keto vanilla butter coffee makes a great fat-fueled breakfast option to kickstart your day. Alternatively, this is perfect served with a slice of low-carb cake as an afternoon treat!\n\n### What ingredients are in this Keto vanilla butter latte?\n\nThis Keto vanilla BULLETPROOF® coffee is prepared with hot almond milk infused with sweet vanilla extract. The milk is then blended with instant coffee and a hearty helping of unsalted butter, before being blitzed until super frothy! We have used a handheld stick blender to froth and blend our Keto coffee, however, you may use a freestanding blender for this if preferred.\n\n### Here are some tips to make Keto vanilla butter coffee?\n\nThis Keto BULLETPROOF® coffee provides a generous serving of fats from unsalted butter. If preferred you may swap the butter for coconut oil or BULLETPROOF® brain octane oil (MCT Oil) or even a blend of fats. You may also swap the erythritol for your preference in low-carb sweetener or omit it entirely if desired. Please be sure to adjust your macros for any changes made.\n\n### Can you use freshly brewed coffee in this Keto recipe?\n\nWhile this recipe calls for instant coffee, you can definitely substitute it for espresso or freshly brewed coffee, just make sure its strong.\n\nBULLETPROOF® is a registered trademark owned by Bulletproof Digital, Inc.", - "prepareTime": 8, - "cookTime": 3, - "ingredients": [ - "Butter, Unsalted 1-½ tablespoon", - "Almond Milk, Vanilla Or Other Flavors, Unsweetened 1-½ cup", - "Instant Coffee 1 teaspoon", - "Vanilla Extract ¼ teaspoon", - "100% Pure Erythritol by Now 1 tsp" - ], - "steps": [ - "Add the almond milk and vanilla extract to a small saucepan over low/medium heat. Stir to combine. Heat the milk through until piping hot but not boiling.", - "Add the instant coffee to a large mug. Add the granulated sweetener. Add the hot milk and stir well to combine, dissolving the coffee and sweetener.", - "Add the butter to the mug. Stir well to combine. Let the butter completely melt into the milky coffee.", - "Carefully use a hand held stick blender or milk frother and blend the coffee and butter. Continue blending until you have a thick frothy layer on the top of your coffee. Serve hot." - ], - "nutrients": [ - "caloriesKCal", - "totalCarbs", - "sugar", - "protein", - "fat", - "cholesterol", - "alcohol", - "gluten" - ], - "image": "https://tinyurl.com/2p82zzca/ccef957b-351f-4c14-b33c-ef050045b877.png" - }, - { - "id": 3, - "name": "Keto Bird Nests", - "description": "This deliciously low-carb breakfast is light to eat and totally instagram-worthy! Spiralized zucchini noodles are gently seasoned and are each a vibrant bed to a single egg. Feel free to expand on this recipe and add additional seasonings or herbs. You can even add a little cheese for more fat and protein!\n\nJessica L.", - "prepareTime": 10, - "cookTime": 20, - "ingredients": [ - "Zucchini 8-½ ounce", - "Olive Oil 2-½ teaspoon", - "Raw Egg 4 large", - "Salt 1 teaspoon", - "Black Pepper 1 teaspoon" - ], - "steps": [ - "Preheat an oven to 350 degrees and line a sheet tray with aluminum foil or simply grease it. Spiralize a large zucchini, and break up the spirals slightly with your fingers.", - "Divide the zucchini into 4 sections (just slightly over 2 oz. each), and arrange them into “nests” on your sheet tray. Create a small space in the center of each nest. Then, drizzle the olive oil gently across the zucchini.", - "Crack an egg into each zucchini nest. Sprinkle about ¼ tsp each of the salt and pepper over each bird nest.", - "Bake the bird nests for approx 20 minutes. This is enough time for the eggs to fully cook and the zucchini to turn just slightly golden on the edges. Allow the tray to cool for 2-3 minutes before transferring the finished recipe to plates with a spatula.", - "You may cook the bird nests for less than 20 minutes, if you want runnier eggs!" - ], - "nutrients": [ - "caloriesKCal", - "totalCarbs", - "sugar", - "protein", - "fat", - "cholesterol", - "alcohol", - "gluten" - ], - "image": "https://tinyurl.com/2p82zzca/348b12ac-3cf3-44a7-b193-4c686a07c0f2.png" - }, - { - "id": 4, - "name": "Keto Thai Spicy Peanut Chicken With Asian Slaw", - "description": "Do you love Thai food? This Thai-inspired chicken dish is a little spicy, a little nutty from some peanut butter, and tossed with fresh herbs. A cool and creamy Asian slaw is paired on the side to combat any heat from the chili sauce. You can have your lunch or dinner ready in 20 minutes or less! You can double this recipe for 4 days of meal prep as well.\n\nJessica L.", - "prepareTime": 10, - "cookTime": 8, - "ingredients": [ - "Packaged Salads Fresh Favorites Colorful Coleslaw by Dole 1 cups", - "Salt 0.25 teaspoon", - "Rice Vinegar 1 teaspoon", - "Mayo 1 tablespoon", - "Sesame Seeds ¼ teaspoon, whole pieces", - "Lime Juice, Fresh 1 wedge - juice from one wedge or slice", - "Boneless Skinless Chicken Breast 10 ounce", - "Olive Oil ½ tablespoon", - "Black Pepper 0.25 teaspoon", - "Peanut Butter 1 tablespoon", - "Sambal Oelek Paste Of Chili by Rooster 1 tablespoon", - "Soy Sauce 2 tablespoon", - "Cilantro ½ tablespoon, chopped", - "Basil 4 leaf", - "Lime Juice, Fresh 2 wedge - juice from one wedge or slice" - ], - "steps": [ - "In a bowl, combine the slaw mix, salt, vinegar, mayo, sesame seeds, and first amount of lime juice. Set this slaw mixture aside for later, giving the salt time to draw out the juices.", - "Trim any unwanted fat from your chicken, and chop the chicken into 1-inch cubes.", - "Heat the olive oil in a medium sized pan on medium-high heat. Toss in the chicken and black pepper, and cook these ingredients until the chicken just cooks through.", - "Use a wooden spoon to move the chicken the the outer edges of the pan and turn the heat to very low. Place the peanut butter, chili sauce, and soy sauce in the exposed center of the pan.", - "Let the heat melt the peanut butter slightly, then stir and combine the chicken and sauce into a consistent and creamy mixture.", - "Finally, chop the cilantro and basil and toss these with the chicken.", - "Serve the hot chicken with the cool slaw and two lime wedges. You can add extra cilantro as a garnish at your discretion. Enjoy!" - ], - "nutrients": [ - "caloriesKCal", - "totalCarbs", - "sugar", - "protein", - "fat", - "cholesterol", - "alcohol", - "gluten" - ], - "image": "https://tinyurl.com/2p82zzca/2b3146d9-f265-48f1-8142-6d445ad8f4a8.png" - }, - { - "id": 5, - "name": "Keto Green Beans and Scallions", - "description": "These aromatic green beans are cooked with tender scallions, spicy chili flakes, a hint of zesty lime and sesame oil.\n\nThese are a great accompaniment to Asian inspired dishes or grilled fish.", - "prepareTime": 5, - "cookTime": 12, - "ingredients": [ - "Green Beans (string Beans), Raw 5 ounce", - "Lime Juice, Fresh 2 teaspoon", - "Scallions 2 medium - 4 1/8\" long", - "Olive Oil 1-½ tablespoon", - "Hot Chili Pepper, Dried, With Seeds 1 teaspoon", - "Sesame Oil 1 teaspoon", - "Salt, Sea Salt ¼ teaspoon", - "Black Pepper ⅛ teaspoon" - ], - "steps": [ - "Add the green beans to a pan of boiling water and simmer for 4-5 minutes until tender. Drain completely.", - "Thinly slice the scallions. Heat a tablespoon of olive oil in a skillet over a low/medium heat. Add the scallions and sauté for 2-3 minutes until tender.", - "Whisk together the remaining olive oil, sesame oil, lime juice, chili flakes, salt and pepper. Add the green beans to the pan and drizzle over the spiced oil. Stir well to combine and cook for a further minute or two to heat the beans through." - ], - "nutrients": [ - "caloriesKCal", - "totalCarbs", - "sugar", - "protein", - "fat", - "cholesterol", - "alcohol", - "gluten" - ], - "image": "https://tinyurl.com/2p82zzca/1a69a125-7c33-4664-b4c2-f4fdddab8781.png" - }, - { - "id": 6, - "name": "Low Carb Spiced Bok Choy and Broccoli Soup", - "description": "This warming soup is packed with low carb broccoli and Bok choy, aromatic cumin, fiery ginger and topped with spicy chili flakes to serve.\n\nThis makes a great lunch or dinner option served with a slice of keto bread.", - "prepareTime": 8, - "cookTime": 13, - "ingredients": [ - "Vegetable Broth, Bouillon Or Consomme 4 cup", - "Cilantro 2 tablespoon", - "Bok Choy 1 cup", - "Olive Oil 1 tablespoon", - "Ginger Root, Raw 1 slice - 1\" diameter", - "Broccoli Florets 1 cups", - "Shallot 1 tablespoon, chopped", - "Garlic 1 clove", - "Hot Chili Pepper, Dried, With Seeds 1 teaspoon", - "Cumin, Seed ½ teaspoon" - ], - "steps": [ - "Thinly slice the shallot and garlic. Heat the oil in a large saucepan over a low/medium heat. Add the garlic and shallots and sweat gently until tender.", - "Slice the tough base from the Bok choy to separate the individual stalks. Cut the leaves from the stalks and reserve to one side. Grate the ginger and add to the pan along with the broccoli florets, Bok choy stalks and cumin seeds. Stir to combine and cook for 2-3 minutes to soften the vegetables and toast the cumin seeds.", - "Add the stock and bring to a gentle boil. Simmer for 5-6 minutes or until the vegetables are fork tender.", - "Add the Bok choy leaves and stir until just wilted. Transfer to a blender or use a handheld stick blender and process until smooth.", - "Scatter with dried chili flakes and fresh cilantro to serve." - ], - "nutrients": [ - "caloriesKCal", - "totalCarbs", - "sugar", - "protein", - "fat", - "cholesterol", - "alcohol", - "gluten" - ], - "image": "https://tinyurl.com/2p82zzca/682207ec-7c88-4eb4-84d8-ddd1703d9346.png" - }, - { - "id": 7, - "name": "Keto Blue Cheese Dressing", - "description": "This quick and easy blue cheese dressing is rich, creamy and perfect for adding a boost of fats and flavor to your Keto meals. Simple to prepare, this blue cheese dressing does not require any cooking and is ready in just a matter of minutes. A great dressing to have on hand in the fridge, ready to drizzle over salads and meats or for dipping low carb vegetables.\n\n### Is Blue Cheese Dressing Keto?\n\nIt’s very simple to make your own Keto-friendly blue cheese dressing. Store-bought versions may contain added fillers and sugars that are not Keto-friendly, but our low carb dressing is so quick to prepare there’s no need to reach for shop-bought again! Our quick and easy Keto dressing combines natural yogurt, mayonnaise, a hint of lemon, a touch of garlic and tangy blue cheese. The ingredients are mixed together until creamy, creating a delicious and pourable dressing.\n\n### Serving Suggestions\n\nThis thick and creamy dressing is versatile in its uses. The dressing can be thinned as desired, perfect for drizzling over your favorite Keto salads. Alternatively, you can keep the dressing slightly thicker, perfect served with meats. We love to pair our blue cheese dressing with Keto burgers. It is also delicious added to salads with crispy bacon.", - "prepareTime": 8, - "cookTime": 0, - "ingredients": [ - "Water 1 tablespoon", - "Lemon Juice 1 teaspoon", - "Dannon All Natural Yogurt, Plain ⅓ cup", - "Mayonnaise ⅓ cup", - "Blue Cheese ⅓ cup", - "Black Pepper ⅛ teaspoon", - "Garlic, Powder ¼ teaspoon" - ], - "steps": [ - "Add the yogurt, mayonnaise and lemon juice to a mixing bowl. You may use a homemade or sugar-free store-bought mayonnaise. You may also use any plain yogurt you desire. Be sure to adjust your macros for any changes made. Whisk the yogurt, mayonnaise and lemon juice together to combine until smooth.", - "Crumble the blue cheese into the mayo and yogurt mix. You can crumble the cheese roughly so you have a mixture of large chunks and smaller pieces. Add the garlic powder and black pepper. You can omit or adjust the quantities of these seasonings as desired. Mix together well to combine, distributing the cheese evenly throughout the dressing. If you would prefer an entirely smooth dressing you can prepare this in a food processor, blending until smooth.", - "Add the water to the dressing and whisk well to combine. This can be omitted or increased to suit your personal preference. Add more water for a thinner salad dressing or omit entirely if you want a thicker dressing for topping meats and burgers. Refrigerate in an airtight container until ready to serve." - ], - "nutrients": [ - "caloriesKCal", - "totalCarbs", - "sugar", - "protein", - "fat", - "cholesterol", - "alcohol", - "gluten" - ], - "image": "https://tinyurl.com/2p82zzca/2a4fac4d-b935-4360-b72c-934e34895681.png" - }, - { - "id": 8, - "name": "Keto One Pan Garlic Butter Chicken Thighs", - "description": "These simple one pan chicken thighs are cooked in a generous helping of butter alongside aromatic garlic, rosemary and lemon.\n\nPerfect for a quick and easy weeknight meal with minimal washing up!", - "prepareTime": 5, - "cookTime": 20, - "ingredients": [ - "Chicken Thigh, Skin Removed Before Cooking 4 medium", - "Garlic 3 clove", - "Unsalted Butter 2 tablespoon", - "Lemon Peel Or Zest Raw 2 tsp", - "Olive Oil 1 tablespoon", - "Rosemary 1 x 1 x sprig", - "Salt, Sea Salt ¼ teaspoon", - "Black Pepper ⅛ teaspoon" - ], - "steps": [ - "Heat the olive oil in a large frying pan or skillet over a medium heat and add the chicken thighs. Fry the thighs until they just start to colour.", - "Add the butter to the pan and continue to cook the chicken until golden brown all over. Set aside.", - "Reduce the heat, slice the garlic and add to the pan cooking gently in the butter until fragrant, taking care not to burn the butter or garlic.", - "Return the chicken thighs to the pan along with the lemon zest and tear in the rosemary.", - "Continue to cook over a medium heat, spooning the hot butter over the chicken until completely cooked through and no longer pink. Serve with steamed green beans or broccoli." - ], - "nutrients": [ - "caloriesKCal", - "totalCarbs", - "sugar", - "protein", - "fat", - "cholesterol", - "alcohol", - "gluten" - ], - "image": "https://tinyurl.com/2p82zzca/1f11e666-3601-4f21-9cf2-c9c7427481b7.png" - }, - { - "id": 9, - "name": "Keto Tahini, Tamari Mushrooms", - "description": "These simple mushrooms are pan fried until tender then tossed with creamy tahini and tangy tamari sauce.\n\nThese make a great side dish to Asian inspired meals.", - "prepareTime": 3, - "cookTime": 8, - "ingredients": [ - "Mushrooms 6 medium", - "Coconut Oil 1 tablespoon", - "Tahini 1 teaspoon", - "Tamari Sauce 1 tablespoon" - ], - "steps": [ - "Melt the coconut oil in a skillet over a medium heat. Thinly slice the mushrooms and add to the skillet, cook through for 6-7 minutes until browned all over and tender.", - "Add the tahini and tamari and stir well, melting the tahini into the mushrooms. Cook briefly until the mushrooms are golden and coated in tamari and tahini. Serve hot. " - ], - "nutrients": [ - "caloriesKCal", - "totalCarbs", - "sugar", - "protein", - "fat", - "cholesterol", - "alcohol", - "gluten" - ], - "image": "https://tinyurl.com/2p82zzca/e77c981f-193e-42d6-b250-47150568c661.png" - }, - { - "id": 10, - "name": "Keto Chocolate Cake Donuts", - "description": "I love how soft, moist and chocolatey these donuts are! They are full of rich chocolate flavor and great with a morning cup of coffee!", - "prepareTime": 10, - "cookTime": 15, - "ingredients": [ - "Almond Flour 1-¼ cup", - "Brown Sugar Replacement by Swerve ⅓ cup", - "Baking Powder 1 teaspoon", - "100% Cocoa Special Dark by Hershey's 3 tbsp", - "Extra Virgin Olive Oil 1-½ tablespoon", - "Original Coconut Milk Unsweetened by Silk 2 tablespoon", - "Raw Egg 2 large", - "Vanilla Extract 1 teaspoon", - "Coarse Kosher Salt by Morton ⅛ tsp", - "The Ultimate Icing Sugar Replacement by Swerve ⅓ cup", - "Original Coconut Milk Unsweetened by Silk 3 tablespoon", - "Vanilla Extract ¼ teaspoon" - ], - "steps": [ - "Preheat an oven to 350 F. Prepare a donut pan with nonstick cooking spray.", - "In a food processor, combine almond flour, sweetener, dark cocoa powder and baking powder together. Pulse to combine.", - "Add in the olive oil, milk, eggs, vanilla extract and ⅛ t kosher salt. Pulse to turn into a smooth batter. ", - "Spoon and smooth batter evenly into the donut pan to make 6 donuts. Being careful not to overfill the pan so as they rise the dough will not close the center. You can wipe the centers of the donuts to keep them free of batter.", - "Bake for 15 minutes or until cooked through. ", - "Allow donuts to cool in the pan for 15 minutes before removing to a cooling rack.", - "In the meantime, prepare the glaze by combining the rest of the Swerve, 3 tablespoons of coconut milk, ¼ t vanilla extract, and a pinch of salt. Dip the top of each donut into the glaze and then set aside on the rack to dry. Keep donuts stored in an airtight container for up to one week." - ], - "nutrients": [ - "caloriesKCal", - "totalCarbs", - "sugar", - "protein", - "fat", - "cholesterol", - "alcohol", - "gluten" - ], - "image": "https://tinyurl.com/2p82zzca/fe58600a-a0b3-4b72-b0f1-691bd85f3198.png" - }, - { - "id": 11, - "name": "Keto Scrambled Egg and Tuna Avocado Boats", - "description": "As a delicious breakfast or lunch, soft avocado is overflowing with scrambled eggs and tender tuna. Despite the over-the-top tuna filling, melted layers of cheese act as the ‘glue’ to hold everything in place. Dig in with a spoon or fork!\n\nJessica L.", - "prepareTime": 8, - "cookTime": 9, - "ingredients": [ - "Raw Egg 2 large", - "Heavy Cream 1 tablespoon", - "Salt ⅛ teaspoon", - "Black Pepper, Ground ⅛ teaspoon", - "Onion Powder ⅛ teaspoon", - "Paprika ¼ teaspoon", - "Canned Tuna 1 can - each 5 ounce net weight", - "Avocado 4-½ ounce", - "Salt ⅛ teaspoon", - "Provolone Cheese 2 slice- each 3/4 ounce" - ], - "steps": [ - "Preheat an oven to 350 degrees. In a bowl, whisk together the egg, cream, first amount of salt, pepper, onion powder, and paprika. Set this aside near the stove for later.", - "In a small pan over medium heat, place in the drained tuna. Continually cook and toss the tuna until it starts to turn golden brown and dries out a little.", - "Turn the stove heat to low and pour the egg mixture into the pan. Slowly stir the egg and tuna until the egg scrambles and is evenly distributed with the tuna. You may wish to leave the egg slightly undercooked, since it will be cooking further in the oven for a few minutes later on.", - "Prepare a whole avocado by slicing it in half, removing the pit, and scooping out 1-2 tablespoons of fruit from the middle of each half. Sprinkle the second amount of salt over the avocado. Next, set the avocado halves on a sheet tray lined with foil or parchment paper. Use a spoon to fill each avocado half with the scrambled egg tuna filling. Gently pack the filling down, as it piles high. Finally, layer a slice of provolone cheese over each avocado half.", - "Bake the tray for just a few minutes - until the cheese is melted and the avocado has been warmed." - ], - "nutrients": [ - "caloriesKCal", - "totalCarbs", - "sugar", - "protein", - "fat", - "cholesterol", - "alcohol", - "gluten" - ], - "image": "https://tinyurl.com/2p82zzca/a336c4de-5d90-4a89-90ce-e0bbee045c62.png" - }, - { - "id": 12, - "name": "Keto Instant Pot Beef and Onion Soup", - "description": "This easy Keto instant pot soup is prepared with just a handful of flavorsome ingredients and pressure cooked in only a matter of minutes. Both low in carbs and Paleo, this beef and onion soup makes a great warming lunch or dinner option. Perfect served with a slice of Keto bread for dipping.\n\n### How to Make Keto Soup\n\nKeto soup is a great addition to your weekly meal rotation. In order to create the best Keto soups, it is best to combine low carb veggies with a source of Keto friendly fats. For our Keto beef and onion soup we have used beef steak and olive oil to provide our fats and complemented this with onion and cauliflower for our veggies. The beef and onion are browned in the instant pot, then pressure cooked in stock for just 8 minutes until the beef is perfectly tender.\n\n### Serving Suggestions\n\nOur Paleo friendly instant pot soup makes a great lunch or light dinner option. We love our soup served with sliced and buttered Keto bread. This is also delicious accompanied by warm Keto bread rolls for dipping.", - "prepareTime": 5, - "cookTime": 8, - "ingredients": [ - "Beef Steak, Porterhouse, Visible Fat Eaten 9 ounce", - "Beef Broth, Bouillon Or Consomme, Ready-to-serve Can, Low Sodium 4 cup", - "Olive Oil 2 tablespoon", - "Cauliflower 2 cup", - "Onion 1 large", - "Garlic 1 clove" - ], - "steps": [ - "Add the olive oil to your instant pot. Set to sauté and allow the oil to get hot. Whilst the oil is heating, roughly slice the steak into half inch strips. Carefully add the steak strips to the instant pot. Using the sauté function, brown the beef all over.", - "Whilst the steak browns, thinly slice the onion and 1 large garlic clove. Add the sliced onion and garlic to the instant pot with the steak. Stir well to combine. Sweat the onion and garlic until tender and lightly golden. Take care not to burn the onion or garlic as this will impair the flavor of the soup.", - "Whilst the onion cooks, dice the cauliflower into bite sized chunks. Add the cauliflower pieces and the stock to the instant pot and stir well to combine. At this point you may season with salt and pepper if you wish. This will depend on the level of seasoning already in your stock. You may also use chicken or vegetable stock if preferred but be sure to adjust your macros for any changes.", - "Place the lid on the instant pot with the valve set to ‘sealing’ and set to high pressure. Set the time for 8 minutes. After this time, do a quick release to release the steam. Once the pressure has been released and it is safe to do so, open the lid and portion out the soup for serving. Taste and adjust seasonings if required." - ], - "nutrients": [ - "caloriesKCal", - "totalCarbs", - "sugar", - "protein", - "fat", - "cholesterol", - "alcohol", - "gluten" - ], - "image": "https://tinyurl.com/2p82zzca/2d1b92c4-84fd-48ed-ba94-dcb6148170e9.png" - }, - { - "id": 13, - "name": "Keto Vegan Cilantro Lime Rice", - "description": "Keto Cilantro Lime Rice is the perfect Keto side dish for any Keto or Low Carb Mexican dinner recipe. Tender, sweet cauliflower rice is tossed with freshly squeezed lime juice and chopped cilantro. The best part is it is heated entirely in the microwave, which means this side dish only takes 5 minutes to prep and cook. Keto Cilantro Lime Rice is a great side dish to have ready-to-go in the fridge whenever hunger strikes; simply pair it with any protein of your choice.\n\n### What kind of cauliflower rice should I use?\n\nFeel free to make homemade cauliflower rice, but store-bought is the quickest way to put together this side dish with minimal effort.\n\nhttps://www.carbmanager.com/recipe-detail/ug:9bfcc61a-9ac3-c122-f2e4-bfaa142c0d6f/cauliflower-rice\n\n### What do I do if I do not have a microwave?\n\nYou can heat the cauliflower rice over medium-low heat until hot, and then add the lime and cilantro to the dish at the very end.\n\nWhat other flavors can I add to the dish?\n\n### Utilizing spices like dark chili powder or chipotle powder can spice up this side dish.\n\nServing suggestions\n\n### Keto Cilantro Lime Rice would be delicious with Keto Baja Pork Shoulder:\n\nhttps://www.carbmanager.com/recipe-detail/ug:3b26040619934242853ba66dfe696353/keto-baja-pork-shoulder", - "prepareTime": 0, - "cookTime": 0, - "ingredients": [ - "Cauliflower rice undefined", - "Cilantro undefined", - "Lime undefined", - "Salt, sea salt undefined" - ], - "steps": [ - "Place cauliflower rice into a medium-sized microwave-safe bowl. Microwave on high for 4 minutes. \n", - "In the meantime, wash and chop the cilantro leaves. Add them to the cauliflower rice once heated. Then squeeze on the lime juice and add the salt. Give the mixture a good toss. Taste adding more lime and salt, as desired. Serve immediately.\n" - ], - "nutrients": [ - "caloriesKCal", - "totalCarbs", - "sugar", - "protein", - "fat", - "cholesterol", - "alcohol", - "gluten" - ], - "image": "https://tinyurl.com/2p82zzca/df9fd021-4abe-4c34-8f46-484e74e71ac5.png" - }, - { - "id": 14, - "name": "Low Carb Simple Ginger and Garlic Fried Cabbage", - "description": "This quick and easy cabbage is pan fried with tender red onion, aromatic garlic and fiery fresh ginger, drizzled with salty tamari sauce to serve.\n\nThis makes a great side to Asian inspired chicken, pork or fish.", - "prepareTime": 6, - "cookTime": 8, - "ingredients": [ - "Olive Oil 2 tablespoon", - "Tamari Sauce 1 tablespoon", - "Red Cabbage 1 cup", - "Cabbage, Green, Raw 1 cup", - "Ginger Root, Raw 1 slice - 1\" diameter", - "Garlic 1 clove", - "Red Onion ¼ medium - 2 1/2\" diameter" - ], - "steps": [ - "Thinly slice the onion and cabbage. Heat the oil in a skillet or wok over a medium/high heat and add the cabbage and onion, cooking for about three minutes to soften.", - "Grate the ginger and thinly slice the garlic. Add to the pan and stir well to combine. Pan fry over a medium heat for a further 2-3 minutes until the garlic is tender and fragrant and the cabbage is soft.", - "Stir through the tamari sauce to serve." - ], - "nutrients": [ - "caloriesKCal", - "totalCarbs", - "sugar", - "protein", - "fat", - "cholesterol", - "alcohol", - "gluten" - ], - "image": "https://tinyurl.com/2p82zzca/dce25a9d-8be4-4c4b-afaa-f994ef227f06.png" - } -] \ No newline at end of file diff --git a/server/migrations/20231114142906-create-recipe.js b/server/migrations/20231114142906-create-recipe.js deleted file mode 100644 index ee7a6dd..0000000 --- a/server/migrations/20231114142906-create-recipe.js +++ /dev/null @@ -1,49 +0,0 @@ -'use strict'; -/** @type {import('sequelize-cli').Migration} */ -module.exports = { - async up(queryInterface, Sequelize) { - await queryInterface.createTable('Recipes', { - id: { - allowNull: false, - autoIncrement: true, - primaryKey: true, - type: Sequelize.INTEGER - }, - name: { - type: Sequelize.STRING - }, - description: { - type: Sequelize.STRING(10000) - }, - prepareTime: { - type: Sequelize.INTEGER - }, - cookTime: { - type: Sequelize.INTEGER - }, - ingredients: { - type: Sequelize.STRING(10000) - }, - steps: { - type: Sequelize.STRING(10000) - }, - nutrients: { - type: Sequelize.STRING - }, - image: { - type: Sequelize.STRING(10000) - }, - createdAt: { - allowNull: false, - type: Sequelize.DATE - }, - updatedAt: { - allowNull: false, - type: Sequelize.DATE - } - }); - }, - async down(queryInterface, Sequelize) { - await queryInterface.dropTable('Recipes'); - } -}; \ No newline at end of file diff --git a/server/models/recipe.js b/server/models/recipe.js deleted file mode 100644 index 26d55a8..0000000 --- a/server/models/recipe.js +++ /dev/null @@ -1,28 +0,0 @@ -'use strict'; -const { Model } = require('sequelize'); -module.exports = (sequelize, DataTypes) => { - class Recipe extends Model { - /** - * Helper method for defining associations. - * This method is not a part of Sequelize lifecycle. - * The `models/index` file will call this method automatically. - */ - static associate(models) { - // define association here - } - } - Recipe.init({ - name: DataTypes.STRING, - description: DataTypes.STRING(10000), - prepareTime: DataTypes.INTEGER, - cookTime: DataTypes.INTEGER, - ingredients: DataTypes.STRING(10000), - steps: DataTypes.STRING(10000), - nutrients: DataTypes.STRING, - image: DataTypes.STRING(10000) - }, { - sequelize, - modelName: 'Recipe', - }); - return Recipe; -}; \ No newline at end of file diff --git a/server/routes/index.js b/server/routes/index.js index dbea220..7b19d0c 100644 --- a/server/routes/index.js +++ b/server/routes/index.js @@ -19,7 +19,7 @@ router.get("/recipes", RecipeController.recipes); router.get("/recipes/:id", RecipeController.recipeById); -router.post("/contact", MailController.sendMail); +router.post("/contact-mail", MailController.sendMail); diff --git a/server/seeders/20231114143111-seed-recipes.js b/server/seeders/20231114143111-seed-recipes.js deleted file mode 100644 index 7e0d97f..0000000 --- a/server/seeders/20231114143111-seed-recipes.js +++ /dev/null @@ -1,22 +0,0 @@ -'use strict'; - -/** @type {import('sequelize-cli').Migration} */ -module.exports = { - async up (queryInterface, Sequelize) { - const recipes = require("../data/recipe.json"); - recipes.forEach((el) => { - delete el.id; - el.createdAt = el.updatedAt = new Date(); - }) - - await queryInterface.bulkInsert("Recipes", recipes); - }, - - async down (queryInterface, Sequelize) { - await queryInterface.bulkDelete("Recipes", null, { - truncate: true, - cascade: true, - restartIdentity: true - }); - } -}; From d83bf1da4157f6d13cbfcb6e4ad55fdeb01e3235 Mon Sep 17 00:00:00 2001 From: pramesthias Date: Thu, 16 Nov 2023 12:49:24 +0700 Subject: [PATCH 15/28] test: add test for CUD comment entity --- README.md | 134 ++++- server/__tests__/comment-add.test.js | 87 ++++ server/__tests__/comment-delete.test.js | 142 ++++++ server/__tests__/comment-edit.test.js | 145 ++++++ .../{contact.test.js => contact-mail.test.js} | 0 server/__tests__/google-login.test.js | 109 +++++ server/__tests__/login.test.js | 109 +++++ server/__tests__/recipe-id.test.js | 188 +++++++ server/__tests__/register.test.js | 137 ++++++ server/config/config.json | 9 +- server/controllers/CommentController.js | 34 ++ server/controllers/RecipeController.js | 5 +- server/controllers/UserController.js | 13 +- server/coverage/clover.xml | 286 +++++++++++ server/coverage/coverage-final.json | 16 + server/coverage/lcov-report/base.css | 224 +++++++++ .../coverage/lcov-report/block-navigation.js | 87 ++++ server/coverage/lcov-report/favicon.png | Bin 0 -> 445 bytes server/coverage/lcov-report/index.html | 191 ++++++++ server/coverage/lcov-report/prettify.css | 1 + server/coverage/lcov-report/prettify.js | 2 + .../coverage/lcov-report/server/app.js.html | 136 ++++++ .../controllers/CommentController.js.html | 184 +++++++ .../server/controllers/MailController.js.html | 199 ++++++++ .../controllers/RecipeController.js.html | 436 +++++++++++++++++ .../server/controllers/UserController.js.html | 358 ++++++++++++++ .../lcov-report/server/controllers/index.html | 161 ++++++ .../server/helpers/bcryptjs.js.html | 121 +++++ .../lcov-report/server/helpers/index.html | 131 +++++ .../lcov-report/server/helpers/jwt.js.html | 124 +++++ server/coverage/lcov-report/server/index.html | 116 +++++ .../server/middlewares/authentication.js.html | 169 +++++++ .../server/middlewares/authorization.js.html | 145 ++++++ .../server/middlewares/errorHandler.js.html | 187 +++++++ .../lcov-report/server/middlewares/index.html | 146 ++++++ .../lcov-report/server/models/comment.js.html | 157 ++++++ .../lcov-report/server/models/index.html | 161 ++++++ .../lcov-report/server/models/index.js.html | 214 ++++++++ .../lcov-report/server/models/recipe.js.html | 172 +++++++ .../lcov-report/server/models/user.js.html | 277 +++++++++++ .../lcov-report/server/routes/index.html | 116 +++++ .../lcov-report/server/routes/index.js.html | 208 ++++++++ .../lcov-report/sort-arrow-sprite.png | Bin 0 -> 138 bytes server/coverage/lcov-report/sorter.js | 196 ++++++++ server/coverage/lcov.info | 462 ++++++++++++++++++ server/data/recipe.json | 291 +++++++++++ server/middlewares/authorization.js | 21 + server/middlewares/errorHandler.js | 10 +- .../20231116023751-create-recipe.js | 49 ++ .../20231116035529-create-comment.js | 37 ++ server/models/comment.js | 25 + server/models/recipe.js | 30 ++ server/models/user.js | 1 + server/routes/index.js | 15 +- server/seeders/20231116030505-recipe-seed.js | 27 + 55 files changed, 6985 insertions(+), 16 deletions(-) create mode 100644 server/__tests__/comment-add.test.js create mode 100644 server/__tests__/comment-delete.test.js create mode 100644 server/__tests__/comment-edit.test.js rename server/__tests__/{contact.test.js => contact-mail.test.js} (100%) create mode 100644 server/controllers/CommentController.js create mode 100644 server/coverage/clover.xml create mode 100644 server/coverage/coverage-final.json create mode 100644 server/coverage/lcov-report/base.css create mode 100644 server/coverage/lcov-report/block-navigation.js create mode 100644 server/coverage/lcov-report/favicon.png create mode 100644 server/coverage/lcov-report/index.html create mode 100644 server/coverage/lcov-report/prettify.css create mode 100644 server/coverage/lcov-report/prettify.js create mode 100644 server/coverage/lcov-report/server/app.js.html create mode 100644 server/coverage/lcov-report/server/controllers/CommentController.js.html create mode 100644 server/coverage/lcov-report/server/controllers/MailController.js.html create mode 100644 server/coverage/lcov-report/server/controllers/RecipeController.js.html create mode 100644 server/coverage/lcov-report/server/controllers/UserController.js.html create mode 100644 server/coverage/lcov-report/server/controllers/index.html create mode 100644 server/coverage/lcov-report/server/helpers/bcryptjs.js.html create mode 100644 server/coverage/lcov-report/server/helpers/index.html create mode 100644 server/coverage/lcov-report/server/helpers/jwt.js.html create mode 100644 server/coverage/lcov-report/server/index.html create mode 100644 server/coverage/lcov-report/server/middlewares/authentication.js.html create mode 100644 server/coverage/lcov-report/server/middlewares/authorization.js.html create mode 100644 server/coverage/lcov-report/server/middlewares/errorHandler.js.html create mode 100644 server/coverage/lcov-report/server/middlewares/index.html create mode 100644 server/coverage/lcov-report/server/models/comment.js.html create mode 100644 server/coverage/lcov-report/server/models/index.html create mode 100644 server/coverage/lcov-report/server/models/index.js.html create mode 100644 server/coverage/lcov-report/server/models/recipe.js.html create mode 100644 server/coverage/lcov-report/server/models/user.js.html create mode 100644 server/coverage/lcov-report/server/routes/index.html create mode 100644 server/coverage/lcov-report/server/routes/index.js.html create mode 100644 server/coverage/lcov-report/sort-arrow-sprite.png create mode 100644 server/coverage/lcov-report/sorter.js create mode 100644 server/coverage/lcov.info create mode 100644 server/data/recipe.json create mode 100644 server/middlewares/authorization.js create mode 100644 server/migrations/20231116023751-create-recipe.js create mode 100644 server/migrations/20231116035529-create-comment.js create mode 100644 server/models/comment.js create mode 100644 server/models/recipe.js create mode 100644 server/seeders/20231116030505-recipe-seed.js diff --git a/README.md b/README.md index c010188..ddcec95 100644 --- a/README.md +++ b/README.md @@ -221,7 +221,7 @@ _Response (200 - OK)_ ``` --- -### GET /recipes/:id +### GET /recipe/:id > Get One Recipe by Id @@ -296,6 +296,8 @@ _Response (404 - Not Found)_ ``` --- +## Mail Sending + ### POST /contact-mail > Send Message to the CYTO team @@ -338,6 +340,129 @@ _Response (400 - Bad Request)_ "message": "Please fill the required form!" } ``` +--- + +## Comment + +### POST/comment/add + +> Create new commebt + +_Request Header_ +``` +{"Authorization": "Bearer "} +``` + +_URL Params_ +``` +not needed +``` + +_Data Params_ +``` +not needed +``` + +_Request Body_ +``` +{ + "description": "string", + "userId": integer +} +``` + +_Response (201 - Created)_ +``` +{ + "id": integer, + "description": "string", + "userId": integer, + "createdAt": "date", + "updatedAt": "date" +} +``` +--- +### PUT /comment/edit/:id + +> Edit comment by id + +_Request Header_ +``` +{"Authorization": "Bearer "} +``` + +_URL Params_ +``` +id: integer [required] +``` + +_Data Params_ +``` +not needed +``` + +_Request Body_ +``` +{ + "description": "string", +} +``` + +_Response (201 - Created)_ +``` +{ + "id": integer, + "description": "string", + "userId": integer, + "createdAt": "date", + "updatedAt": "date" +} +``` + +_Response (404 - Not Found)_ +``` +{ + "message": "Comment not found" +} +``` +--- +### DELETE /comment/delete/:id + +> Delete comment by id + +_Request Header_ +``` +{"Authorization": "Bearer "} +``` + +_URL Params_ +``` +id: integer [required] +``` + +_Data Params_ +``` +not needed +``` + +_Request Body_ +``` +not needed +``` + +_Response (200 - OK)_ +``` +{ + "message": "Comment success to delete" +} +``` + +_Response (404 - Not Found)_ +``` +{ + "message": "Comment not found" +} +``` --- @@ -350,6 +475,13 @@ _Response (401 - Unauthorized)_ } ``` +_Response (403 - Forbidden)_ +``` +{ + "message": "You are not authorized" +} +``` + _Response (500 - Internal Server Error)_ ``` { diff --git a/server/__tests__/comment-add.test.js b/server/__tests__/comment-add.test.js new file mode 100644 index 0000000..73c50eb --- /dev/null +++ b/server/__tests__/comment-add.test.js @@ -0,0 +1,87 @@ +const app = require('../app'); +const request = require("supertest"); +const { sequelize, User, Category } = require("../models"); +const { signToken } = require('../helpers/jwt'); +const { queryInterface } = sequelize; + +let user1 = { + username: "Admin", + email: "admin@email.com", + password: "12345", +} + +let invalidToken = "eyJhbGciOiJIUzpXVC9.eyJpZCI6MSwiaWF0Ij1MDc0Q.A85Qn24V-jNwPqbAwPXFhcpVAClS0J78OS" +let tokenAdm; +let admin; +let comment; + +beforeAll(async () => { + admin = await User.create(user1); + tokenAdm = signToken({id: admin.id}); + + comment = { + "description": "Any", + "userId": admin.id, + } +}) + + +describe("/comment/add", () => { + + //Berhasil membuat entitas baru + test("success creating new comment", async () => { + let {status, body} = await request(app) + .post("/comment/add") + .set("Authorization", `Bearer ${tokenAdm}`) + .send(comment); + + expect(status).toBe(201); + expect(body).toBeInstanceOf(Object); + expect(body).toHaveProperty("id", expect.any(Number)); + expect(body).toHaveProperty("description", expect.any(String));; + expect(body).toHaveProperty("userId", expect.any(Number)); + }) + + //Gagal menjalankan fitur karena belum login + test("failed because not login yet", async () => { + let {status, body} = await request(app) + .post("/comment/add") + .send(comment); + + expect(status).toBe(401); + expect(body).toBeInstanceOf(Object); + expect(body).toHaveProperty("message", expect.any(String)); + expect(body.message).toContain("Unauthenticated"); + }) + + + // Gagal menjalankan fitur karena token yang diberikan tidak valid + test("failed because invalid token", async () => { + let {status, body} = await request(app) + .post("/comment/add") + .set("Authorization", `Bearer ${invalidToken}`) + .send(comment); + + expect(status).toBe(401); + expect(body).toBeInstanceOf(Object); + expect(body).toHaveProperty("message", expect.any(String)); + expect(body.message).toContain("invalid token"); + }) + +}) + + +afterAll(async () => { + await queryInterface.bulkDelete("Comments",null, { + truncate: true, + cascade: true, + restartIdentity: true + }) + + await queryInterface.bulkDelete("Users",null, { + truncate: true, + cascade: true, + restartIdentity: true + }) + +}) diff --git a/server/__tests__/comment-delete.test.js b/server/__tests__/comment-delete.test.js new file mode 100644 index 0000000..321474c --- /dev/null +++ b/server/__tests__/comment-delete.test.js @@ -0,0 +1,142 @@ +const app = require('../app'); +const request = require("supertest"); +const { sequelize, User } = require("../models"); +const { signToken } = require('../helpers/jwt'); +const { queryInterface } = sequelize; + +let user1 = { + username: "Admin", + email: "admin@email.com", + password: "12345", +} + +let user2 = { + username: "staff", + email: "staff@email.com", + password: "12345", +} + +let invalidToken = "eyJhbGciOiJIUzpXVC9.eyJpZCI6MSwiaWF0Ij1MDc0Q.A85Qn24V-jNwPqbAwPXFhcpVAClS0J78OS" +let tokenAdm; +let admin; +let tokenStf; +let staff; + +beforeAll(async () => { + admin = await User.create(user1); + staff = await User.create(user2); + tokenAdm = signToken({id: admin.id}); + tokenStf = signToken({id: staff.id}); + + const dateNow = new Date(); + + await queryInterface.bulkInsert('Comments', [ + { + "description": "Any", + "userId": admin.id, + "createdAt": dateNow, + "updatedAt": dateNow + }, + { + "description": "Any", + "userId": staff.id, + "createdAt": dateNow, + "updatedAt": dateNow + }, + { + "description": "Any", + "userId": admin.id, + "createdAt": dateNow, + "updatedAt": dateNow + } + ]) + +}) + +let commentId = 1; + +describe("/comment/delete/:id", () => { + + //Berhasil menghapus data Entitas berdasarkan params id yang diberikan + test("success delete one comment by id (200)", async () => { + let {status, body} = await request(app) + .delete(`/comment/delete/${commentId}`) + .set("Authorization", `Bearer ${tokenAdm}`) + + expect(status).toBe(200); + expect(body).toBeInstanceOf(Object); + expect(body).toHaveProperty("message", expect.any(String)); + expect(body.message).toContain("Comment success to delete"); + }) + + + // Gagal menjalankan fitur karena belum login + test("failed delete comment because not login yet (401)", async () => { + let {status, body} = await request(app) + .delete(`/comment/delete/${commentId}`) + + expect(status).toBe(401); + expect(body).toBeInstanceOf(Object); + expect(body).toHaveProperty("message", expect.any(String)); + expect(body.message).toContain("Unauthenticated"); + }) + + + // Gagal menjalankan fitur karena token yang diberikan tidak valid + test("failed delete comment with invalid token (401)", async () => { + let {status, body} = await request(app) + .delete(`/comment/delete/${commentId}`) + .set("Authorization", `Bearer ${invalidToken}`) + + expect(status).toBe(401); + expect(body).toBeInstanceOf(Object); + expect(body).toHaveProperty("message", expect.any(String)); + expect(body.message).toContain("invalid token"); + }) + + + // Gagal karena id entity yang dikirim tidak terdapat di database + test("failed delete comment with invalid id (404)", async () => { + const invalidId = 7; + let {status, body} = await request(app) + .delete(`/comment/delete/${invalidId}`) + .set("Authorization", `Bearer ${tokenStf}`) + + expect(status).toBe(404); + expect(body).toBeInstanceOf(Object); + expect(body).toHaveProperty("message", expect.any(String)); + expect(body.message).toContain("Comment not found"); + }) + + + // Gagal menjalankan fitur ketika menghapus entity yang bukan miliknya + test("failed to delete someone's else comment (403)", async () => { + commentId = 3; + let {status, body} = await request(app) + .delete(`/comment/delete/${commentId}`) + .set("Authorization", `Bearer ${tokenStf}`) + + expect(status).toBe(403); + expect(body).toBeInstanceOf(Object); + expect(body).toHaveProperty("message", expect.any(String)); + expect(body.message).toContain("You are not authorized"); + }) + +}) + + +afterAll(async () => { + + await queryInterface.bulkDelete("Comments",null, { + truncate: true, + cascade: true, + restartIdentity: true + }) + + await queryInterface.bulkDelete("Users",null, { + truncate: true, + cascade: true, + restartIdentity: true + }) + +}) \ No newline at end of file diff --git a/server/__tests__/comment-edit.test.js b/server/__tests__/comment-edit.test.js new file mode 100644 index 0000000..35a1dbd --- /dev/null +++ b/server/__tests__/comment-edit.test.js @@ -0,0 +1,145 @@ +const app = require('../app'); +const request = require("supertest"); +const { sequelize, User } = require("../models"); +const { signToken } = require('../helpers/jwt'); +const { queryInterface } = sequelize; + +let user1 = { + username: "Admin", + email: "admin@email.com", + password: "12345", +} + +let user2 = { + username: "staff", + email: "staff@email.com", + password: "12345", +} + +let invalidToken = "eyJhbGciOiJIUzpXVC9.eyJpZCI6MSwiaWF0Ij1MDc0Q.A85Qn24V-jNwPqbAwPXFhcpVAClS0J78OS" +let tokenAdm; +let admin; +let tokenStf; +let staff; + +beforeAll(async () => { + admin = await User.create(user1); + staff = await User.create(user2); + tokenAdm = signToken({id: admin.id}); + tokenStf = signToken({id: staff.id}); + + const dateNow = new Date(); + + await queryInterface.bulkInsert('Comments', [ + { + "description": "Any", + "userId": admin.id, + "createdAt": dateNow, + "updatedAt": dateNow + }, + { + "description": "Any", + "userId": staff.id, + "createdAt": dateNow, + "updatedAt": dateNow + } + ]) + +}) + +let commentId = 1; +let comment = { + "description": "Any", + "userId": 1 +} + +describe("/comment/edit/:id", () => { + + //Berhasil mengupdate data Entitas berdasarkan params id yang diberikan + test("success update cuisine by id (200)", async () => { + let {status, body} = await request(app) + .put(`/comment/edit/${commentId}`) + .set("Authorization", `Bearer ${tokenAdm}`) + .send(comment); + + expect(status).toBe(200); + expect(body).toBeInstanceOf(Object); + expect(body).toHaveProperty("id", expect.any(Number)); + expect(body.id).toBe(commentId); + expect(body).toHaveProperty("description", expect.any(String)); + expect(body).toHaveProperty("userId", expect.any(Number)); + expect(body.userId).toBe(admin.id); + }) + + + // Gagal menjalankan fitur karena belum login + test("failed update cuisine because not login yet (401)", async () => { + let {status, body} = await request(app) + .put(`/comment/edit/${commentId}`) + .send(comment); + + expect(status).toBe(401); + expect(body).toBeInstanceOf(Object); + expect(body).toHaveProperty("message", expect.any(String)); + expect(body.message).toContain("Unauthenticated"); + }) + + + // Gagal menjalankan fitur karena token yang diberikan tidak valid + test("failed update cuisine with invalid token (401)", async () => { + let {status, body} = await request(app) + .put(`/comment/edit/${commentId}`) + .set("Authorization", `Bearer ${invalidToken}`) + .send(comment); + + expect(status).toBe(401); + expect(body).toBeInstanceOf(Object); + expect(body).toHaveProperty("message", expect.any(String)); + expect(body.message).toContain("invalid token"); + }) + + + // Gagal karena id entity yang dikirim tidak terdapat di database + test("failed update cuisine with invalid id (404)", async () => { + const invalidId = 7; + let {status, body} = await request(app) + .put(`/comment/edit/${invalidId}`) + .set("Authorization", `Bearer ${tokenAdm}`) + .send(comment); + + expect(status).toBe(404); + expect(body).toBeInstanceOf(Object); + expect(body).toHaveProperty("message", expect.any(String)); + expect(body.message).toContain("Comment not found"); + }) + + + // Gagal menjalankan fitur ketika mengolah data entity yang bukan miliknya + test("failed to updatesomeone's else comment (403)", async () => { + let {status, body} = await request(app) + .put(`/comment/edit/${commentId}`) + .set("Authorization", `Bearer ${tokenStf}`) + .send(comment); + + expect(status).toBe(403); + expect(body).toBeInstanceOf(Object); + expect(body).toHaveProperty("message", expect.any(String)); + expect(body.message).toContain("You are not authorized"); + }) + +}) + + +afterAll(async () => { + await queryInterface.bulkDelete("Comments",null, { + truncate: true, + cascade: true, + restartIdentity: true + }) + + await queryInterface.bulkDelete("Users",null, { + truncate: true, + cascade: true, + restartIdentity: true + }) +}) \ No newline at end of file diff --git a/server/__tests__/contact.test.js b/server/__tests__/contact-mail.test.js similarity index 100% rename from server/__tests__/contact.test.js rename to server/__tests__/contact-mail.test.js diff --git a/server/__tests__/google-login.test.js b/server/__tests__/google-login.test.js index e69de29..1a87ed8 100644 --- a/server/__tests__/google-login.test.js +++ b/server/__tests__/google-login.test.js @@ -0,0 +1,109 @@ +const app = require('../app'); +const request = require("supertest"); +const { sequelize, User } = require("../models"); +const { queryInterface } = sequelize; + +const user1 = { + email: "admin@email.com", + password: "12345" +} + +const user2 = { + email: "staff@email.com", + password: "12345" +} + + +beforeAll(async () => { + await User.create(user1); +}) + +describe("/google-login", () => { + + //Berhasil login dan diberikan access token + test("success login and get access token", async () => { + let {status, body} = await request(app) + .post("/google-login") + .send(user1); + + console.log(status, body); + expect(status).toBe(200); + expect(body).toBeInstanceOf(Object); + expect(body).toHaveProperty("access_token", expect.any(String)); + }) + + + //Email tidak diberikan / tidak diinput + test("failed login without email", async () => { + let {status, body} = await request(app) + .post("/google-login") + .send({ + email: "", + password: "12345678" + }); + + console.log(status, body); + expect(status).toBe(400); + expect(body).toBeInstanceOf(Object); + expect(body).toHaveProperty("message", expect.any(String)); + expect(body.message).toContain("Error invalid email or Password"); + }) + + + //Password tidak diberikan / tidak diinput + test("failed login without password", async () => { + let {status, body} = await request(app) + .post("/google-login") + .send({ + email: "admin@email.com", + password: "" + }); + + console.log(status, body); + expect(status).toBe(400); + expect(body).toBeInstanceOf(Object); + expect(body).toHaveProperty("message", expect.any(String)); + expect(body.message).toContain("Error invalid email or Password"); + }) + + + // Email diberikan invalid / tidak terdaftar + test("failed login with invalid email", async () => { + let {status, body} = await request(app) + .post("/google-login") + .send(user2); + + console.log(status, body); + expect(status).toBe(401); + expect(body).toBeInstanceOf(Object); + expect(body).toHaveProperty("message", expect.any(String)); + expect(body.message).toContain("User not found or Password not matched"); + }) + + + // Password diberikan salah / tidak match + test("failed login with invalid password", async () => { + let {status, body} = await request(app) + .post("/google-login") + .send({ + email: "admin@email.com", + password: "09876" + }); + + console.log(status, body); + expect(status).toBe(401); + expect(body).toBeInstanceOf(Object); + expect(body).toHaveProperty("message", expect.any(String)); + expect(body.message).toContain("User not found or Password not matched"); + }) + +}) + + +afterAll(async () => { + await queryInterface.bulkDelete("Users",null, { + truncate: true, + cascade: true, + restartIdentity: true + }) +}) \ No newline at end of file diff --git a/server/__tests__/login.test.js b/server/__tests__/login.test.js index e69de29..cf7db64 100644 --- a/server/__tests__/login.test.js +++ b/server/__tests__/login.test.js @@ -0,0 +1,109 @@ +const app = require('../app'); +const request = require("supertest"); +const { sequelize, User } = require("../models"); +const { queryInterface } = sequelize; + +const user1 = { + email: "admin@email.com", + password: "12345" +} + +const user2 = { + email: "staff@email.com", + password: "12345" +} + + +beforeAll(async () => { + await User.create(user1); +}) + +describe("/login", () => { + + //Berhasil login dan diberikan access token + test("success login and get access token", async () => { + let {status, body} = await request(app) + .post("/login") + .send(user1); + + console.log(status, body); + expect(status).toBe(200); + expect(body).toBeInstanceOf(Object); + expect(body).toHaveProperty("access_token", expect.any(String)); + }) + + + //Email tidak diberikan / tidak diinput + test("failed login without email", async () => { + let {status, body} = await request(app) + .post("/login") + .send({ + email: "", + password: "12345678" + }); + + console.log(status, body); + expect(status).toBe(400); + expect(body).toBeInstanceOf(Object); + expect(body).toHaveProperty("message", expect.any(String)); + expect(body.message).toContain("Error invalid email or Password"); + }) + + + //Password tidak diberikan / tidak diinput + test("failed login without password", async () => { + let {status, body} = await request(app) + .post("/login") + .send({ + email: "admin@email.com", + password: "" + }); + + console.log(status, body); + expect(status).toBe(400); + expect(body).toBeInstanceOf(Object); + expect(body).toHaveProperty("message", expect.any(String)); + expect(body.message).toContain("Error invalid email or Password"); + }) + + + // Email diberikan invalid / tidak terdaftar + test("failed login with invalid email", async () => { + let {status, body} = await request(app) + .post("/login") + .send(user2); + + console.log(status, body); + expect(status).toBe(401); + expect(body).toBeInstanceOf(Object); + expect(body).toHaveProperty("message", expect.any(String)); + expect(body.message).toContain("User not found or Password not matched"); + }) + + + // Password diberikan salah / tidak match + test("failed login with invalid password", async () => { + let {status, body} = await request(app) + .post("/login") + .send({ + email: "admin@email.com", + password: "09876" + }); + + console.log(status, body); + expect(status).toBe(401); + expect(body).toBeInstanceOf(Object); + expect(body).toHaveProperty("message", expect.any(String)); + expect(body.message).toContain("User not found or Password not matched"); + }) + +}) + + +afterAll(async () => { + await queryInterface.bulkDelete("Users",null, { + truncate: true, + cascade: true, + restartIdentity: true + }) +}) \ No newline at end of file diff --git a/server/__tests__/recipe-id.test.js b/server/__tests__/recipe-id.test.js index e69de29..ac0cd86 100644 --- a/server/__tests__/recipe-id.test.js +++ b/server/__tests__/recipe-id.test.js @@ -0,0 +1,188 @@ +const app = require('../app'); +const request = require("supertest"); +const { sequelize, User } = require("../models"); +const { signToken } = require('../helpers/jwt'); +const { queryInterface } = sequelize; + +let user = { + username: "string", + email: "admin@email.com", + password: "12345" +} + +let invalidToken = "eyJhbGciOiJIUzI1NiInR5cCI6IkpXVC9.eyJpZCI6MSwiaWF0IjoxNjk4ODMDc0Q.A8n24V-jNwPqbc1VuFAuvgwPXFhcpVAClS0J78OS" +let tokenAdm; +let admin; + +beforeAll(async () => { + admin = await User.create(user); + tokenAdm = signToken({ id: admin.id }); + + const dateNow = new Date(); + + await queryInterface.bulkInsert('Recipes', [ + { + "number": 1, + "id": "133fcdec-4595-44be-a302-02df4c209451", + "name": "Keto Sesame Tuna and Egg Salad", + "description": "This quick and easy tuna and egg salad is combined with aromatic scallions and parsley and coated in a sesame, lemon mayo.\n\nThis is a great option for stuffing lettuce wraps or topping low carb crackers.", + "prepareTime": "8 minutes", + "cookTime": "0 minutes", + "ingredients": [ + "Hard Boiled Egg 2 medium", + "Scallions 2 medium - 4 1/8\" long", + "Canned Tuna 1-¾ ounce", + "Lemon Juice, Fresh 1 teaspoon", + "Parsley, Fresh 1 tablespoon", + "Sesame Oil 1 teaspoon", + "Mayonnaise 1 tablespoon", + "Sesame Seeds, Hulled, Toasted, Unsalted 1 teaspoon, whole pieces", + "Salt, Sea Salt ⅛ teaspoon", + "Black Pepper ⅛ teaspoon" + ], + "steps": [ + "Peel and roughly chop the eggs and add to a mixing bowl.", + "Thinly slice the scallions and roughly chop the parsley. Add to the mixing bowl along with the tuna and stir well to combine.", + "Add the mayonnaise to a mixing bowl with the lemon juice, sesame oil, salt and pepper. Whisk together until smooth.", + "Spoon the dressing over the salad and stir well to coat.", + "Sprinkle over the sesame seeds to serve." + ], + "nutrients": { + "caloriesKCal": 170.313, + "totalCarbs": 2.366, + "sugar": 1.166, + "protein": 11.004, + "fat": 13.018, + "cholesterol": 175.938, + "alcohol": 0, + "gluten": 0 + }, + "image": "https://tinyurl.com/2p82zzca/133fcdec-4595-44be-a302-02df4c209451.png", + "createdAt": dateNow, + "updatedAt": dateNow, + }, + { + "number": 2, + "id": "ccef957b-351f-4c14-b33c-ef050045b877", + "name": "Keto Vanilla Butter Latte", + "description": "This creamy Keto vanilla BULLETPROOF® coffee recipe is rich in fats, frothy plant-based milk, and a hint of sweet vanilla. This Keto vanilla butter coffee makes a great fat-fueled breakfast option to kickstart your day. Alternatively, this is perfect served with a slice of low-carb cake as an afternoon treat!\n\n### What ingredients are in this Keto vanilla butter latte?\n\nThis Keto vanilla BULLETPROOF® coffee is prepared with hot almond milk infused with sweet vanilla extract. The milk is then blended with instant coffee and a hearty helping of unsalted butter, before being blitzed until super frothy! We have used a handheld stick blender to froth and blend our Keto coffee, however, you may use a freestanding blender for this if preferred.\n\n### Here are some tips to make Keto vanilla butter coffee?\n\nThis Keto BULLETPROOF® coffee provides a generous serving of fats from unsalted butter. If preferred you may swap the butter for coconut oil or BULLETPROOF® brain octane oil (MCT Oil) or even a blend of fats. You may also swap the erythritol for your preference in low-carb sweetener or omit it entirely if desired. Please be sure to adjust your macros for any changes made.\n\n### Can you use freshly brewed coffee in this Keto recipe?\n\nWhile this recipe calls for instant coffee, you can definitely substitute it for espresso or freshly brewed coffee, just make sure its strong.\n\nBULLETPROOF® is a registered trademark owned by Bulletproof Digital, Inc.", + "prepareTime": "8 minutes", + "cookTime": "3 minutes", + "ingredients": [ + "Butter, Unsalted 1-½ tablespoon", + "Almond Milk, Vanilla Or Other Flavors, Unsweetened 1-½ cup", + "Instant Coffee 1 teaspoon", + "Vanilla Extract ¼ teaspoon", + "100% Pure Erythritol by Now 1 tsp" + ], + "steps": [ + "Add the almond milk and vanilla extract to a small saucepan over low/medium heat. Stir to combine. Heat the milk through until piping hot but not boiling.", + "Add the instant coffee to a large mug. Add the granulated sweetener. Add the hot milk and stir well to combine, dissolving the coffee and sweetener.", + "Add the butter to the mug. Stir well to combine. Let the butter completely melt into the milky coffee.", + "Carefully use a hand held stick blender or milk frother and blend the coffee and butter. Continue blending until you have a thick frothy layer on the top of your coffee. Serve hot." + ], + "nutrients": { + "caloriesKCal": 203.555, + "totalCarbs": 5.903, + "sugar": 0.572, + "protein": 1.779, + "fat": 21.015, + "cholesterol": 45.755, + "alcohol": 0.631, + "gluten": 0 + }, + "image": "https://tinyurl.com/2p82zzca/ccef957b-351f-4c14-b33c-ef050045b877.png", + "createdAt": dateNow, + "updatedAt": dateNow, + }]) + +}) + + +const validId = "7631d8cb-2973-4f7c-833b-daeb4190c278"; + +describe("/recipe/:id", () => { + + //Berhasil mendapatkan 1 Entitas Utama sesuai dengan params id yang diberikan + test("success get one recipe by id (200)", async () => { + let { status, body } = await request(app) + .get(`/recipe/${validId}`) + .set("Authorization", `Bearer ${tokenAdm}`) + + expect(status).toBe(200); + expect(body).toBeInstanceOf(Object); + expect(body).toHaveProperty("id"); + expect(body.id).toBe(validId); + expect(body).toHaveProperty("name", expect.any(String)); + expect(body).toHaveProperty("description", expect.any(String)); + expect(body).toHaveProperty("prepareTime", expect.any(String)); + expect(body).toHaveProperty("cookTime", expect.any(String)); + + expect(body).toHaveProperty("ingredients"); + expect(body.ingredients).toBeInstanceOf(Array); + expect(body[0]).toBeInstanceOf(String); + + expect(body).toHaveProperty("steps"); + expect(body.steps).toBeInstanceOf(Array); + expect(body[0]).toBeInstanceOf(String); + + expect(body).toHaveProperty("nutrients"); + expect(body.nutrients).toBeInstanceOf(Object); + + expect(body).toHaveProperty("image", expect.any(String)); + }) + + + // Gagal menjalankan fitur karena belum login + test("failed get one recipe because not login yet (401)", async () => { + let { status, body } = await request(app) + .get(`/recipe/${validId}`) + + expect(status).toBe(401); + expect(body).toBeInstanceOf(Object); + expect(body).toHaveProperty("message", expect.any(String)); + expect(body.message).toContain("Unauthenticated"); + }) + + + // Gagal menjalankan fitur karena token yang diberikan tidak valid + test("failed get one recipe with invalid token (401)", async () => { + let { status, body } = await request(app) + .get(`/recipe/${validId}`) + .set("Authorization", `Bearer ${invalidToken}`) + + expect(status).toBe(401); + expect(body).toBeInstanceOf(Object); + expect(body).toHaveProperty("message", expect.any(String)); + expect(body.message).toContain("invalid token"); + }) + + // Gagal mendapatkan Entitas Utama karena params id yang diberikan invalid + test("failed get recipe with invalid id (404)", async () => { + const invalidId = "7631d8cb-2973"; + let { status, body } = await request(app) + .get(`/recipe/${invalidId}`) + .set("Authorization", `Bearer ${tokenAdm}`) + + expect(status).toBe(404); + expect(body).toBeInstanceOf(Object); + expect(body).toHaveProperty("message", expect.any(String)); + expect(body.message).toContain("Recipe not found"); + }) +}) + + +afterAll(async () => { + await queryInterface.bulkDelete("Users", null, { + truncate: true, + cascade: true, + restartIdentity: true + }) + + await queryInterface.bulkDelete("Recipes", null, { + truncate: true, + cascade: true, + restartIdentity: true + }) + +}) \ No newline at end of file diff --git a/server/__tests__/register.test.js b/server/__tests__/register.test.js index e69de29..0740511 100644 --- a/server/__tests__/register.test.js +++ b/server/__tests__/register.test.js @@ -0,0 +1,137 @@ +const app = require('../app'); +const request = require("supertest"); +const { sequelize, User } = require("../models"); +const { queryInterface } = sequelize; + +let seed_user1 = { + username: "Rara", + email: "rara@email.com", + password: "12345" +} + +const user2 = { + email: "staff@mail.com", + password: "12345" +} + +beforeAll(async () => { + await User.create(seed_user1); +}) + +describe("/register", () => { + + // Berhasil register + test("success register new user", async () => { + let {status, body} = await request(app) + .post("/register") + .send(user2); + + expect(status).toBe(201); + expect(body).toBeInstanceOf(Object); + expect(body).toHaveProperty("id", expect.any(Number)); + expect(body).toHaveProperty("username", expect.any(String)); + }) + + + //Email tidak diberikan / tidak diinput + test("failed register when email is null", async () => { + let {status, body} = await request(app) + .post("/register") + .send({ + password: "12345" + }); + + expect(status).toBe(400); + expect(body).toBeInstanceOf(Object); + expect(body).toHaveProperty("message", expect.any(String)); + expect(body.message).toContain("Email cannot be Empty!"); + }) + + + //Password tidak diberikan / tidak diinput + test("failed register when password is null", async () => { + let {status, body} = await request(app) + .post("/register") + .send({ + email: "staff@mail.com", + }); + + expect(status).toBe(400); + expect(body).toBeInstanceOf(Object); + expect(body).toHaveProperty("message", expect.any(String)); + expect(body.message).toContain("Password cannot be Empty!"); + }) + + + //Email diberikan string kosong + test("failed register when password is empty", async () => { + let {status, body} = await request(app) + .post("/register") + .send({ + email: "", + password: "12345" + }); + + expect(status).toBe(400); + expect(body).toBeInstanceOf(Object); + expect(body).toHaveProperty("message", expect.any(String)); + expect(body.message).toContain("Email cannot be Empty!"); + }) + + + //Password diberikan string kosong + test("failed register when password is empty", async () => { + let {status, body} = await request(app) + .post("/register") + .send({ + email: "staff@mail.com", + password: "" + }); + + expect(status).toBe(400); + expect(body).toBeInstanceOf(Object); + expect(body).toHaveProperty("message", expect.any(String)); + expect(body.message).toContain("Password cannot be Empty!"); + }) + + + //Email sudah terdaftar + test("failed register with duplicate email (400)", async () => { + let {status, body} = await request(app) + .post("/register") + .send({ + email: seed_user1.email, + password: seed_user1.password + }); + + expect(status).toBe(400); + expect(body).toBeInstanceOf(Object); + expect(body).toHaveProperty("message", expect.any(String)); + expect(body.message).toContain("email must be unique"); + }) + + + //Format Email salah / invalid + test("failed register with invalid email format", async () => { + let {status, body} = await request(app) + .post("/register") + .send({ + email: "staffmailcom", + password: "12345" + }); + + expect(status).toBe(400); + expect(body).toBeInstanceOf(Object); + expect(body).toHaveProperty("message", expect.any(String)); + expect(body.message).toContain("Email should be written in email format!"); + }) + +}) + +afterAll(async () => { + await queryInterface.bulkDelete("Users",null, { + truncate: true, + cascade: true, + restartIdentity: true + }) +}) \ No newline at end of file diff --git a/server/config/config.json b/server/config/config.json index ef1749e..19564e6 100644 --- a/server/config/config.json +++ b/server/config/config.json @@ -7,11 +7,12 @@ "dialect": "postgres" }, "test": { - "username": "root", - "password": null, - "database": "database_test", + "username": "postgres", + "password": "postgres", + "database": "cyto_test", "host": "127.0.0.1", - "dialect": "mysql" + "dialect": "postgres", + "logging": false }, "production": { "username": "root", diff --git a/server/controllers/CommentController.js b/server/controllers/CommentController.js new file mode 100644 index 0000000..d84d8dd --- /dev/null +++ b/server/controllers/CommentController.js @@ -0,0 +1,34 @@ +const { Comment } = require("../models"); + +module.exports = class MailController { + + static async addComment(req, res, next) { + try { + const comment = await Comment.create({ ...req.body, userId: req.user.id }) + res.status(201).json(comment); + } catch (error) { + next(error); + } + } + + static async editComment(req, res, next) { + try { + let comment = await Comment.findByPk(req.params.id); + let update = await comment.update(req.body, { returning: true }); + res.status(200).json(update); + } catch (error) { + next(error); + } + } + + static async deleteComment(req, res, next) { + try { + let comment = await Comment.findByPk(req.params.id); + await comment.destroy(); + res.status(200).json({ message: `Comment success to delete` }); + } catch (error) { + next(error); + } + } + +} \ No newline at end of file diff --git a/server/controllers/RecipeController.js b/server/controllers/RecipeController.js index 7f02dee..54feac4 100644 --- a/server/controllers/RecipeController.js +++ b/server/controllers/RecipeController.js @@ -39,7 +39,6 @@ module.exports = class RecipeController { const recipes = response.data; const mappedRecipes = recipes.map((recipe, index) => { return { - number: ++index, id: recipe.id, name: recipe.name, description: recipe.description, @@ -93,8 +92,8 @@ module.exports = class RecipeController { id: recipe.id, name: recipe.name, description: recipe.description, - prepareTime: recipe.prepareTime, - cookTime: recipe.cookTime, + prepareTime: `${recipe.prepareTime} minutes`, + cookTime: `${recipe.cookTime} minutes`, ingredients: recipe.ingredients.map(ingredient => `${ingredient.name} ${ingredient.servingSize.desc}`), steps: recipe.steps, nutrients: { diff --git a/server/controllers/UserController.js b/server/controllers/UserController.js index a6239fb..f393f72 100644 --- a/server/controllers/UserController.js +++ b/server/controllers/UserController.js @@ -28,12 +28,12 @@ module.exports = class UserController { const { email, password } = req.body; if (!email) { - res.status(400).json({ message: "Error invalid email or password" }); + res.status(400).json({ message: "Error invalid email or Password" }); return; } if (!password) { - res.status(400).json({ message: "Error invalid email or password" }); + res.status(400).json({ message: "Error invalid email or Password" }); return; } @@ -53,6 +53,7 @@ module.exports = class UserController { res.status(200).json({ access_token }); } catch (error) { + console.log(error) next(error); } } @@ -60,22 +61,22 @@ module.exports = class UserController { static async googleLogin(req, res, next) { console.log(req.headers.g_token) - + try { const ticket = await client.verifyIdToken({ idToken: req.headers.g_token, - audience: process.env.G_CLIENT_ID, + audience: process.env.G_CLIENT_ID, }); const payload = ticket.getPayload(); - console.log(payload) + console.log(payload) const [user, isNewRecord] = await User.findOrCreate({ where: { email: payload.email }, defaults: { - username: payload.name, + username: payload.name, password: String(Math.random()) } }) diff --git a/server/coverage/clover.xml b/server/coverage/clover.xml new file mode 100644 index 0000000..5494c94 --- /dev/null +++ b/server/coverage/clover.xml @@ -0,0 +1,286 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/server/coverage/coverage-final.json b/server/coverage/coverage-final.json new file mode 100644 index 0000000..a2accea --- /dev/null +++ b/server/coverage/coverage-final.json @@ -0,0 +1,16 @@ +{"D:\\IP-RMT42\\server\\app.js": {"path":"D:\\IP-RMT42\\server\\app.js","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":3,"column":3}},"1":{"start":{"line":2,"column":4},"end":{"line":2,"column":30}},"2":{"start":{"line":5,"column":16},"end":{"line":5,"column":34}},"3":{"start":{"line":6,"column":12},"end":{"line":6,"column":21}},"4":{"start":{"line":7,"column":13},"end":{"line":7,"column":28}},"5":{"start":{"line":9,"column":0},"end":{"line":9,"column":15}},"6":{"start":{"line":11,"column":0},"end":{"line":11,"column":48}},"7":{"start":{"line":13,"column":0},"end":{"line":13,"column":24}},"8":{"start":{"line":15,"column":0},"end":{"line":15,"column":29}},"9":{"start":{"line":18,"column":0},"end":{"line":18,"column":21}}},"fnMap":{},"branchMap":{"0":{"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":3}},"type":"if","locations":[{"start":{"line":1,"column":0},"end":{"line":3,"column":3}},{"start":{},"end":{}}],"line":1}},"s":{"0":1,"1":1,"2":1,"3":1,"4":1,"5":1,"6":1,"7":1,"8":1,"9":1},"f":{},"b":{"0":[1,0]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"3c4696dddacd4890423030158e274d3635612ff9"} +,"D:\\IP-RMT42\\server\\controllers\\CommentController.js": {"path":"D:\\IP-RMT42\\server\\controllers\\CommentController.js","statementMap":{"0":{"start":{"line":1,"column":20},"end":{"line":1,"column":40}},"1":{"start":{"line":3,"column":0},"end":{"line":34,"column":1}},"2":{"start":{"line":6,"column":8},"end":{"line":11,"column":9}},"3":{"start":{"line":7,"column":28},"end":{"line":7,"column":86}},"4":{"start":{"line":8,"column":12},"end":{"line":8,"column":42}},"5":{"start":{"line":10,"column":12},"end":{"line":10,"column":24}},"6":{"start":{"line":15,"column":8},"end":{"line":21,"column":9}},"7":{"start":{"line":16,"column":26},"end":{"line":16,"column":63}},"8":{"start":{"line":17,"column":25},"end":{"line":17,"column":76}},"9":{"start":{"line":18,"column":12},"end":{"line":18,"column":41}},"10":{"start":{"line":20,"column":12},"end":{"line":20,"column":24}},"11":{"start":{"line":25,"column":8},"end":{"line":31,"column":9}},"12":{"start":{"line":26,"column":26},"end":{"line":26,"column":63}},"13":{"start":{"line":27,"column":12},"end":{"line":27,"column":36}},"14":{"start":{"line":28,"column":12},"end":{"line":28,"column":75}},"15":{"start":{"line":30,"column":12},"end":{"line":30,"column":24}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":5,"column":4},"end":{"line":5,"column":5}},"loc":{"start":{"line":5,"column":44},"end":{"line":12,"column":5}},"line":5},"1":{"name":"(anonymous_1)","decl":{"start":{"line":14,"column":4},"end":{"line":14,"column":5}},"loc":{"start":{"line":14,"column":45},"end":{"line":22,"column":5}},"line":14},"2":{"name":"(anonymous_2)","decl":{"start":{"line":24,"column":4},"end":{"line":24,"column":5}},"loc":{"start":{"line":24,"column":47},"end":{"line":32,"column":5}},"line":24}},"branchMap":{},"s":{"0":1,"1":1,"2":1,"3":1,"4":1,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0},"f":{"0":1,"1":0,"2":0},"b":{},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"a8b718399e09e4b5cdce82973780d5f2e053a75a"} +,"D:\\IP-RMT42\\server\\controllers\\MailController.js": {"path":"D:\\IP-RMT42\\server\\controllers\\MailController.js","statementMap":{"0":{"start":{"line":1,"column":19},"end":{"line":1,"column":40}},"1":{"start":{"line":3,"column":0},"end":{"line":39,"column":1}},"2":{"start":{"line":6,"column":8},"end":{"line":35,"column":9}},"3":{"start":{"line":7,"column":49},"end":{"line":7,"column":57}},"4":{"start":{"line":11,"column":12},"end":{"line":11,"column":123}},"5":{"start":{"line":11,"column":48},"end":{"line":11,"column":123}},"6":{"start":{"line":13,"column":32},"end":{"line":19,"column":14}},"7":{"start":{"line":21,"column":32},"end":{"line":26,"column":13}},"8":{"start":{"line":28,"column":12},"end":{"line":28,"column":52}},"9":{"start":{"line":30,"column":12},"end":{"line":30,"column":80}},"10":{"start":{"line":33,"column":12},"end":{"line":33,"column":24}},"11":{"start":{"line":34,"column":12},"end":{"line":34,"column":30}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":5,"column":4},"end":{"line":5,"column":5}},"loc":{"start":{"line":5,"column":42},"end":{"line":36,"column":5}},"line":5}},"branchMap":{"0":{"loc":{"start":{"line":11,"column":12},"end":{"line":11,"column":123}},"type":"if","locations":[{"start":{"line":11,"column":12},"end":{"line":11,"column":123}},{"start":{},"end":{}}],"line":11},"1":{"loc":{"start":{"line":11,"column":15},"end":{"line":11,"column":46}},"type":"binary-expr","locations":[{"start":{"line":11,"column":15},"end":{"line":11,"column":24}},{"start":{"line":11,"column":28},"end":{"line":11,"column":34}},{"start":{"line":11,"column":38},"end":{"line":11,"column":46}}],"line":11}},"s":{"0":1,"1":1,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0},"f":{"0":0},"b":{"0":[0,0],"1":[0,0,0]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"ebab0cfc4a56fd1dce951694db907c08d7e4a773"} +,"D:\\IP-RMT42\\server\\controllers\\RecipeController.js": {"path":"D:\\IP-RMT42\\server\\controllers\\RecipeController.js","statementMap":{"0":{"start":{"line":1,"column":14},"end":{"line":1,"column":30}},"1":{"start":{"line":2,"column":19},"end":{"line":2,"column":39}},"2":{"start":{"line":4,"column":0},"end":{"line":118,"column":1}},"3":{"start":{"line":8,"column":37},"end":{"line":8,"column":46}},"4":{"start":{"line":10,"column":20},"end":{"line":26,"column":5}},"5":{"start":{"line":28,"column":4},"end":{"line":30,"column":5}},"6":{"start":{"line":29,"column":6},"end":{"line":29,"column":35}},"7":{"start":{"line":32,"column":4},"end":{"line":35,"column":5}},"8":{"start":{"line":33,"column":6},"end":{"line":33,"column":70}},"9":{"start":{"line":34,"column":6},"end":{"line":34,"column":52}},"10":{"start":{"line":37,"column":4},"end":{"line":70,"column":5}},"11":{"start":{"line":38,"column":23},"end":{"line":38,"column":51}},"12":{"start":{"line":39,"column":22},"end":{"line":39,"column":35}},"13":{"start":{"line":40,"column":28},"end":{"line":61,"column":8}},"14":{"start":{"line":41,"column":8},"end":{"line":60,"column":9}},"15":{"start":{"line":47,"column":60},"end":{"line":47,"column":111}},"16":{"start":{"line":63,"column":21},"end":{"line":63,"column":55}},"17":{"start":{"line":64,"column":22},"end":{"line":64,"column":32}},"18":{"start":{"line":65,"column":21},"end":{"line":65,"column":57}},"19":{"start":{"line":67,"column":6},"end":{"line":67,"column":35}},"20":{"start":{"line":69,"column":6},"end":{"line":69,"column":18}},"21":{"start":{"line":76,"column":17},"end":{"line":76,"column":27}},"22":{"start":{"line":78,"column":20},"end":{"line":85,"column":5}},"23":{"start":{"line":87,"column":4},"end":{"line":114,"column":5}},"24":{"start":{"line":88,"column":23},"end":{"line":88,"column":51}},"25":{"start":{"line":89,"column":21},"end":{"line":89,"column":34}},"26":{"start":{"line":91,"column":6},"end":{"line":110,"column":9}},"27":{"start":{"line":97,"column":58},"end":{"line":97,"column":109}},"28":{"start":{"line":113,"column":6},"end":{"line":113,"column":18}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":6,"column":2},"end":{"line":6,"column":3}},"loc":{"start":{"line":6,"column":39},"end":{"line":71,"column":3}},"line":6},"1":{"name":"(anonymous_1)","decl":{"start":{"line":40,"column":40},"end":{"line":40,"column":41}},"loc":{"start":{"line":40,"column":59},"end":{"line":61,"column":7}},"line":40},"2":{"name":"(anonymous_2)","decl":{"start":{"line":47,"column":46},"end":{"line":47,"column":47}},"loc":{"start":{"line":47,"column":60},"end":{"line":47,"column":111}},"line":47},"3":{"name":"(anonymous_3)","decl":{"start":{"line":74,"column":2},"end":{"line":74,"column":3}},"loc":{"start":{"line":74,"column":42},"end":{"line":115,"column":3}},"line":74},"4":{"name":"(anonymous_4)","decl":{"start":{"line":97,"column":44},"end":{"line":97,"column":45}},"loc":{"start":{"line":97,"column":58},"end":{"line":97,"column":109}},"line":97}},"branchMap":{"0":{"loc":{"start":{"line":28,"column":4},"end":{"line":30,"column":5}},"type":"if","locations":[{"start":{"line":28,"column":4},"end":{"line":30,"column":5}},{"start":{},"end":{}}],"line":28},"1":{"loc":{"start":{"line":32,"column":4},"end":{"line":35,"column":5}},"type":"if","locations":[{"start":{"line":32,"column":4},"end":{"line":35,"column":5}},{"start":{},"end":{}}],"line":32},"2":{"loc":{"start":{"line":63,"column":21},"end":{"line":63,"column":55}},"type":"cond-expr","locations":[{"start":{"line":63,"column":37},"end":{"line":63,"column":38}},{"start":{"line":63,"column":41},"end":{"line":63,"column":55}}],"line":63}},"s":{"0":1,"1":1,"2":1,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0},"f":{"0":0,"1":0,"2":0,"3":0,"4":0},"b":{"0":[0,0],"1":[0,0],"2":[0,0]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"91bf0a2654e4ad11fc7d0cf5f557a16740e6f686"} +,"D:\\IP-RMT42\\server\\controllers\\UserController.js": {"path":"D:\\IP-RMT42\\server\\controllers\\UserController.js","statementMap":{"0":{"start":{"line":1,"column":28},"end":{"line":1,"column":58}},"1":{"start":{"line":2,"column":22},"end":{"line":2,"column":47}},"2":{"start":{"line":3,"column":17},"end":{"line":3,"column":37}},"3":{"start":{"line":4,"column":25},"end":{"line":4,"column":55}},"4":{"start":{"line":5,"column":15},"end":{"line":5,"column":33}},"5":{"start":{"line":8,"column":0},"end":{"line":92,"column":1}},"6":{"start":{"line":11,"column":8},"end":{"line":22,"column":9}},"7":{"start":{"line":12,"column":50},"end":{"line":12,"column":58}},"8":{"start":{"line":13,"column":25},"end":{"line":13,"column":73}},"9":{"start":{"line":15,"column":12},"end":{"line":18,"column":15}},"10":{"start":{"line":21,"column":12},"end":{"line":21,"column":24}},"11":{"start":{"line":27,"column":8},"end":{"line":58,"column":9}},"12":{"start":{"line":28,"column":40},"end":{"line":28,"column":48}},"13":{"start":{"line":30,"column":12},"end":{"line":33,"column":13}},"14":{"start":{"line":31,"column":16},"end":{"line":31,"column":85}},"15":{"start":{"line":32,"column":16},"end":{"line":32,"column":23}},"16":{"start":{"line":35,"column":12},"end":{"line":38,"column":13}},"17":{"start":{"line":36,"column":16},"end":{"line":36,"column":85}},"18":{"start":{"line":37,"column":16},"end":{"line":37,"column":23}},"19":{"start":{"line":40,"column":25},"end":{"line":40,"column":65}},"20":{"start":{"line":41,"column":12},"end":{"line":44,"column":13}},"21":{"start":{"line":42,"column":16},"end":{"line":42,"column":101}},"22":{"start":{"line":43,"column":16},"end":{"line":43,"column":23}},"23":{"start":{"line":46,"column":36},"end":{"line":46,"column":76}},"24":{"start":{"line":47,"column":12},"end":{"line":50,"column":13}},"25":{"start":{"line":48,"column":16},"end":{"line":48,"column":101}},"26":{"start":{"line":49,"column":16},"end":{"line":49,"column":23}},"27":{"start":{"line":52,"column":33},"end":{"line":52,"column":59}},"28":{"start":{"line":53,"column":12},"end":{"line":53,"column":51}},"29":{"start":{"line":56,"column":12},"end":{"line":56,"column":30}},"30":{"start":{"line":57,"column":12},"end":{"line":57,"column":24}},"31":{"start":{"line":63,"column":8},"end":{"line":63,"column":40}},"32":{"start":{"line":65,"column":8},"end":{"line":89,"column":9}},"33":{"start":{"line":66,"column":27},"end":{"line":69,"column":14}},"34":{"start":{"line":70,"column":28},"end":{"line":70,"column":47}},"35":{"start":{"line":72,"column":12},"end":{"line":72,"column":32}},"36":{"start":{"line":74,"column":40},"end":{"line":82,"column":14}},"37":{"start":{"line":84,"column":33},"end":{"line":84,"column":59}},"38":{"start":{"line":85,"column":12},"end":{"line":85,"column":71}},"39":{"start":{"line":88,"column":12},"end":{"line":88,"column":24}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":10,"column":4},"end":{"line":10,"column":5}},"loc":{"start":{"line":10,"column":42},"end":{"line":23,"column":5}},"line":10},"1":{"name":"(anonymous_1)","decl":{"start":{"line":26,"column":4},"end":{"line":26,"column":5}},"loc":{"start":{"line":26,"column":39},"end":{"line":59,"column":5}},"line":26},"2":{"name":"(anonymous_2)","decl":{"start":{"line":62,"column":4},"end":{"line":62,"column":5}},"loc":{"start":{"line":62,"column":45},"end":{"line":90,"column":5}},"line":62}},"branchMap":{"0":{"loc":{"start":{"line":30,"column":12},"end":{"line":33,"column":13}},"type":"if","locations":[{"start":{"line":30,"column":12},"end":{"line":33,"column":13}},{"start":{},"end":{}}],"line":30},"1":{"loc":{"start":{"line":35,"column":12},"end":{"line":38,"column":13}},"type":"if","locations":[{"start":{"line":35,"column":12},"end":{"line":38,"column":13}},{"start":{},"end":{}}],"line":35},"2":{"loc":{"start":{"line":41,"column":12},"end":{"line":44,"column":13}},"type":"if","locations":[{"start":{"line":41,"column":12},"end":{"line":44,"column":13}},{"start":{},"end":{}}],"line":41},"3":{"loc":{"start":{"line":47,"column":12},"end":{"line":50,"column":13}},"type":"if","locations":[{"start":{"line":47,"column":12},"end":{"line":50,"column":13}},{"start":{},"end":{}}],"line":47},"4":{"loc":{"start":{"line":85,"column":23},"end":{"line":85,"column":46}},"type":"cond-expr","locations":[{"start":{"line":85,"column":37},"end":{"line":85,"column":40}},{"start":{"line":85,"column":43},"end":{"line":85,"column":46}}],"line":85}},"s":{"0":1,"1":1,"2":1,"3":1,"4":1,"5":1,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":0,"31":0,"32":0,"33":0,"34":0,"35":0,"36":0,"37":0,"38":0,"39":0},"f":{"0":0,"1":0,"2":0},"b":{"0":[0,0],"1":[0,0],"2":[0,0],"3":[0,0],"4":[0,0]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"6ebdeb727e9ad3c3b3d452bf6da7c8c621ab5402"} +,"D:\\IP-RMT42\\server\\helpers\\bcryptjs.js": {"path":"D:\\IP-RMT42\\server\\helpers\\bcryptjs.js","statementMap":{"0":{"start":{"line":1,"column":15},"end":{"line":1,"column":34}},"1":{"start":{"line":3,"column":21},"end":{"line":6,"column":1}},"2":{"start":{"line":4,"column":17},"end":{"line":4,"column":39}},"3":{"start":{"line":5,"column":4},"end":{"line":5,"column":43}},"4":{"start":{"line":8,"column":24},"end":{"line":10,"column":1}},"5":{"start":{"line":9,"column":4},"end":{"line":9,"column":56}},"6":{"start":{"line":13,"column":0},"end":{"line":13,"column":49}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":3,"column":21},"end":{"line":3,"column":22}},"loc":{"start":{"line":3,"column":35},"end":{"line":6,"column":1}},"line":3},"1":{"name":"(anonymous_1)","decl":{"start":{"line":8,"column":24},"end":{"line":8,"column":25}},"loc":{"start":{"line":8,"column":54},"end":{"line":10,"column":1}},"line":8}},"branchMap":{},"s":{"0":1,"1":1,"2":1,"3":1,"4":1,"5":0,"6":1},"f":{"0":1,"1":0},"b":{},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"f4e7adf820f9a1e12aa3d842fa380a19157d320f"} +,"D:\\IP-RMT42\\server\\helpers\\jwt.js": {"path":"D:\\IP-RMT42\\server\\helpers\\jwt.js","statementMap":{"0":{"start":{"line":1,"column":12},"end":{"line":1,"column":35}},"1":{"start":{"line":3,"column":19},"end":{"line":3,"column":41}},"2":{"start":{"line":5,"column":18},"end":{"line":7,"column":1}},"3":{"start":{"line":6,"column":4},"end":{"line":6,"column":41}},"4":{"start":{"line":9,"column":20},"end":{"line":11,"column":1}},"5":{"start":{"line":10,"column":4},"end":{"line":10,"column":41}},"6":{"start":{"line":14,"column":0},"end":{"line":14,"column":42}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":5,"column":18},"end":{"line":5,"column":19}},"loc":{"start":{"line":5,"column":31},"end":{"line":7,"column":1}},"line":5},"1":{"name":"(anonymous_1)","decl":{"start":{"line":9,"column":20},"end":{"line":9,"column":21}},"loc":{"start":{"line":9,"column":31},"end":{"line":11,"column":1}},"line":9}},"branchMap":{},"s":{"0":1,"1":1,"2":1,"3":1,"4":1,"5":2,"6":1},"f":{"0":1,"1":2},"b":{},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"4af7a390dc8d5bb0010375dba86fe5e4fb533ffa"} +,"D:\\IP-RMT42\\server\\middlewares\\authentication.js": {"path":"D:\\IP-RMT42\\server\\middlewares\\authentication.js","statementMap":{"0":{"start":{"line":1,"column":17},"end":{"line":1,"column":37}},"1":{"start":{"line":2,"column":24},"end":{"line":2,"column":49}},"2":{"start":{"line":5,"column":4},"end":{"line":26,"column":5}},"3":{"start":{"line":6,"column":27},"end":{"line":6,"column":52}},"4":{"start":{"line":8,"column":8},"end":{"line":8,"column":60}},"5":{"start":{"line":8,"column":26},"end":{"line":8,"column":60}},"6":{"start":{"line":10,"column":23},"end":{"line":10,"column":57}},"7":{"start":{"line":12,"column":19},"end":{"line":12,"column":42}},"8":{"start":{"line":14,"column":8},"end":{"line":14,"column":52}},"9":{"start":{"line":14,"column":18},"end":{"line":14,"column":52}},"10":{"start":{"line":16,"column":8},"end":{"line":18,"column":9}},"11":{"start":{"line":20,"column":8},"end":{"line":20,"column":29}},"12":{"start":{"line":21,"column":8},"end":{"line":21,"column":15}},"13":{"start":{"line":24,"column":8},"end":{"line":24,"column":20}},"14":{"start":{"line":25,"column":8},"end":{"line":25,"column":26}},"15":{"start":{"line":29,"column":0},"end":{"line":29,"column":31}}},"fnMap":{"0":{"name":"authentication","decl":{"start":{"line":4,"column":15},"end":{"line":4,"column":29}},"loc":{"start":{"line":4,"column":45},"end":{"line":27,"column":1}},"line":4}},"branchMap":{"0":{"loc":{"start":{"line":8,"column":8},"end":{"line":8,"column":60}},"type":"if","locations":[{"start":{"line":8,"column":8},"end":{"line":8,"column":60}},{"start":{},"end":{}}],"line":8},"1":{"loc":{"start":{"line":14,"column":8},"end":{"line":14,"column":52}},"type":"if","locations":[{"start":{"line":14,"column":8},"end":{"line":14,"column":52}},{"start":{},"end":{}}],"line":14}},"s":{"0":1,"1":1,"2":3,"3":3,"4":3,"5":1,"6":2,"7":1,"8":1,"9":0,"10":1,"11":1,"12":1,"13":2,"14":2,"15":1},"f":{"0":3},"b":{"0":[1,2],"1":[0,1]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"c52a6eb796d691cdb6d74c76b8ddbccfc81d8162"} +,"D:\\IP-RMT42\\server\\middlewares\\authorization.js": {"path":"D:\\IP-RMT42\\server\\middlewares\\authorization.js","statementMap":{"0":{"start":{"line":1,"column":18},"end":{"line":1,"column":38}},"1":{"start":{"line":4,"column":4},"end":{"line":17,"column":5}},"2":{"start":{"line":5,"column":22},"end":{"line":5,"column":59}},"3":{"start":{"line":7,"column":8},"end":{"line":7,"column":47}},"4":{"start":{"line":7,"column":21},"end":{"line":7,"column":47}},"5":{"start":{"line":9,"column":8},"end":{"line":11,"column":9}},"6":{"start":{"line":10,"column":12},"end":{"line":10,"column":37}},"7":{"start":{"line":13,"column":8},"end":{"line":13,"column":15}},"8":{"start":{"line":16,"column":8},"end":{"line":16,"column":20}},"9":{"start":{"line":21,"column":0},"end":{"line":21,"column":31}}},"fnMap":{"0":{"name":"authorization","decl":{"start":{"line":3,"column":15},"end":{"line":3,"column":28}},"loc":{"start":{"line":3,"column":45},"end":{"line":18,"column":1}},"line":3}},"branchMap":{"0":{"loc":{"start":{"line":7,"column":8},"end":{"line":7,"column":47}},"type":"if","locations":[{"start":{"line":7,"column":8},"end":{"line":7,"column":47}},{"start":{},"end":{}}],"line":7},"1":{"loc":{"start":{"line":9,"column":8},"end":{"line":11,"column":9}},"type":"if","locations":[{"start":{"line":9,"column":8},"end":{"line":11,"column":9}},{"start":{},"end":{}}],"line":9}},"s":{"0":1,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":1},"f":{"0":0},"b":{"0":[0,0],"1":[0,0]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"fd4b46832ec27473ac744561f94dc9125c34a7f5"} +,"D:\\IP-RMT42\\server\\middlewares\\errorHandler.js": {"path":"D:\\IP-RMT42\\server\\middlewares\\errorHandler.js","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":35,"column":1}},"1":{"start":{"line":3,"column":4},"end":{"line":33,"column":5}},"2":{"start":{"line":6,"column":12},"end":{"line":6,"column":71}},"3":{"start":{"line":7,"column":12},"end":{"line":7,"column":18}},"4":{"start":{"line":10,"column":12},"end":{"line":10,"column":61}},"5":{"start":{"line":11,"column":12},"end":{"line":11,"column":18}},"6":{"start":{"line":15,"column":12},"end":{"line":15,"column":82}},"7":{"start":{"line":16,"column":12},"end":{"line":16,"column":18}},"8":{"start":{"line":19,"column":12},"end":{"line":19,"column":72}},"9":{"start":{"line":20,"column":12},"end":{"line":20,"column":18}},"10":{"start":{"line":23,"column":12},"end":{"line":23,"column":67}},"11":{"start":{"line":24,"column":12},"end":{"line":24,"column":18}},"12":{"start":{"line":27,"column":12},"end":{"line":27,"column":66}},"13":{"start":{"line":28,"column":12},"end":{"line":28,"column":18}},"14":{"start":{"line":31,"column":12},"end":{"line":31,"column":71}},"15":{"start":{"line":32,"column":12},"end":{"line":32,"column":18}}},"fnMap":{"0":{"name":"errorHandler","decl":{"start":{"line":1,"column":26},"end":{"line":1,"column":38}},"loc":{"start":{"line":1,"column":62},"end":{"line":35,"column":1}},"line":1}},"branchMap":{"0":{"loc":{"start":{"line":3,"column":4},"end":{"line":33,"column":5}},"type":"switch","locations":[{"start":{"line":4,"column":8},"end":{"line":4,"column":40}},{"start":{"line":5,"column":8},"end":{"line":7,"column":18}},{"start":{"line":9,"column":8},"end":{"line":11,"column":18}},{"start":{"line":13,"column":8},"end":{"line":13,"column":33}},{"start":{"line":14,"column":8},"end":{"line":16,"column":18}},{"start":{"line":18,"column":8},"end":{"line":20,"column":18}},{"start":{"line":22,"column":8},"end":{"line":24,"column":18}},{"start":{"line":26,"column":8},"end":{"line":28,"column":18}},{"start":{"line":30,"column":8},"end":{"line":32,"column":18}}],"line":3},"1":{"loc":{"start":{"line":15,"column":44},"end":{"line":15,"column":78}},"type":"binary-expr","locations":[{"start":{"line":15,"column":44},"end":{"line":15,"column":57}},{"start":{"line":15,"column":61},"end":{"line":15,"column":78}}],"line":15}},"s":{"0":1,"1":2,"2":0,"3":0,"4":0,"5":0,"6":2,"7":2,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0},"f":{"0":2},"b":{"0":[0,0,0,1,2,0,0,0,0],"1":[2,1]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"21faaae869c2555b110e907fb153b538d48e758a"} +,"D:\\IP-RMT42\\server\\models\\comment.js": {"path":"D:\\IP-RMT42\\server\\models\\comment.js","statementMap":{"0":{"start":{"line":4,"column":4},"end":{"line":4,"column":24}},"1":{"start":{"line":5,"column":0},"end":{"line":25,"column":2}},"2":{"start":{"line":14,"column":6},"end":{"line":14,"column":60}},"3":{"start":{"line":17,"column":2},"end":{"line":23,"column":5}},"4":{"start":{"line":24,"column":2},"end":{"line":24,"column":17}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":5,"column":17},"end":{"line":5,"column":18}},"loc":{"start":{"line":5,"column":43},"end":{"line":25,"column":1}},"line":5},"1":{"name":"(anonymous_1)","decl":{"start":{"line":12,"column":4},"end":{"line":12,"column":5}},"loc":{"start":{"line":12,"column":29},"end":{"line":15,"column":5}},"line":12}},"branchMap":{},"s":{"0":1,"1":1,"2":1,"3":1,"4":1},"f":{"0":1,"1":1},"b":{},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"6d6f8423a2d02432d92d3b5fc92e0a77f98b0c1a"} +,"D:\\IP-RMT42\\server\\models\\index.js": {"path":"D:\\IP-RMT42\\server\\models\\index.js","statementMap":{"0":{"start":{"line":3,"column":11},"end":{"line":3,"column":24}},"1":{"start":{"line":4,"column":13},"end":{"line":4,"column":28}},"2":{"start":{"line":5,"column":18},"end":{"line":5,"column":38}},"3":{"start":{"line":6,"column":16},"end":{"line":6,"column":34}},"4":{"start":{"line":7,"column":17},"end":{"line":7,"column":42}},"5":{"start":{"line":8,"column":12},"end":{"line":8,"column":49}},"6":{"start":{"line":9,"column":15},"end":{"line":9,"column":65}},"7":{"start":{"line":10,"column":11},"end":{"line":10,"column":13}},"8":{"start":{"line":13,"column":0},"end":{"line":17,"column":1}},"9":{"start":{"line":14,"column":2},"end":{"line":14,"column":74}},"10":{"start":{"line":16,"column":2},"end":{"line":16,"column":87}},"11":{"start":{"line":19,"column":0},"end":{"line":32,"column":5}},"12":{"start":{"line":22,"column":4},"end":{"line":27,"column":6}},"13":{"start":{"line":30,"column":18},"end":{"line":30,"column":85}},"14":{"start":{"line":31,"column":4},"end":{"line":31,"column":27}},"15":{"start":{"line":34,"column":0},"end":{"line":38,"column":3}},"16":{"start":{"line":35,"column":2},"end":{"line":37,"column":3}},"17":{"start":{"line":36,"column":4},"end":{"line":36,"column":32}},"18":{"start":{"line":40,"column":0},"end":{"line":40,"column":25}},"19":{"start":{"line":41,"column":0},"end":{"line":41,"column":25}},"20":{"start":{"line":43,"column":0},"end":{"line":43,"column":20}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":21,"column":10},"end":{"line":21,"column":11}},"loc":{"start":{"line":21,"column":18},"end":{"line":28,"column":3}},"line":21},"1":{"name":"(anonymous_1)","decl":{"start":{"line":29,"column":11},"end":{"line":29,"column":12}},"loc":{"start":{"line":29,"column":19},"end":{"line":32,"column":3}},"line":29},"2":{"name":"(anonymous_2)","decl":{"start":{"line":34,"column":24},"end":{"line":34,"column":25}},"loc":{"start":{"line":34,"column":37},"end":{"line":38,"column":1}},"line":34}},"branchMap":{"0":{"loc":{"start":{"line":8,"column":12},"end":{"line":8,"column":49}},"type":"binary-expr","locations":[{"start":{"line":8,"column":12},"end":{"line":8,"column":32}},{"start":{"line":8,"column":36},"end":{"line":8,"column":49}}],"line":8},"1":{"loc":{"start":{"line":13,"column":0},"end":{"line":17,"column":1}},"type":"if","locations":[{"start":{"line":13,"column":0},"end":{"line":17,"column":1}},{"start":{"line":15,"column":7},"end":{"line":17,"column":1}}],"line":13},"2":{"loc":{"start":{"line":23,"column":6},"end":{"line":26,"column":37}},"type":"binary-expr","locations":[{"start":{"line":23,"column":6},"end":{"line":23,"column":29}},{"start":{"line":24,"column":6},"end":{"line":24,"column":23}},{"start":{"line":25,"column":6},"end":{"line":25,"column":30}},{"start":{"line":26,"column":6},"end":{"line":26,"column":37}}],"line":23},"3":{"loc":{"start":{"line":35,"column":2},"end":{"line":37,"column":3}},"type":"if","locations":[{"start":{"line":35,"column":2},"end":{"line":37,"column":3}},{"start":{},"end":{}}],"line":35}},"s":{"0":1,"1":1,"2":1,"3":1,"4":1,"5":1,"6":1,"7":1,"8":1,"9":0,"10":1,"11":1,"12":4,"13":3,"14":3,"15":1,"16":3,"17":3,"18":1,"19":1,"20":1},"f":{"0":4,"1":3,"2":3},"b":{"0":[1,0],"1":[0,1],"2":[4,4,3,3],"3":[3,0]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"212e906caafd968e92070d46b09632103eeaa93c"} +,"D:\\IP-RMT42\\server\\models\\recipe.js": {"path":"D:\\IP-RMT42\\server\\models\\recipe.js","statementMap":{"0":{"start":{"line":4,"column":4},"end":{"line":4,"column":24}},"1":{"start":{"line":5,"column":0},"end":{"line":30,"column":2}},"2":{"start":{"line":16,"column":2},"end":{"line":28,"column":5}},"3":{"start":{"line":29,"column":2},"end":{"line":29,"column":16}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":5,"column":17},"end":{"line":5,"column":18}},"loc":{"start":{"line":5,"column":43},"end":{"line":30,"column":1}},"line":5},"1":{"name":"(anonymous_1)","decl":{"start":{"line":12,"column":4},"end":{"line":12,"column":5}},"loc":{"start":{"line":12,"column":29},"end":{"line":14,"column":5}},"line":12}},"branchMap":{},"s":{"0":1,"1":1,"2":1,"3":1},"f":{"0":1,"1":1},"b":{},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"83a4c86bf2d26b7378b9cb519cd42cf454874ae6"} +,"D:\\IP-RMT42\\server\\models\\user.js": {"path":"D:\\IP-RMT42\\server\\models\\user.js","statementMap":{"0":{"start":{"line":2,"column":18},"end":{"line":2,"column":38}},"1":{"start":{"line":3,"column":25},"end":{"line":3,"column":55}},"2":{"start":{"line":6,"column":0},"end":{"line":65,"column":2}},"3":{"start":{"line":15,"column":6},"end":{"line":15,"column":59}},"4":{"start":{"line":18,"column":2},"end":{"line":58,"column":5}},"5":{"start":{"line":60,"column":2},"end":{"line":62,"column":4}},"6":{"start":{"line":61,"column":4},"end":{"line":61,"column":48}},"7":{"start":{"line":64,"column":2},"end":{"line":64,"column":14}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":6,"column":17},"end":{"line":6,"column":18}},"loc":{"start":{"line":6,"column":43},"end":{"line":65,"column":1}},"line":6},"1":{"name":"(anonymous_1)","decl":{"start":{"line":13,"column":4},"end":{"line":13,"column":5}},"loc":{"start":{"line":13,"column":29},"end":{"line":16,"column":5}},"line":13},"2":{"name":"(anonymous_2)","decl":{"start":{"line":60,"column":20},"end":{"line":60,"column":21}},"loc":{"start":{"line":60,"column":30},"end":{"line":62,"column":3}},"line":60}},"branchMap":{},"s":{"0":1,"1":1,"2":1,"3":1,"4":1,"5":1,"6":1,"7":1},"f":{"0":1,"1":1,"2":1},"b":{},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"93c11956d0a296188dcc2a2958bb6f4e084876d9"} +,"D:\\IP-RMT42\\server\\routes\\index.js": {"path":"D:\\IP-RMT42\\server\\routes\\index.js","statementMap":{"0":{"start":{"line":1,"column":23},"end":{"line":1,"column":63}},"1":{"start":{"line":2,"column":25},"end":{"line":2,"column":67}},"2":{"start":{"line":3,"column":23},"end":{"line":3,"column":63}},"3":{"start":{"line":4,"column":26},"end":{"line":4,"column":69}},"4":{"start":{"line":5,"column":23},"end":{"line":5,"column":63}},"5":{"start":{"line":6,"column":22},"end":{"line":6,"column":61}},"6":{"start":{"line":7,"column":21},"end":{"line":7,"column":59}},"7":{"start":{"line":9,"column":15},"end":{"line":9,"column":42}},"8":{"start":{"line":12,"column":0},"end":{"line":12,"column":50}},"9":{"start":{"line":14,"column":0},"end":{"line":14,"column":44}},"10":{"start":{"line":16,"column":0},"end":{"line":16,"column":57}},"11":{"start":{"line":21,"column":0},"end":{"line":21,"column":26}},"12":{"start":{"line":23,"column":0},"end":{"line":23,"column":49}},"13":{"start":{"line":25,"column":0},"end":{"line":25,"column":55}},"14":{"start":{"line":27,"column":0},"end":{"line":27,"column":54}},"15":{"start":{"line":32,"column":0},"end":{"line":32,"column":58}},"16":{"start":{"line":34,"column":0},"end":{"line":34,"column":78}},"17":{"start":{"line":36,"column":0},"end":{"line":36,"column":85}},"18":{"start":{"line":39,"column":0},"end":{"line":39,"column":25}},"19":{"start":{"line":42,"column":0},"end":{"line":42,"column":24}}},"fnMap":{},"branchMap":{},"s":{"0":1,"1":1,"2":1,"3":1,"4":1,"5":1,"6":1,"7":1,"8":1,"9":1,"10":1,"11":1,"12":1,"13":1,"14":1,"15":1,"16":1,"17":1,"18":1,"19":1},"f":{},"b":{},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"da6975d7843f5bf7a0e013236865af8855ab8da1"} +} diff --git a/server/coverage/lcov-report/base.css b/server/coverage/lcov-report/base.css new file mode 100644 index 0000000..f418035 --- /dev/null +++ b/server/coverage/lcov-report/base.css @@ -0,0 +1,224 @@ +body, html { + margin:0; padding: 0; + height: 100%; +} +body { + font-family: Helvetica Neue, Helvetica, Arial; + font-size: 14px; + color:#333; +} +.small { font-size: 12px; } +*, *:after, *:before { + -webkit-box-sizing:border-box; + -moz-box-sizing:border-box; + box-sizing:border-box; + } +h1 { font-size: 20px; margin: 0;} +h2 { font-size: 14px; } +pre { + font: 12px/1.4 Consolas, "Liberation Mono", Menlo, Courier, monospace; + margin: 0; + padding: 0; + -moz-tab-size: 2; + -o-tab-size: 2; + tab-size: 2; +} +a { color:#0074D9; text-decoration:none; } +a:hover { text-decoration:underline; } +.strong { font-weight: bold; } +.space-top1 { padding: 10px 0 0 0; } +.pad2y { padding: 20px 0; } +.pad1y { padding: 10px 0; } +.pad2x { padding: 0 20px; } +.pad2 { padding: 20px; } +.pad1 { padding: 10px; } +.space-left2 { padding-left:55px; } +.space-right2 { padding-right:20px; } +.center { text-align:center; } +.clearfix { display:block; } +.clearfix:after { + content:''; + display:block; + height:0; + clear:both; + visibility:hidden; + } +.fl { float: left; } +@media only screen and (max-width:640px) { + .col3 { width:100%; max-width:100%; } + .hide-mobile { display:none!important; } +} + +.quiet { + color: #7f7f7f; + color: rgba(0,0,0,0.5); +} +.quiet a { opacity: 0.7; } + +.fraction { + font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; + font-size: 10px; + color: #555; + background: #E8E8E8; + padding: 4px 5px; + border-radius: 3px; + vertical-align: middle; +} + +div.path a:link, div.path a:visited { color: #333; } +table.coverage { + border-collapse: collapse; + margin: 10px 0 0 0; + padding: 0; +} + +table.coverage td { + margin: 0; + padding: 0; + vertical-align: top; +} +table.coverage td.line-count { + text-align: right; + padding: 0 5px 0 20px; +} +table.coverage td.line-coverage { + text-align: right; + padding-right: 10px; + min-width:20px; +} + +table.coverage td span.cline-any { + display: inline-block; + padding: 0 5px; + width: 100%; +} +.missing-if-branch { + display: inline-block; + margin-right: 5px; + border-radius: 3px; + position: relative; + padding: 0 4px; + background: #333; + color: yellow; +} + +.skip-if-branch { + display: none; + margin-right: 10px; + position: relative; + padding: 0 4px; + background: #ccc; + color: white; +} +.missing-if-branch .typ, .skip-if-branch .typ { + color: inherit !important; +} +.coverage-summary { + border-collapse: collapse; + width: 100%; +} +.coverage-summary tr { border-bottom: 1px solid #bbb; } +.keyline-all { border: 1px solid #ddd; } +.coverage-summary td, .coverage-summary th { padding: 10px; } +.coverage-summary tbody { border: 1px solid #bbb; } +.coverage-summary td { border-right: 1px solid #bbb; } +.coverage-summary td:last-child { border-right: none; } +.coverage-summary th { + text-align: left; + font-weight: normal; + white-space: nowrap; +} +.coverage-summary th.file { border-right: none !important; } +.coverage-summary th.pct { } +.coverage-summary th.pic, +.coverage-summary th.abs, +.coverage-summary td.pct, +.coverage-summary td.abs { text-align: right; } +.coverage-summary td.file { white-space: nowrap; } +.coverage-summary td.pic { min-width: 120px !important; } +.coverage-summary tfoot td { } + +.coverage-summary .sorter { + height: 10px; + width: 7px; + display: inline-block; + margin-left: 0.5em; + background: url(sort-arrow-sprite.png) no-repeat scroll 0 0 transparent; +} +.coverage-summary .sorted .sorter { + background-position: 0 -20px; +} +.coverage-summary .sorted-desc .sorter { + background-position: 0 -10px; +} +.status-line { height: 10px; } +/* yellow */ +.cbranch-no { background: yellow !important; color: #111; } +/* dark red */ +.red.solid, .status-line.low, .low .cover-fill { background:#C21F39 } +.low .chart { border:1px solid #C21F39 } +.highlighted, +.highlighted .cstat-no, .highlighted .fstat-no, .highlighted .cbranch-no{ + background: #C21F39 !important; +} +/* medium red */ +.cstat-no, .fstat-no, .cbranch-no, .cbranch-no { background:#F6C6CE } +/* light red */ +.low, .cline-no { background:#FCE1E5 } +/* light green */ +.high, .cline-yes { background:rgb(230,245,208) } +/* medium green */ +.cstat-yes { background:rgb(161,215,106) } +/* dark green */ +.status-line.high, .high .cover-fill { background:rgb(77,146,33) } +.high .chart { border:1px solid rgb(77,146,33) } +/* dark yellow (gold) */ +.status-line.medium, .medium .cover-fill { background: #f9cd0b; } +.medium .chart { border:1px solid #f9cd0b; } +/* light yellow */ +.medium { background: #fff4c2; } + +.cstat-skip { background: #ddd; color: #111; } +.fstat-skip { background: #ddd; color: #111 !important; } +.cbranch-skip { background: #ddd !important; color: #111; } + +span.cline-neutral { background: #eaeaea; } + +.coverage-summary td.empty { + opacity: .5; + padding-top: 4px; + padding-bottom: 4px; + line-height: 1; + color: #888; +} + +.cover-fill, .cover-empty { + display:inline-block; + height: 12px; +} +.chart { + line-height: 0; +} +.cover-empty { + background: white; +} +.cover-full { + border-right: none !important; +} +pre.prettyprint { + border: none !important; + padding: 0 !important; + margin: 0 !important; +} +.com { color: #999 !important; } +.ignore-none { color: #999; font-weight: normal; } + +.wrapper { + min-height: 100%; + height: auto !important; + height: 100%; + margin: 0 auto -48px; +} +.footer, .push { + height: 48px; +} diff --git a/server/coverage/lcov-report/block-navigation.js b/server/coverage/lcov-report/block-navigation.js new file mode 100644 index 0000000..cc12130 --- /dev/null +++ b/server/coverage/lcov-report/block-navigation.js @@ -0,0 +1,87 @@ +/* eslint-disable */ +var jumpToCode = (function init() { + // Classes of code we would like to highlight in the file view + var missingCoverageClasses = ['.cbranch-no', '.cstat-no', '.fstat-no']; + + // Elements to highlight in the file listing view + var fileListingElements = ['td.pct.low']; + + // We don't want to select elements that are direct descendants of another match + var notSelector = ':not(' + missingCoverageClasses.join('):not(') + ') > '; // becomes `:not(a):not(b) > ` + + // Selecter that finds elements on the page to which we can jump + var selector = + fileListingElements.join(', ') + + ', ' + + notSelector + + missingCoverageClasses.join(', ' + notSelector); // becomes `:not(a):not(b) > a, :not(a):not(b) > b` + + // The NodeList of matching elements + var missingCoverageElements = document.querySelectorAll(selector); + + var currentIndex; + + function toggleClass(index) { + missingCoverageElements + .item(currentIndex) + .classList.remove('highlighted'); + missingCoverageElements.item(index).classList.add('highlighted'); + } + + function makeCurrent(index) { + toggleClass(index); + currentIndex = index; + missingCoverageElements.item(index).scrollIntoView({ + behavior: 'smooth', + block: 'center', + inline: 'center' + }); + } + + function goToPrevious() { + var nextIndex = 0; + if (typeof currentIndex !== 'number' || currentIndex === 0) { + nextIndex = missingCoverageElements.length - 1; + } else if (missingCoverageElements.length > 1) { + nextIndex = currentIndex - 1; + } + + makeCurrent(nextIndex); + } + + function goToNext() { + var nextIndex = 0; + + if ( + typeof currentIndex === 'number' && + currentIndex < missingCoverageElements.length - 1 + ) { + nextIndex = currentIndex + 1; + } + + makeCurrent(nextIndex); + } + + return function jump(event) { + if ( + document.getElementById('fileSearch') === document.activeElement && + document.activeElement != null + ) { + // if we're currently focused on the search input, we don't want to navigate + return; + } + + switch (event.which) { + case 78: // n + case 74: // j + goToNext(); + break; + case 66: // b + case 75: // k + case 80: // p + goToPrevious(); + break; + } + }; +})(); +window.addEventListener('keydown', jumpToCode); diff --git a/server/coverage/lcov-report/favicon.png b/server/coverage/lcov-report/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..c1525b811a167671e9de1fa78aab9f5c0b61cef7 GIT binary patch literal 445 zcmV;u0Yd(XP))rP{nL}Ln%S7`m{0DjX9TLF* zFCb$4Oi7vyLOydb!7n&^ItCzb-%BoB`=x@N2jll2Nj`kauio%aw_@fe&*}LqlFT43 z8doAAe))z_%=P%v^@JHp3Hjhj^6*Kr_h|g_Gr?ZAa&y>wxHE99Gk>A)2MplWz2xdG zy8VD2J|Uf#EAw*bo5O*PO_}X2Tob{%bUoO2G~T`@%S6qPyc}VkhV}UifBuRk>%5v( z)x7B{I~z*k<7dv#5tC+m{km(D087J4O%+<<;K|qwefb6@GSX45wCK}Sn*> + + + + Code coverage report for All files + + + + + + + + + +
+
+

All files

+
+ +
+ 52.94% + Statements + 117/221 +
+ + +
+ 28.84% + Branches + 15/52 +
+ + +
+ 55.17% + Functions + 16/29 +
+ + +
+ 53.45% + Lines + 116/217 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
server +
+
100%10/1050%1/2100%0/0100%10/10
server/controllers +
+
16.49%16/970%0/218.33%1/1216.66%16/96
server/helpers +
+
92.85%13/14100%0/075%3/492.85%13/14
server/middlewares +
+
50%21/4236.84%7/1966.66%2/351.28%20/39
server/models +
+
97.36%37/3870%7/10100%10/1097.36%37/38
server/routes +
+
100%20/20100%0/0100%0/0100%20/20
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/server/coverage/lcov-report/prettify.css b/server/coverage/lcov-report/prettify.css new file mode 100644 index 0000000..b317a7c --- /dev/null +++ b/server/coverage/lcov-report/prettify.css @@ -0,0 +1 @@ +.pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee} diff --git a/server/coverage/lcov-report/prettify.js b/server/coverage/lcov-report/prettify.js new file mode 100644 index 0000000..b322523 --- /dev/null +++ b/server/coverage/lcov-report/prettify.js @@ -0,0 +1,2 @@ +/* eslint-disable */ +window.PR_SHOULD_USE_CONTINUATION=true;(function(){var h=["break,continue,do,else,for,if,return,while"];var u=[h,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"];var p=[u,"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"];var l=[p,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"];var x=[p,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"];var R=[x,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"];var r="all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes";var w=[p,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"];var s="caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END";var I=[h,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"];var f=[h,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"];var H=[h,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"];var A=[l,R,w,s+I,f,H];var e=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/;var C="str";var z="kwd";var j="com";var O="typ";var G="lit";var L="pun";var F="pln";var m="tag";var E="dec";var J="src";var P="atn";var n="atv";var N="nocode";var M="(?:^^\\.?|[+-]|\\!|\\!=|\\!==|\\#|\\%|\\%=|&|&&|&&=|&=|\\(|\\*|\\*=|\\+=|\\,|\\-=|\\->|\\/|\\/=|:|::|\\;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|\\?|\\@|\\[|\\^|\\^=|\\^\\^|\\^\\^=|\\{|\\||\\|=|\\|\\||\\|\\|=|\\~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*";function k(Z){var ad=0;var S=false;var ac=false;for(var V=0,U=Z.length;V122)){if(!(al<65||ag>90)){af.push([Math.max(65,ag)|32,Math.min(al,90)|32])}if(!(al<97||ag>122)){af.push([Math.max(97,ag)&~32,Math.min(al,122)&~32])}}}}af.sort(function(av,au){return(av[0]-au[0])||(au[1]-av[1])});var ai=[];var ap=[NaN,NaN];for(var ar=0;arat[0]){if(at[1]+1>at[0]){an.push("-")}an.push(T(at[1]))}}an.push("]");return an.join("")}function W(al){var aj=al.source.match(new RegExp("(?:\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]|\\\\u[A-Fa-f0-9]{4}|\\\\x[A-Fa-f0-9]{2}|\\\\[0-9]+|\\\\[^ux0-9]|\\(\\?[:!=]|[\\(\\)\\^]|[^\\x5B\\x5C\\(\\)\\^]+)","g"));var ah=aj.length;var an=[];for(var ak=0,am=0;ak=2&&ai==="["){aj[ak]=X(ag)}else{if(ai!=="\\"){aj[ak]=ag.replace(/[a-zA-Z]/g,function(ao){var ap=ao.charCodeAt(0);return"["+String.fromCharCode(ap&~32,ap|32)+"]"})}}}}return aj.join("")}var aa=[];for(var V=0,U=Z.length;V=0;){S[ac.charAt(ae)]=Y}}var af=Y[1];var aa=""+af;if(!ag.hasOwnProperty(aa)){ah.push(af);ag[aa]=null}}ah.push(/[\0-\uffff]/);V=k(ah)})();var X=T.length;var W=function(ah){var Z=ah.sourceCode,Y=ah.basePos;var ad=[Y,F];var af=0;var an=Z.match(V)||[];var aj={};for(var ae=0,aq=an.length;ae=5&&"lang-"===ap.substring(0,5);if(am&&!(ai&&typeof ai[1]==="string")){am=false;ap=J}if(!am){aj[ag]=ap}}var ab=af;af+=ag.length;if(!am){ad.push(Y+ab,ap)}else{var al=ai[1];var ak=ag.indexOf(al);var ac=ak+al.length;if(ai[2]){ac=ag.length-ai[2].length;ak=ac-al.length}var ar=ap.substring(5);B(Y+ab,ag.substring(0,ak),W,ad);B(Y+ab+ak,al,q(ar,al),ad);B(Y+ab+ac,ag.substring(ac),W,ad)}}ah.decorations=ad};return W}function i(T){var W=[],S=[];if(T.tripleQuotedStrings){W.push([C,/^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,null,"'\""])}else{if(T.multiLineStrings){W.push([C,/^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,null,"'\"`"])}else{W.push([C,/^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,null,"\"'"])}}if(T.verbatimStrings){S.push([C,/^@\"(?:[^\"]|\"\")*(?:\"|$)/,null])}var Y=T.hashComments;if(Y){if(T.cStyleComments){if(Y>1){W.push([j,/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,null,"#"])}else{W.push([j,/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\r\n]*)/,null,"#"])}S.push([C,/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,null])}else{W.push([j,/^#[^\r\n]*/,null,"#"])}}if(T.cStyleComments){S.push([j,/^\/\/[^\r\n]*/,null]);S.push([j,/^\/\*[\s\S]*?(?:\*\/|$)/,null])}if(T.regexLiterals){var X=("/(?=[^/*])(?:[^/\\x5B\\x5C]|\\x5C[\\s\\S]|\\x5B(?:[^\\x5C\\x5D]|\\x5C[\\s\\S])*(?:\\x5D|$))+/");S.push(["lang-regex",new RegExp("^"+M+"("+X+")")])}var V=T.types;if(V){S.push([O,V])}var U=(""+T.keywords).replace(/^ | $/g,"");if(U.length){S.push([z,new RegExp("^(?:"+U.replace(/[\s,]+/g,"|")+")\\b"),null])}W.push([F,/^\s+/,null," \r\n\t\xA0"]);S.push([G,/^@[a-z_$][a-z_$@0-9]*/i,null],[O,/^(?:[@_]?[A-Z]+[a-z][A-Za-z_$@0-9]*|\w+_t\b)/,null],[F,/^[a-z_$][a-z_$@0-9]*/i,null],[G,new RegExp("^(?:0x[a-f0-9]+|(?:\\d(?:_\\d+)*\\d*(?:\\.\\d*)?|\\.\\d\\+)(?:e[+\\-]?\\d+)?)[a-z]*","i"),null,"0123456789"],[F,/^\\[\s\S]?/,null],[L,/^.[^\s\w\.$@\'\"\`\/\#\\]*/,null]);return g(W,S)}var K=i({keywords:A,hashComments:true,cStyleComments:true,multiLineStrings:true,regexLiterals:true});function Q(V,ag){var U=/(?:^|\s)nocode(?:\s|$)/;var ab=/\r\n?|\n/;var ac=V.ownerDocument;var S;if(V.currentStyle){S=V.currentStyle.whiteSpace}else{if(window.getComputedStyle){S=ac.defaultView.getComputedStyle(V,null).getPropertyValue("white-space")}}var Z=S&&"pre"===S.substring(0,3);var af=ac.createElement("LI");while(V.firstChild){af.appendChild(V.firstChild)}var W=[af];function ae(al){switch(al.nodeType){case 1:if(U.test(al.className)){break}if("BR"===al.nodeName){ad(al);if(al.parentNode){al.parentNode.removeChild(al)}}else{for(var an=al.firstChild;an;an=an.nextSibling){ae(an)}}break;case 3:case 4:if(Z){var am=al.nodeValue;var aj=am.match(ab);if(aj){var ai=am.substring(0,aj.index);al.nodeValue=ai;var ah=am.substring(aj.index+aj[0].length);if(ah){var ak=al.parentNode;ak.insertBefore(ac.createTextNode(ah),al.nextSibling)}ad(al);if(!ai){al.parentNode.removeChild(al)}}}break}}function ad(ak){while(!ak.nextSibling){ak=ak.parentNode;if(!ak){return}}function ai(al,ar){var aq=ar?al.cloneNode(false):al;var ao=al.parentNode;if(ao){var ap=ai(ao,1);var an=al.nextSibling;ap.appendChild(aq);for(var am=an;am;am=an){an=am.nextSibling;ap.appendChild(am)}}return aq}var ah=ai(ak.nextSibling,0);for(var aj;(aj=ah.parentNode)&&aj.nodeType===1;){ah=aj}W.push(ah)}for(var Y=0;Y=S){ah+=2}if(V>=ap){Z+=2}}}var t={};function c(U,V){for(var S=V.length;--S>=0;){var T=V[S];if(!t.hasOwnProperty(T)){t[T]=U}else{if(window.console){console.warn("cannot override language handler %s",T)}}}}function q(T,S){if(!(T&&t.hasOwnProperty(T))){T=/^\s*]*(?:>|$)/],[j,/^<\!--[\s\S]*?(?:-\->|$)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],[L,/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),["default-markup","htm","html","mxml","xhtml","xml","xsl"]);c(g([[F,/^[\s]+/,null," \t\r\n"],[n,/^(?:\"[^\"]*\"?|\'[^\']*\'?)/,null,"\"'"]],[[m,/^^<\/?[a-z](?:[\w.:-]*\w)?|\/?>$/i],[P,/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^>\'\"\s]*(?:[^>\'\"\s\/]|\/(?=\s)))/],[L,/^[=<>\/]+/],["lang-js",/^on\w+\s*=\s*\"([^\"]+)\"/i],["lang-js",/^on\w+\s*=\s*\'([^\']+)\'/i],["lang-js",/^on\w+\s*=\s*([^\"\'>\s]+)/i],["lang-css",/^style\s*=\s*\"([^\"]+)\"/i],["lang-css",/^style\s*=\s*\'([^\']+)\'/i],["lang-css",/^style\s*=\s*([^\"\'>\s]+)/i]]),["in.tag"]);c(g([],[[n,/^[\s\S]+/]]),["uq.val"]);c(i({keywords:l,hashComments:true,cStyleComments:true,types:e}),["c","cc","cpp","cxx","cyc","m"]);c(i({keywords:"null,true,false"}),["json"]);c(i({keywords:R,hashComments:true,cStyleComments:true,verbatimStrings:true,types:e}),["cs"]);c(i({keywords:x,cStyleComments:true}),["java"]);c(i({keywords:H,hashComments:true,multiLineStrings:true}),["bsh","csh","sh"]);c(i({keywords:I,hashComments:true,multiLineStrings:true,tripleQuotedStrings:true}),["cv","py"]);c(i({keywords:s,hashComments:true,multiLineStrings:true,regexLiterals:true}),["perl","pl","pm"]);c(i({keywords:f,hashComments:true,multiLineStrings:true,regexLiterals:true}),["rb"]);c(i({keywords:w,cStyleComments:true,regexLiterals:true}),["js"]);c(i({keywords:r,hashComments:3,cStyleComments:true,multilineStrings:true,tripleQuotedStrings:true,regexLiterals:true}),["coffee"]);c(g([],[[C,/^[\s\S]+/]]),["regex"]);function d(V){var U=V.langExtension;try{var S=a(V.sourceNode);var T=S.sourceCode;V.sourceCode=T;V.spans=S.spans;V.basePos=0;q(U,T)(V);D(V)}catch(W){if("console" in window){console.log(W&&W.stack?W.stack:W)}}}function y(W,V,U){var S=document.createElement("PRE");S.innerHTML=W;if(U){Q(S,U)}var T={langExtension:V,numberLines:U,sourceNode:S};d(T);return S.innerHTML}function b(ad){function Y(af){return document.getElementsByTagName(af)}var ac=[Y("pre"),Y("code"),Y("xmp")];var T=[];for(var aa=0;aa=0){var ah=ai.match(ab);var am;if(!ah&&(am=o(aj))&&"CODE"===am.tagName){ah=am.className.match(ab)}if(ah){ah=ah[1]}var al=false;for(var ak=aj.parentNode;ak;ak=ak.parentNode){if((ak.tagName==="pre"||ak.tagName==="code"||ak.tagName==="xmp")&&ak.className&&ak.className.indexOf("prettyprint")>=0){al=true;break}}if(!al){var af=aj.className.match(/\blinenums\b(?::(\d+))?/);af=af?af[1]&&af[1].length?+af[1]:true:false;if(af){Q(aj,af)}S={langExtension:ah,sourceNode:aj,numberLines:af};d(S)}}}if(X]*(?:>|$)/],[PR.PR_COMMENT,/^<\!--[\s\S]*?(?:-\->|$)/],[PR.PR_PUNCTUATION,/^(?:<[%?]|[%?]>)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],["lang-",/^]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-handlebars",/^]*type\s*=\s*['"]?text\/x-handlebars-template['"]?\b[^>]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-js",/^]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i],[PR.PR_DECLARATION,/^{{[#^>/]?\s*[\w.][^}]*}}/],[PR.PR_DECLARATION,/^{{&?\s*[\w.][^}]*}}/],[PR.PR_DECLARATION,/^{{{>?\s*[\w.][^}]*}}}/],[PR.PR_COMMENT,/^{{![^}]*}}/]]),["handlebars","hbs"]);PR.registerLangHandler(PR.createSimpleLexer([[PR.PR_PLAIN,/^[ \t\r\n\f]+/,null," \t\r\n\f"]],[[PR.PR_STRING,/^\"(?:[^\n\r\f\\\"]|\\(?:\r\n?|\n|\f)|\\[\s\S])*\"/,null],[PR.PR_STRING,/^\'(?:[^\n\r\f\\\']|\\(?:\r\n?|\n|\f)|\\[\s\S])*\'/,null],["lang-css-str",/^url\(([^\)\"\']*)\)/i],[PR.PR_KEYWORD,/^(?:url|rgb|\!important|@import|@page|@media|@charset|inherit)(?=[^\-\w]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|(?:\\[0-9a-f]+ ?))(?:[_a-z0-9\-]|\\(?:\\[0-9a-f]+ ?))*)\s*:/i],[PR.PR_COMMENT,/^\/\*[^*]*\*+(?:[^\/*][^*]*\*+)*\//],[PR.PR_COMMENT,/^(?:)/],[PR.PR_LITERAL,/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],[PR.PR_LITERAL,/^#(?:[0-9a-f]{3}){1,2}/i],[PR.PR_PLAIN,/^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i],[PR.PR_PUNCTUATION,/^[^\s\w\'\"]+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[[PR.PR_KEYWORD,/^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[[PR.PR_STRING,/^[^\)\"\']+/]]),["css-str"]); diff --git a/server/coverage/lcov-report/server/app.js.html b/server/coverage/lcov-report/server/app.js.html new file mode 100644 index 0000000..a0ebbe4 --- /dev/null +++ b/server/coverage/lcov-report/server/app.js.html @@ -0,0 +1,136 @@ + + + + + + Code coverage report for server/app.js + + + + + + + + + +
+
+

All files / server app.js

+
+ +
+ 100% + Statements + 10/10 +
+ + +
+ 50% + Branches + 1/2 +
+ + +
+ 100% + Functions + 0/0 +
+ + +
+ 100% + Lines + 10/10 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +181x +1x +  +  +1x +1x +1x +  +1x +  +1x +  +1x +  +1x +  +  +1x
Eif(process.env.NODE_ENV !== "production"){
+    require('dotenv').config()  //selalu ditulis di awal app.js & di pakai tahap dev
+  }
+ 
+const express = require('express');
+const app = express();
+const cors = require('cors'); //DISTRIBUTION
+ 
+app.use(cors()) // DEPLOY
+ 
+app.use(express.urlencoded({ extended: true }));
+ 
+app.use(express.json());
+ 
+app.use(require("./routes"));
+ 
+ 
+module.exports = app;
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/server/coverage/lcov-report/server/controllers/CommentController.js.html b/server/coverage/lcov-report/server/controllers/CommentController.js.html new file mode 100644 index 0000000..9f951bd --- /dev/null +++ b/server/coverage/lcov-report/server/controllers/CommentController.js.html @@ -0,0 +1,184 @@ + + + + + + Code coverage report for server/controllers/CommentController.js + + + + + + + + + +
+
+

All files / server/controllers CommentController.js

+
+ +
+ 31.25% + Statements + 5/16 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 33.33% + Functions + 1/3 +
+ + +
+ 31.25% + Lines + 5/16 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +341x +  +1x +  +  +1x +1x +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
const { Comment } = require("../models");
+ 
+module.exports = class MailController {
+ 
+    static async addComment(req, res, next) {
+        try {
+            const comment = await Comment.create({ ...req.body, userId: req.user.id })
+            res.status(201).json(comment);
+        } catch (error) {
+            next(error); 
+        }
+    }
+ 
+    static async editComment(req, res, next) {
+        try {
+            let comment = await Comment.findByPk(req.params.id);
+            let update = await comment.update(req.body, { returning: true });
+            res.status(200).json(update);
+        } catch (error) {
+            next(error);
+        }
+    }
+ 
+    static async deleteComment(req, res, next) { 
+        try {
+            let comment = await Comment.findByPk(req.params.id);
+            await comment.destroy();
+            res.status(200).json({ message: `Comment success to delete` });
+        } catch (error) {
+            next(error);
+        }
+    }
+ 
+}
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/server/coverage/lcov-report/server/controllers/MailController.js.html b/server/coverage/lcov-report/server/controllers/MailController.js.html new file mode 100644 index 0000000..0560c74 --- /dev/null +++ b/server/coverage/lcov-report/server/controllers/MailController.js.html @@ -0,0 +1,199 @@ + + + + + + Code coverage report for server/controllers/MailController.js + + + + + + + + + +
+
+

All files / server/controllers MailController.js

+
+ +
+ 16.66% + Statements + 2/12 +
+ + +
+ 0% + Branches + 0/5 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 18.18% + Lines + 2/11 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +391x +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
const nodemailer = require('nodemailer');
+ 
+module.exports = class MailController {
+ 
+    static async sendMail(req, res, next) {
+        try {
+            const { username, email, message } = req.body;
+ 
+            // console.log(username, email, message)
+ 
+            if(!username || !email || !message) throw ({name: "IncompleteData", message: "Please fill the required form!"})
+ 
+            const transporter = nodemailer.createTransport({
+                service: 'gmail',
+                auth: {
+                    user: `${process.env.E_SENDER}`,
+                    pass: `${process.env.E_PASS}`,
+                },
+            });
+ 
+            const mailOptions = {
+                from: `${email}`,
+                to: `${process.env.E_RECEIVER}`,
+                subject: `New Message from our dearest ViCYTOr - ${username},`,
+                text: `From: ${username} \nEmail: ${email} \nMessage: ${message}`,
+            };
+ 
+            await transporter.sendMail(mailOptions);
+ 
+            res.status(200).json({ message: "Your message sent successfully" });
+ 
+        } catch (error) {
+            next(error);
+            console.log(error)
+        }
+    }
+ 
+ 
+}
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/server/coverage/lcov-report/server/controllers/RecipeController.js.html b/server/coverage/lcov-report/server/controllers/RecipeController.js.html new file mode 100644 index 0000000..3551e0a --- /dev/null +++ b/server/coverage/lcov-report/server/controllers/RecipeController.js.html @@ -0,0 +1,436 @@ + + + + + + Code coverage report for server/controllers/RecipeController.js + + + + + + + + + +
+
+

All files / server/controllers RecipeController.js

+
+ +
+ 10.34% + Statements + 3/29 +
+ + +
+ 0% + Branches + 0/6 +
+ + +
+ 0% + Functions + 0/5 +
+ + +
+ 10.34% + Lines + 3/29 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +1181x +1x +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
const axios = require('axios');
+const { Recipe } = require("../models");  //DEVELOPMENT
+ 
+module.exports = class RecipeController {
+ 
+  static async recipes(req, res, next) {
+ 
+    const { search, filter, page } = req.query;
+ 
+    const options = {
+      method: 'GET',
+      url: 'https://low-carb-recipes.p.rapidapi.com/search',
+      params: {
+        maxPrepareTime: '10',
+        maxCookTime: '20',
+        maxCalories: '500',
+        maxNetCarbs: '5',
+        maxSugar: '3',
+        maxAddedSugar: '0',
+        limit: '20'
+      },
+      headers: {
+        'X-RapidAPI-Key': `${process.env.RECIPES_API_KEY}`,
+        'X-RapidAPI-Host': 'low-carb-recipes.p.rapidapi.com'
+      }
+    };
+ 
+    if (search) {
+      options.params.name = search;
+    }
+ 
+    if (filter) {
+      options.params.includeIngredients = filter.slice(',').join(';');
+      console.log(options.params.includeIngredients)
+    }
+ 
+    try {
+      const response = await axios.request(options);
+      const recipes = response.data;
+      const mappedRecipes = recipes.map((recipe, index) => {
+        return {
+          id: recipe.id,
+          name: recipe.name,
+          description: recipe.description,
+          prepareTime: `${recipe.prepareTime} minutes`,
+          cookTime: `${recipe.cookTime} minutes`,
+          ingredients: recipe.ingredients.map(ingredient => `${ingredient.name} ${ingredient.servingSize.desc}`),
+          steps: recipe.steps,
+          nutrients: {
+            caloriesKCal: recipe.nutrients.caloriesKCal,
+            totalCarbs: recipe.nutrients.totalCarbs,
+            sugar: recipe.nutrients.sugar,
+            protein: recipe.nutrients.protein,
+            fat: recipe.nutrients.fat,
+            cholesterol: recipe.nutrients.cholesterol,
+            alcohol: recipe.nutrients.alcohol,
+            gluten: recipe.nutrients.gluten,
+          },
+          image: recipe.image,
+        }
+      })
+ 
+      const offset = !Number(page) ? 0 : 8 * (page - 1)
+      const newPage = offset + 8
+      const result = mappedRecipes.slice(offset, newPage)
+ 
+      res.status(200).json(result);
+    } catch (error) {
+      next(error);
+    }
+  }
+ 
+ 
+  static async recipeById(req, res, next) {
+ 
+    const {id} = req.params
+ 
+    const options = {
+      method: 'GET',
+      url: `https://low-carb-recipes.p.rapidapi.com/recipes/${id}`,
+      headers: {
+        'X-RapidAPI-Key': `${process.env.RECIPES_API_KEY}`,
+        'X-RapidAPI-Host': 'low-carb-recipes.p.rapidapi.com'
+      }
+    };
+ 
+    try {
+      const response = await axios.request(options);
+      const recipe = response.data;
+ 
+      res.status(200).json({
+        id: recipe.id,
+        name: recipe.name,
+        description: recipe.description,
+        prepareTime: `${recipe.prepareTime} minutes`,
+        cookTime: `${recipe.cookTime} minutes`,
+        ingredients: recipe.ingredients.map(ingredient => `${ingredient.name} ${ingredient.servingSize.desc}`),
+        steps: recipe.steps,
+        nutrients: {
+          caloriesKCal: recipe.nutrients.caloriesKCal,
+          totalCarbs: recipe.nutrients.totalCarbs,
+          sugar: recipe.nutrients.sugar,
+          protein: recipe.nutrients.protein,
+          fat: recipe.nutrients.fat,
+          cholesterol: recipe.nutrients.cholesterol,
+          alcohol: recipe.nutrients.alcohol,
+          gluten: recipe.nutrients.gluten,
+        },
+        image: recipe.image,
+      });
+ 
+    } catch (error) {
+      next(error);
+    }
+  }
+ 
+ 
+}
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/server/coverage/lcov-report/server/controllers/UserController.js.html b/server/coverage/lcov-report/server/controllers/UserController.js.html new file mode 100644 index 0000000..0843573 --- /dev/null +++ b/server/coverage/lcov-report/server/controllers/UserController.js.html @@ -0,0 +1,358 @@ + + + + + + Code coverage report for server/controllers/UserController.js + + + + + + + + + +
+
+

All files / server/controllers UserController.js

+
+ +
+ 15% + Statements + 6/40 +
+ + +
+ 0% + Branches + 0/10 +
+ + +
+ 0% + Functions + 0/3 +
+ + +
+ 15% + Lines + 6/40 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +921x +1x +1x +1x +1x +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
const { comparePassword } = require("../helpers/bcryptjs");
+const { signToken } = require("../helpers/jwt");
+const { User } = require("../models");
+const { OAuth2Client } = require('google-auth-library');
+const client = new OAuth2Client();
+ 
+ 
+module.exports = class UserController {
+ 
+    static async register(req, res, next) {
+        try {
+            const { username, email, password } = req.body
+            const user = await User.create({ username, email, password });
+ 
+            res.status(201).json({
+                "id": user.id,
+                "username": user.username
+            });
+ 
+        } catch (error) {
+            next(error);
+        }
+    }
+ 
+ 
+    static async login(req, res, next) {
+        try {
+            const { email, password } = req.body;
+ 
+            if (!email) {
+                res.status(400).json({ message: "Error invalid email or Password" });
+                return;
+            }
+ 
+            if (!password) {
+                res.status(400).json({ message: "Error invalid email or Password" });
+                return;
+            }
+ 
+            const user = await User.findOne({ where: { email } });
+            if (!user) {
+                next({ name: 'Unauthenticated', message: "User not found or Password not matched" });
+                return;
+            }
+ 
+            const isValidPassword = comparePassword(password, user.password);
+            if (!isValidPassword) {
+                next({ name: 'Unauthenticated', message: "User not found or Password not matched" });
+                return;
+            }
+ 
+            const access_token = signToken({ id: user.id });
+            res.status(200).json({ access_token });
+ 
+        } catch (error) {
+            console.log(error)
+            next(error);
+        }
+    }
+ 
+ 
+    static async googleLogin(req, res, next) {
+        console.log(req.headers.g_token)
+ 
+        try {
+            const ticket = await client.verifyIdToken({
+                idToken: req.headers.g_token,
+                audience: process.env.G_CLIENT_ID,
+            });
+            const payload = ticket.getPayload();
+ 
+            console.log(payload)
+ 
+            const [user, isNewRecord] = await User.findOrCreate({
+                where: {
+                    email: payload.email
+                },
+                defaults: {
+                    username: payload.name,
+                    password: String(Math.random())
+                }
+            })
+ 
+            const access_token = signToken({ id: user.id });
+            res.status(isNewRecord ? 201 : 200).json({ access_token });
+ 
+        } catch (error) {
+            next(error);
+        }
+    }
+ 
+}
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/server/coverage/lcov-report/server/controllers/index.html b/server/coverage/lcov-report/server/controllers/index.html new file mode 100644 index 0000000..47ea815 --- /dev/null +++ b/server/coverage/lcov-report/server/controllers/index.html @@ -0,0 +1,161 @@ + + + + + + Code coverage report for server/controllers + + + + + + + + + +
+
+

All files server/controllers

+
+ +
+ 16.49% + Statements + 16/97 +
+ + +
+ 0% + Branches + 0/21 +
+ + +
+ 8.33% + Functions + 1/12 +
+ + +
+ 16.66% + Lines + 16/96 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
CommentController.js +
+
31.25%5/16100%0/033.33%1/331.25%5/16
MailController.js +
+
16.66%2/120%0/50%0/118.18%2/11
RecipeController.js +
+
10.34%3/290%0/60%0/510.34%3/29
UserController.js +
+
15%6/400%0/100%0/315%6/40
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/server/coverage/lcov-report/server/helpers/bcryptjs.js.html b/server/coverage/lcov-report/server/helpers/bcryptjs.js.html new file mode 100644 index 0000000..b67ced1 --- /dev/null +++ b/server/coverage/lcov-report/server/helpers/bcryptjs.js.html @@ -0,0 +1,121 @@ + + + + + + Code coverage report for server/helpers/bcryptjs.js + + + + + + + + + +
+
+

All files / server/helpers bcryptjs.js

+
+ +
+ 85.71% + Statements + 6/7 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 50% + Functions + 1/2 +
+ + +
+ 85.71% + Lines + 6/7 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +131x +  +1x +1x +1x +  +  +1x +  +  +  +  +1x
const bcrypt = require('bcryptjs');
+ 
+const hashPassword = (password) => {
+    const salt = bcrypt.genSaltSync(10);
+    return bcrypt.hashSync(password, salt);
+}
+ 
+const comparePassword = (password, hashedPassword) => {
+    return bcrypt.compareSync(password, hashedPassword);
+}
+ 
+ 
+module.exports = {hashPassword, comparePassword};
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/server/coverage/lcov-report/server/helpers/index.html b/server/coverage/lcov-report/server/helpers/index.html new file mode 100644 index 0000000..6cda12c --- /dev/null +++ b/server/coverage/lcov-report/server/helpers/index.html @@ -0,0 +1,131 @@ + + + + + + Code coverage report for server/helpers + + + + + + + + + +
+
+

All files server/helpers

+
+ +
+ 92.85% + Statements + 13/14 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 75% + Functions + 3/4 +
+ + +
+ 92.85% + Lines + 13/14 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
bcryptjs.js +
+
85.71%6/7100%0/050%1/285.71%6/7
jwt.js +
+
100%7/7100%0/0100%2/2100%7/7
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/server/coverage/lcov-report/server/helpers/jwt.js.html b/server/coverage/lcov-report/server/helpers/jwt.js.html new file mode 100644 index 0000000..0a49d68 --- /dev/null +++ b/server/coverage/lcov-report/server/helpers/jwt.js.html @@ -0,0 +1,124 @@ + + + + + + Code coverage report for server/helpers/jwt.js + + + + + + + + + +
+
+

All files / server/helpers jwt.js

+
+ +
+ 100% + Statements + 7/7 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 100% + Functions + 2/2 +
+ + +
+ 100% + Lines + 7/7 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +141x +  +1x +  +1x +1x +  +  +1x +2x +  +  +  +1x
const jwt = require('jsonwebtoken');
+ 
+const JWT_SECRET = process.env.JWT_SECRET;
+ 
+const signToken = (payload) => {
+    return jwt.sign(payload, JWT_SECRET);
+}
+ 
+const verifyToken = (token) => {
+    return jwt.verify(token, JWT_SECRET);
+}
+ 
+ 
+module.exports = {signToken, verifyToken};
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/server/coverage/lcov-report/server/index.html b/server/coverage/lcov-report/server/index.html new file mode 100644 index 0000000..a7746d7 --- /dev/null +++ b/server/coverage/lcov-report/server/index.html @@ -0,0 +1,116 @@ + + + + + + Code coverage report for server + + + + + + + + + +
+
+

All files server

+
+ +
+ 100% + Statements + 10/10 +
+ + +
+ 50% + Branches + 1/2 +
+ + +
+ 100% + Functions + 0/0 +
+ + +
+ 100% + Lines + 10/10 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
app.js +
+
100%10/1050%1/2100%0/0100%10/10
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/server/coverage/lcov-report/server/middlewares/authentication.js.html b/server/coverage/lcov-report/server/middlewares/authentication.js.html new file mode 100644 index 0000000..988ae3b --- /dev/null +++ b/server/coverage/lcov-report/server/middlewares/authentication.js.html @@ -0,0 +1,169 @@ + + + + + + Code coverage report for server/middlewares/authentication.js + + + + + + + + + +
+
+

All files / server/middlewares authentication.js

+
+ +
+ 93.75% + Statements + 15/16 +
+ + +
+ 75% + Branches + 3/4 +
+ + +
+ 100% + Functions + 1/1 +
+ + +
+ 100% + Lines + 14/14 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +291x +1x +  +  +3x +3x +  +3x +  +2x +  +1x +  +1x +  +1x +  +  +  +1x +1x +  +  +2x +2x +  +  +  +1x
const { User } = require("../models");
+const { verifyToken } = require("../helpers/jwt");
+ 
+async function authentication(req, res, next){
+    try {
+        let access_token = req.headers.authorization;
+ 
+        if(!access_token) throw ({name: "Unauthenticated"});   
+ 
+        const { id } = verifyToken(access_token.slice(7));
+ 
+        let user = await User.findByPk(id);
+ 
+        Iif(!user) throw ({name: "Unauthenticated"});  
+ 
+        req.user = {
+            id: user.id
+        }
+ 
+        console.log(req.user)
+        next();
+ 
+    } catch (error) {
+        next(error);
+        console.log(error)
+    }
+}
+ 
+module.exports = authentication
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/server/coverage/lcov-report/server/middlewares/authorization.js.html b/server/coverage/lcov-report/server/middlewares/authorization.js.html new file mode 100644 index 0000000..2cfe1c8 --- /dev/null +++ b/server/coverage/lcov-report/server/middlewares/authorization.js.html @@ -0,0 +1,145 @@ + + + + + + Code coverage report for server/middlewares/authorization.js + + + + + + + + + +
+
+

All files / server/middlewares authorization.js

+
+ +
+ 20% + Statements + 2/10 +
+ + +
+ 0% + Branches + 0/4 +
+ + +
+ 0% + Functions + 0/1 +
+ + +
+ 22.22% + Lines + 2/9 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +211x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x
const {Comment} = require("../models");
+ 
+async function authorization(req, res, next) {
+    try {
+        let comment = await Comment.findByPk(req.params.id);
+ 
+        if(!comment) throw ({name: "NotFound"})
+ 
+        if(comment.userId !== req.user.id){
+            throw {name: "Forbidden"}
+        }
+ 
+        next();
+ 
+    } catch (error) {
+        next(error);
+    }
+}
+ 
+ 
+module.exports = authorization;
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/server/coverage/lcov-report/server/middlewares/errorHandler.js.html b/server/coverage/lcov-report/server/middlewares/errorHandler.js.html new file mode 100644 index 0000000..4dd95de --- /dev/null +++ b/server/coverage/lcov-report/server/middlewares/errorHandler.js.html @@ -0,0 +1,187 @@ + + + + + + Code coverage report for server/middlewares/errorHandler.js + + + + + + + + + +
+
+

All files / server/middlewares errorHandler.js

+
+ +
+ 25% + Statements + 4/16 +
+ + +
+ 36.36% + Branches + 4/11 +
+ + +
+ 100% + Functions + 1/1 +
+ + +
+ 25% + Lines + 4/16 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +351x +  +2x +  +  +  +  +  +  +  +  +  +  +  +2x +2x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  + 
module.exports = function errorHandler(error, req, res, next) {
+ 
+    switch (error.name) {
+        case "SequelizeValidationError":
+        case "SequelizeUniqueConstraintError":
+            res.status(400).json({ message: error.errors[0].message });
+            break;
+ 
+        case "IncompleteData":
+            res.status(400).json({ message: error.message });
+            break;
+ 
+        case "JsonWebTokenError":
+        case "Unauthenticated":
+            res.status(401).json({ message: error.message ?? "Unauthenticated" });
+            break;
+ 
+        case "Forbidden":
+            res.status(403).json({ message: "You are not authorized" });
+            break;
+ 
+        case "NotFound":
+            res.status(404).json({ message: "Comment not found" });
+            break;
+ 
+        case "AxiosError":
+            res.status(404).json({ message: "Recipe not found" });
+            break;
+ 
+        default:
+            res.status(500).json({ message: "Internal Server Error" });
+            break;
+    }
+ 
+}
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/server/coverage/lcov-report/server/middlewares/index.html b/server/coverage/lcov-report/server/middlewares/index.html new file mode 100644 index 0000000..79cfbd3 --- /dev/null +++ b/server/coverage/lcov-report/server/middlewares/index.html @@ -0,0 +1,146 @@ + + + + + + Code coverage report for server/middlewares + + + + + + + + + +
+
+

All files server/middlewares

+
+ +
+ 50% + Statements + 21/42 +
+ + +
+ 36.84% + Branches + 7/19 +
+ + +
+ 66.66% + Functions + 2/3 +
+ + +
+ 51.28% + Lines + 20/39 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
authentication.js +
+
93.75%15/1675%3/4100%1/1100%14/14
authorization.js +
+
20%2/100%0/40%0/122.22%2/9
errorHandler.js +
+
25%4/1636.36%4/11100%1/125%4/16
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/server/coverage/lcov-report/server/models/comment.js.html b/server/coverage/lcov-report/server/models/comment.js.html new file mode 100644 index 0000000..4185fb4 --- /dev/null +++ b/server/coverage/lcov-report/server/models/comment.js.html @@ -0,0 +1,157 @@ + + + + + + Code coverage report for server/models/comment.js + + + + + + + + + +
+
+

All files / server/models comment.js

+
+ +
+ 100% + Statements + 5/5 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 100% + Functions + 2/2 +
+ + +
+ 100% + Lines + 5/5 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25  +  +  +1x +1x +  +  +  +  +  +  +  +  +1x +  +  +1x +  +  +  +  +  +  +1x + 
'use strict';
+const {
+  Model
+} = require('sequelize');
+module.exports = (sequelize, DataTypes) => {
+  class Comment extends Model {
+    /**
+     * Helper method for defining associations.
+     * This method is not a part of Sequelize lifecycle.
+     * The `models/index` file will call this method automatically.
+     */
+    static associate(models) {
+      // define association here
+      Comment.belongsTo(models.User, {foreignKey: "userId"})
+    }
+  }
+  Comment.init({
+    description: DataTypes.STRING,
+    userId: DataTypes.INTEGER
+  }, {
+    sequelize,
+    modelName: 'Comment',
+  });
+  return Comment;
+};
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/server/coverage/lcov-report/server/models/index.html b/server/coverage/lcov-report/server/models/index.html new file mode 100644 index 0000000..e11f880 --- /dev/null +++ b/server/coverage/lcov-report/server/models/index.html @@ -0,0 +1,161 @@ + + + + + + Code coverage report for server/models + + + + + + + + + +
+
+

All files server/models

+
+ +
+ 97.36% + Statements + 37/38 +
+ + +
+ 70% + Branches + 7/10 +
+ + +
+ 100% + Functions + 10/10 +
+ + +
+ 97.36% + Lines + 37/38 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
comment.js +
+
100%5/5100%0/0100%2/2100%5/5
index.js +
+
95.23%20/2170%7/10100%3/395.23%20/21
recipe.js +
+
100%4/4100%0/0100%2/2100%4/4
user.js +
+
100%8/8100%0/0100%3/3100%8/8
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/server/coverage/lcov-report/server/models/index.js.html b/server/coverage/lcov-report/server/models/index.js.html new file mode 100644 index 0000000..267ec35 --- /dev/null +++ b/server/coverage/lcov-report/server/models/index.js.html @@ -0,0 +1,214 @@ + + + + + + Code coverage report for server/models/index.js + + + + + + + + + +
+
+

All files / server/models index.js

+
+ +
+ 95.23% + Statements + 20/21 +
+ + +
+ 70% + Branches + 7/10 +
+ + +
+ 100% + Functions + 3/3 +
+ + +
+ 95.23% + Lines + 20/21 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44  +  +1x +1x +1x +1x +1x +1x +1x +1x +  +  +1x +  +  +1x +  +  +1x +  +  +4x +  +  +  +  +  +  +  +3x +3x +  +  +1x +3x +3x +  +  +  +1x +1x +  +1x + 
'use strict';
+ 
+const fs = require('fs');
+const path = require('path');
+const Sequelize = require('sequelize');
+const process = require('process');
+const basename = path.basename(__filename);
+const env = process.env.NODE_ENV || 'development';
+const config = require(__dirname + '/../config/config.json')[env];
+const db = {};
+ 
+let sequelize;
+Iif (config.use_env_variable) {
+  sequelize = new Sequelize(process.env[config.use_env_variable], config);
+} else {
+  sequelize = new Sequelize(config.database, config.username, config.password, config);
+}
+ 
+fs
+  .readdirSync(__dirname)
+  .filter(file => {
+    return (
+      file.indexOf('.') !== 0 &&
+      file !== basename &&
+      file.slice(-3) === '.js' &&
+      file.indexOf('.test.js') === -1
+    );
+  })
+  .forEach(file => {
+    const model = require(path.join(__dirname, file))(sequelize, Sequelize.DataTypes);
+    db[model.name] = model;
+  });
+ 
+Object.keys(db).forEach(modelName => {
+  Eif (db[modelName].associate) {
+    db[modelName].associate(db);
+  }
+});
+ 
+db.sequelize = sequelize;
+db.Sequelize = Sequelize;
+ 
+module.exports = db;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/server/coverage/lcov-report/server/models/recipe.js.html b/server/coverage/lcov-report/server/models/recipe.js.html new file mode 100644 index 0000000..83084e9 --- /dev/null +++ b/server/coverage/lcov-report/server/models/recipe.js.html @@ -0,0 +1,172 @@ + + + + + + Code coverage report for server/models/recipe.js + + + + + + + + + +
+
+

All files / server/models recipe.js

+
+ +
+ 100% + Statements + 4/4 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 100% + Functions + 2/2 +
+ + +
+ 100% + Lines + 4/4 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30  +  +  +1x +1x +  +  +  +  +  +  +  +  +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +1x + 
'use strict';
+const {
+  Model
+} = require('sequelize');
+module.exports = (sequelize, DataTypes) => {
+  class Recipe extends Model {
+    /**
+     * Helper method for defining associations.
+     * This method is not a part of Sequelize lifecycle.
+     * The `models/index` file will call this method automatically.
+     */
+    static associate(models) {
+      // define association here
+    }
+  }
+  Recipe.init({
+    name: DataTypes.STRING,
+    description: DataTypes.STRING(10000),
+    prepareTime: DataTypes.STRING,
+    cookTime: DataTypes.STRING,
+    ingredients: DataTypes.ARRAY(DataTypes.STRING(20000)),
+    steps: DataTypes.ARRAY(DataTypes.STRING(20000)),
+    nutrients: DataTypes.JSON,
+    image: DataTypes.STRING
+  }, {
+    sequelize,
+    modelName: 'Recipe',
+  });
+  return Recipe;
+};
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/server/coverage/lcov-report/server/models/user.js.html b/server/coverage/lcov-report/server/models/user.js.html new file mode 100644 index 0000000..c5dd04f --- /dev/null +++ b/server/coverage/lcov-report/server/models/user.js.html @@ -0,0 +1,277 @@ + + + + + + Code coverage report for server/models/user.js + + + + + + + + + +
+
+

All files / server/models user.js

+
+ +
+ 100% + Statements + 8/8 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 100% + Functions + 3/3 +
+ + +
+ 100% + Lines + 8/8 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65  +1x +1x +  +  +1x +  +  +  +  +  +  +  +  +1x +  +  +1x +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +  +1x +1x +  +  +1x + 
'use strict';
+const { Model } = require('sequelize');
+const { hashPassword } = require('../helpers/bcryptjs');
+ 
+ 
+module.exports = (sequelize, DataTypes) => {
+  class User extends Model {
+    /**
+     * Helper method for defining associations.
+     * This method is not a part of Sequelize lifecycle.
+     * The `models/index` file will call this method automatically.
+     */
+    static associate(models) {
+      // define association here
+      User.hasMany(models.Comment, {foreignKey: 'userId'});
+    }
+  }
+  User.init({
+ 
+    username: {
+      type: DataTypes.STRING,
+      defaultValue: "ViCYTOr"
+    },
+ 
+    email: {
+      type: DataTypes.STRING,
+      allowNull: false,
+      unique: true,
+      validate: {
+        notNull: {
+          msg: "Email cannot be Empty!"
+        },
+        notEmpty: {
+          msg: "Email cannot be Empty!"
+        },
+        isEmail: {
+          msg: "Email should be written in email format!"
+        }
+      }
+    },
+ 
+    password: {
+      type: DataTypes.STRING,
+      allowNull: false,
+      validate: {
+        notNull: {
+          msg: "Password cannot be Empty!"
+        },
+        notEmpty: {
+          msg: "Password cannot be Empty!"
+        }
+      }
+    }
+ 
+  }, {
+    sequelize,
+    modelName: 'User',
+  });
+ 
+  User.beforeCreate((user) => {
+    user.password = hashPassword(user.password);
+  })
+ 
+  return User;
+};
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/server/coverage/lcov-report/server/routes/index.html b/server/coverage/lcov-report/server/routes/index.html new file mode 100644 index 0000000..731ff09 --- /dev/null +++ b/server/coverage/lcov-report/server/routes/index.html @@ -0,0 +1,116 @@ + + + + + + Code coverage report for server/routes + + + + + + + + + +
+
+

All files server/routes

+
+ +
+ 100% + Statements + 20/20 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 100% + Functions + 0/0 +
+ + +
+ 100% + Lines + 20/20 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
index.js +
+
100%20/20100%0/0100%0/0100%20/20
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/server/coverage/lcov-report/server/routes/index.js.html b/server/coverage/lcov-report/server/routes/index.js.html new file mode 100644 index 0000000..bf857c0 --- /dev/null +++ b/server/coverage/lcov-report/server/routes/index.js.html @@ -0,0 +1,208 @@ + + + + + + Code coverage report for server/routes/index.js + + + + + + + + + +
+
+

All files / server/routes index.js

+
+ +
+ 100% + Statements + 20/20 +
+ + +
+ 100% + Branches + 0/0 +
+ + +
+ 100% + Functions + 0/0 +
+ + +
+ 100% + Lines + 20/20 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +421x +1x +1x +1x +1x +1x +1x +  +1x +  +  +1x +  +1x +  +1x +  +  +  +  +1x +  +1x +  +1x +  +1x +  +  +  +  +1x +  +1x +  +1x +  +  +1x +  +  +1x
const MailController = require('../controllers/MailController');
+const RecipeController = require('../controllers/RecipeController');
+const UserController = require('../controllers/UserController');
+const CommentController = require('../controllers/CommentController');
+const authentication = require('../middlewares/authentication');
+const authorization = require('../middlewares/authorization');
+const errorHandler = require('../middlewares/errorHandler');
+ 
+const router = require('express').Router(); 
+ 
+ 
+router.post("/register", UserController.register);   //register
+ 
+router.post("/login", UserController.login);    //login
+ 
+router.post("/google-login", UserController.googleLogin);  //GOOGLE LOGIN
+ 
+ 
+// ======================== Authentication START ============================
+ 
+router.use(authentication)
+ 
+router.get("/recipes", RecipeController.recipes); 
+ 
+router.get("/recipe/:id", RecipeController.recipeById); 
+ 
+router.post("/contact-mail", MailController.sendMail); 
+ 
+ 
+// ==================================== Comment ======================================
+ 
+router.post("/comment/add", CommentController.addComment); 
+ 
+router.put("/comment/edit/:id", authorization, CommentController.editComment);  
+ 
+router.delete("/comment/delete/:id", authorization, CommentController.deleteComment); 
+ 
+ 
+router.use(errorHandler);
+ 
+ 
+module.exports = router;
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/server/coverage/lcov-report/sort-arrow-sprite.png b/server/coverage/lcov-report/sort-arrow-sprite.png new file mode 100644 index 0000000000000000000000000000000000000000..6ed68316eb3f65dec9063332d2f69bf3093bbfab GIT binary patch literal 138 zcmeAS@N?(olHy`uVBq!ia0vp^>_9Bd!3HEZxJ@+%Qh}Z>jv*C{$p!i!8j}?a+@3A= zIAGwzjijN=FBi!|L1t?LM;Q;gkwn>2cAy-KV{dn nf0J1DIvEHQu*n~6U}x}qyky7vi4|9XhBJ7&`njxgN@xNA8m%nc literal 0 HcmV?d00001 diff --git a/server/coverage/lcov-report/sorter.js b/server/coverage/lcov-report/sorter.js new file mode 100644 index 0000000..2bb296a --- /dev/null +++ b/server/coverage/lcov-report/sorter.js @@ -0,0 +1,196 @@ +/* eslint-disable */ +var addSorting = (function() { + 'use strict'; + var cols, + currentSort = { + index: 0, + desc: false + }; + + // returns the summary table element + function getTable() { + return document.querySelector('.coverage-summary'); + } + // returns the thead element of the summary table + function getTableHeader() { + return getTable().querySelector('thead tr'); + } + // returns the tbody element of the summary table + function getTableBody() { + return getTable().querySelector('tbody'); + } + // returns the th element for nth column + function getNthColumn(n) { + return getTableHeader().querySelectorAll('th')[n]; + } + + function onFilterInput() { + const searchValue = document.getElementById('fileSearch').value; + const rows = document.getElementsByTagName('tbody')[0].children; + for (let i = 0; i < rows.length; i++) { + const row = rows[i]; + if ( + row.textContent + .toLowerCase() + .includes(searchValue.toLowerCase()) + ) { + row.style.display = ''; + } else { + row.style.display = 'none'; + } + } + } + + // loads the search box + function addSearchBox() { + var template = document.getElementById('filterTemplate'); + var templateClone = template.content.cloneNode(true); + templateClone.getElementById('fileSearch').oninput = onFilterInput; + template.parentElement.appendChild(templateClone); + } + + // loads all columns + function loadColumns() { + var colNodes = getTableHeader().querySelectorAll('th'), + colNode, + cols = [], + col, + i; + + for (i = 0; i < colNodes.length; i += 1) { + colNode = colNodes[i]; + col = { + key: colNode.getAttribute('data-col'), + sortable: !colNode.getAttribute('data-nosort'), + type: colNode.getAttribute('data-type') || 'string' + }; + cols.push(col); + if (col.sortable) { + col.defaultDescSort = col.type === 'number'; + colNode.innerHTML = + colNode.innerHTML + ''; + } + } + return cols; + } + // attaches a data attribute to every tr element with an object + // of data values keyed by column name + function loadRowData(tableRow) { + var tableCols = tableRow.querySelectorAll('td'), + colNode, + col, + data = {}, + i, + val; + for (i = 0; i < tableCols.length; i += 1) { + colNode = tableCols[i]; + col = cols[i]; + val = colNode.getAttribute('data-value'); + if (col.type === 'number') { + val = Number(val); + } + data[col.key] = val; + } + return data; + } + // loads all row data + function loadData() { + var rows = getTableBody().querySelectorAll('tr'), + i; + + for (i = 0; i < rows.length; i += 1) { + rows[i].data = loadRowData(rows[i]); + } + } + // sorts the table using the data for the ith column + function sortByIndex(index, desc) { + var key = cols[index].key, + sorter = function(a, b) { + a = a.data[key]; + b = b.data[key]; + return a < b ? -1 : a > b ? 1 : 0; + }, + finalSorter = sorter, + tableBody = document.querySelector('.coverage-summary tbody'), + rowNodes = tableBody.querySelectorAll('tr'), + rows = [], + i; + + if (desc) { + finalSorter = function(a, b) { + return -1 * sorter(a, b); + }; + } + + for (i = 0; i < rowNodes.length; i += 1) { + rows.push(rowNodes[i]); + tableBody.removeChild(rowNodes[i]); + } + + rows.sort(finalSorter); + + for (i = 0; i < rows.length; i += 1) { + tableBody.appendChild(rows[i]); + } + } + // removes sort indicators for current column being sorted + function removeSortIndicators() { + var col = getNthColumn(currentSort.index), + cls = col.className; + + cls = cls.replace(/ sorted$/, '').replace(/ sorted-desc$/, ''); + col.className = cls; + } + // adds sort indicators for current column being sorted + function addSortIndicators() { + getNthColumn(currentSort.index).className += currentSort.desc + ? ' sorted-desc' + : ' sorted'; + } + // adds event listeners for all sorter widgets + function enableUI() { + var i, + el, + ithSorter = function ithSorter(i) { + var col = cols[i]; + + return function() { + var desc = col.defaultDescSort; + + if (currentSort.index === i) { + desc = !currentSort.desc; + } + sortByIndex(i, desc); + removeSortIndicators(); + currentSort.index = i; + currentSort.desc = desc; + addSortIndicators(); + }; + }; + for (i = 0; i < cols.length; i += 1) { + if (cols[i].sortable) { + // add the click event handler on the th so users + // dont have to click on those tiny arrows + el = getNthColumn(i).querySelector('.sorter').parentElement; + if (el.addEventListener) { + el.addEventListener('click', ithSorter(i)); + } else { + el.attachEvent('onclick', ithSorter(i)); + } + } + } + } + // adds sorting functionality to the UI + return function() { + if (!getTable()) { + return; + } + cols = loadColumns(); + loadData(); + addSearchBox(); + addSortIndicators(); + enableUI(); + }; +})(); + +window.addEventListener('load', addSorting); diff --git a/server/coverage/lcov.info b/server/coverage/lcov.info new file mode 100644 index 0000000..8b9727c --- /dev/null +++ b/server/coverage/lcov.info @@ -0,0 +1,462 @@ +TN: +SF:app.js +FNF:0 +FNH:0 +DA:1,1 +DA:2,1 +DA:5,1 +DA:6,1 +DA:7,1 +DA:9,1 +DA:11,1 +DA:13,1 +DA:15,1 +DA:18,1 +LF:10 +LH:10 +BRDA:1,0,0,1 +BRDA:1,0,1,0 +BRF:2 +BRH:1 +end_of_record +TN: +SF:controllers\CommentController.js +FN:5,(anonymous_0) +FN:14,(anonymous_1) +FN:24,(anonymous_2) +FNF:3 +FNH:1 +FNDA:1,(anonymous_0) +FNDA:0,(anonymous_1) +FNDA:0,(anonymous_2) +DA:1,1 +DA:3,1 +DA:6,1 +DA:7,1 +DA:8,1 +DA:10,0 +DA:15,0 +DA:16,0 +DA:17,0 +DA:18,0 +DA:20,0 +DA:25,0 +DA:26,0 +DA:27,0 +DA:28,0 +DA:30,0 +LF:16 +LH:5 +BRF:0 +BRH:0 +end_of_record +TN: +SF:controllers\MailController.js +FN:5,(anonymous_0) +FNF:1 +FNH:0 +FNDA:0,(anonymous_0) +DA:1,1 +DA:3,1 +DA:6,0 +DA:7,0 +DA:11,0 +DA:13,0 +DA:21,0 +DA:28,0 +DA:30,0 +DA:33,0 +DA:34,0 +LF:11 +LH:2 +BRDA:11,0,0,0 +BRDA:11,0,1,0 +BRDA:11,1,0,0 +BRDA:11,1,1,0 +BRDA:11,1,2,0 +BRF:5 +BRH:0 +end_of_record +TN: +SF:controllers\RecipeController.js +FN:6,(anonymous_0) +FN:40,(anonymous_1) +FN:47,(anonymous_2) +FN:74,(anonymous_3) +FN:97,(anonymous_4) +FNF:5 +FNH:0 +FNDA:0,(anonymous_0) +FNDA:0,(anonymous_1) +FNDA:0,(anonymous_2) +FNDA:0,(anonymous_3) +FNDA:0,(anonymous_4) +DA:1,1 +DA:2,1 +DA:4,1 +DA:8,0 +DA:10,0 +DA:28,0 +DA:29,0 +DA:32,0 +DA:33,0 +DA:34,0 +DA:37,0 +DA:38,0 +DA:39,0 +DA:40,0 +DA:41,0 +DA:47,0 +DA:63,0 +DA:64,0 +DA:65,0 +DA:67,0 +DA:69,0 +DA:76,0 +DA:78,0 +DA:87,0 +DA:88,0 +DA:89,0 +DA:91,0 +DA:97,0 +DA:113,0 +LF:29 +LH:3 +BRDA:28,0,0,0 +BRDA:28,0,1,0 +BRDA:32,1,0,0 +BRDA:32,1,1,0 +BRDA:63,2,0,0 +BRDA:63,2,1,0 +BRF:6 +BRH:0 +end_of_record +TN: +SF:controllers\UserController.js +FN:10,(anonymous_0) +FN:26,(anonymous_1) +FN:62,(anonymous_2) +FNF:3 +FNH:0 +FNDA:0,(anonymous_0) +FNDA:0,(anonymous_1) +FNDA:0,(anonymous_2) +DA:1,1 +DA:2,1 +DA:3,1 +DA:4,1 +DA:5,1 +DA:8,1 +DA:11,0 +DA:12,0 +DA:13,0 +DA:15,0 +DA:21,0 +DA:27,0 +DA:28,0 +DA:30,0 +DA:31,0 +DA:32,0 +DA:35,0 +DA:36,0 +DA:37,0 +DA:40,0 +DA:41,0 +DA:42,0 +DA:43,0 +DA:46,0 +DA:47,0 +DA:48,0 +DA:49,0 +DA:52,0 +DA:53,0 +DA:56,0 +DA:57,0 +DA:63,0 +DA:65,0 +DA:66,0 +DA:70,0 +DA:72,0 +DA:74,0 +DA:84,0 +DA:85,0 +DA:88,0 +LF:40 +LH:6 +BRDA:30,0,0,0 +BRDA:30,0,1,0 +BRDA:35,1,0,0 +BRDA:35,1,1,0 +BRDA:41,2,0,0 +BRDA:41,2,1,0 +BRDA:47,3,0,0 +BRDA:47,3,1,0 +BRDA:85,4,0,0 +BRDA:85,4,1,0 +BRF:10 +BRH:0 +end_of_record +TN: +SF:helpers\bcryptjs.js +FN:3,(anonymous_0) +FN:8,(anonymous_1) +FNF:2 +FNH:1 +FNDA:1,(anonymous_0) +FNDA:0,(anonymous_1) +DA:1,1 +DA:3,1 +DA:4,1 +DA:5,1 +DA:8,1 +DA:9,0 +DA:13,1 +LF:7 +LH:6 +BRF:0 +BRH:0 +end_of_record +TN: +SF:helpers\jwt.js +FN:5,(anonymous_0) +FN:9,(anonymous_1) +FNF:2 +FNH:2 +FNDA:1,(anonymous_0) +FNDA:2,(anonymous_1) +DA:1,1 +DA:3,1 +DA:5,1 +DA:6,1 +DA:9,1 +DA:10,2 +DA:14,1 +LF:7 +LH:7 +BRF:0 +BRH:0 +end_of_record +TN: +SF:middlewares\authentication.js +FN:4,authentication +FNF:1 +FNH:1 +FNDA:3,authentication +DA:1,1 +DA:2,1 +DA:5,3 +DA:6,3 +DA:8,3 +DA:10,2 +DA:12,1 +DA:14,1 +DA:16,1 +DA:20,1 +DA:21,1 +DA:24,2 +DA:25,2 +DA:29,1 +LF:14 +LH:14 +BRDA:8,0,0,1 +BRDA:8,0,1,2 +BRDA:14,1,0,0 +BRDA:14,1,1,1 +BRF:4 +BRH:3 +end_of_record +TN: +SF:middlewares\authorization.js +FN:3,authorization +FNF:1 +FNH:0 +FNDA:0,authorization +DA:1,1 +DA:4,0 +DA:5,0 +DA:7,0 +DA:9,0 +DA:10,0 +DA:13,0 +DA:16,0 +DA:21,1 +LF:9 +LH:2 +BRDA:7,0,0,0 +BRDA:7,0,1,0 +BRDA:9,1,0,0 +BRDA:9,1,1,0 +BRF:4 +BRH:0 +end_of_record +TN: +SF:middlewares\errorHandler.js +FN:1,errorHandler +FNF:1 +FNH:1 +FNDA:2,errorHandler +DA:1,1 +DA:3,2 +DA:6,0 +DA:7,0 +DA:10,0 +DA:11,0 +DA:15,2 +DA:16,2 +DA:19,0 +DA:20,0 +DA:23,0 +DA:24,0 +DA:27,0 +DA:28,0 +DA:31,0 +DA:32,0 +LF:16 +LH:4 +BRDA:3,0,0,0 +BRDA:3,0,1,0 +BRDA:3,0,2,0 +BRDA:3,0,3,1 +BRDA:3,0,4,2 +BRDA:3,0,5,0 +BRDA:3,0,6,0 +BRDA:3,0,7,0 +BRDA:3,0,8,0 +BRDA:15,1,0,2 +BRDA:15,1,1,1 +BRF:11 +BRH:4 +end_of_record +TN: +SF:models\comment.js +FN:5,(anonymous_0) +FN:12,(anonymous_1) +FNF:2 +FNH:2 +FNDA:1,(anonymous_0) +FNDA:1,(anonymous_1) +DA:4,1 +DA:5,1 +DA:14,1 +DA:17,1 +DA:24,1 +LF:5 +LH:5 +BRF:0 +BRH:0 +end_of_record +TN: +SF:models\index.js +FN:21,(anonymous_0) +FN:29,(anonymous_1) +FN:34,(anonymous_2) +FNF:3 +FNH:3 +FNDA:4,(anonymous_0) +FNDA:3,(anonymous_1) +FNDA:3,(anonymous_2) +DA:3,1 +DA:4,1 +DA:5,1 +DA:6,1 +DA:7,1 +DA:8,1 +DA:9,1 +DA:10,1 +DA:13,1 +DA:14,0 +DA:16,1 +DA:19,1 +DA:22,4 +DA:30,3 +DA:31,3 +DA:34,1 +DA:35,3 +DA:36,3 +DA:40,1 +DA:41,1 +DA:43,1 +LF:21 +LH:20 +BRDA:8,0,0,1 +BRDA:8,0,1,0 +BRDA:13,1,0,0 +BRDA:13,1,1,1 +BRDA:23,2,0,4 +BRDA:23,2,1,4 +BRDA:23,2,2,3 +BRDA:23,2,3,3 +BRDA:35,3,0,3 +BRDA:35,3,1,0 +BRF:10 +BRH:7 +end_of_record +TN: +SF:models\recipe.js +FN:5,(anonymous_0) +FN:12,(anonymous_1) +FNF:2 +FNH:2 +FNDA:1,(anonymous_0) +FNDA:1,(anonymous_1) +DA:4,1 +DA:5,1 +DA:16,1 +DA:29,1 +LF:4 +LH:4 +BRF:0 +BRH:0 +end_of_record +TN: +SF:models\user.js +FN:6,(anonymous_0) +FN:13,(anonymous_1) +FN:60,(anonymous_2) +FNF:3 +FNH:3 +FNDA:1,(anonymous_0) +FNDA:1,(anonymous_1) +FNDA:1,(anonymous_2) +DA:2,1 +DA:3,1 +DA:6,1 +DA:15,1 +DA:18,1 +DA:60,1 +DA:61,1 +DA:64,1 +LF:8 +LH:8 +BRF:0 +BRH:0 +end_of_record +TN: +SF:routes\index.js +FNF:0 +FNH:0 +DA:1,1 +DA:2,1 +DA:3,1 +DA:4,1 +DA:5,1 +DA:6,1 +DA:7,1 +DA:9,1 +DA:12,1 +DA:14,1 +DA:16,1 +DA:21,1 +DA:23,1 +DA:25,1 +DA:27,1 +DA:32,1 +DA:34,1 +DA:36,1 +DA:39,1 +DA:42,1 +LF:20 +LH:20 +BRF:0 +BRH:0 +end_of_record diff --git a/server/data/recipe.json b/server/data/recipe.json new file mode 100644 index 0000000..df36e6c --- /dev/null +++ b/server/data/recipe.json @@ -0,0 +1,291 @@ +[ + { + "number": 1, + "id": "133fcdec-4595-44be-a302-02df4c209451", + "name": "Keto Sesame Tuna and Egg Salad", + "description": "This quick and easy tuna and egg salad is combined with aromatic scallions and parsley and coated in a sesame, lemon mayo.\n\nThis is a great option for stuffing lettuce wraps or topping low carb crackers.", + "prepareTime": "8 minutes", + "cookTime": "0 minutes", + "ingredients": [ + "Hard Boiled Egg 2 medium", + "Scallions 2 medium - 4 1/8\" long", + "Canned Tuna 1-¾ ounce", + "Lemon Juice, Fresh 1 teaspoon", + "Parsley, Fresh 1 tablespoon", + "Sesame Oil 1 teaspoon", + "Mayonnaise 1 tablespoon", + "Sesame Seeds, Hulled, Toasted, Unsalted 1 teaspoon, whole pieces", + "Salt, Sea Salt ⅛ teaspoon", + "Black Pepper ⅛ teaspoon" + ], + "steps": [ + "Peel and roughly chop the eggs and add to a mixing bowl.", + "Thinly slice the scallions and roughly chop the parsley. Add to the mixing bowl along with the tuna and stir well to combine.", + "Add the mayonnaise to a mixing bowl with the lemon juice, sesame oil, salt and pepper. Whisk together until smooth.", + "Spoon the dressing over the salad and stir well to coat.", + "Sprinkle over the sesame seeds to serve." + ], + "nutrients": { + "caloriesKCal": 170.313, + "totalCarbs": 2.366, + "sugar": 1.166, + "protein": 11.004, + "fat": 13.018, + "cholesterol": 175.938, + "alcohol": 0, + "gluten": 0 + }, + "image": "https://tinyurl.com/2p82zzca/133fcdec-4595-44be-a302-02df4c209451.png" + }, + { + "number": 2, + "id": "ccef957b-351f-4c14-b33c-ef050045b877", + "name": "Keto Vanilla Butter Latte", + "description": "This creamy Keto vanilla BULLETPROOF® coffee recipe is rich in fats, frothy plant-based milk, and a hint of sweet vanilla. This Keto vanilla butter coffee makes a great fat-fueled breakfast option to kickstart your day. Alternatively, this is perfect served with a slice of low-carb cake as an afternoon treat!\n\n### What ingredients are in this Keto vanilla butter latte?\n\nThis Keto vanilla BULLETPROOF® coffee is prepared with hot almond milk infused with sweet vanilla extract. The milk is then blended with instant coffee and a hearty helping of unsalted butter, before being blitzed until super frothy! We have used a handheld stick blender to froth and blend our Keto coffee, however, you may use a freestanding blender for this if preferred.\n\n### Here are some tips to make Keto vanilla butter coffee?\n\nThis Keto BULLETPROOF® coffee provides a generous serving of fats from unsalted butter. If preferred you may swap the butter for coconut oil or BULLETPROOF® brain octane oil (MCT Oil) or even a blend of fats. You may also swap the erythritol for your preference in low-carb sweetener or omit it entirely if desired. Please be sure to adjust your macros for any changes made.\n\n### Can you use freshly brewed coffee in this Keto recipe?\n\nWhile this recipe calls for instant coffee, you can definitely substitute it for espresso or freshly brewed coffee, just make sure its strong.\n\nBULLETPROOF® is a registered trademark owned by Bulletproof Digital, Inc.", + "prepareTime": "8 minutes", + "cookTime": "3 minutes", + "ingredients": [ + "Butter, Unsalted 1-½ tablespoon", + "Almond Milk, Vanilla Or Other Flavors, Unsweetened 1-½ cup", + "Instant Coffee 1 teaspoon", + "Vanilla Extract ¼ teaspoon", + "100% Pure Erythritol by Now 1 tsp" + ], + "steps": [ + "Add the almond milk and vanilla extract to a small saucepan over low/medium heat. Stir to combine. Heat the milk through until piping hot but not boiling.", + "Add the instant coffee to a large mug. Add the granulated sweetener. Add the hot milk and stir well to combine, dissolving the coffee and sweetener.", + "Add the butter to the mug. Stir well to combine. Let the butter completely melt into the milky coffee.", + "Carefully use a hand held stick blender or milk frother and blend the coffee and butter. Continue blending until you have a thick frothy layer on the top of your coffee. Serve hot." + ], + "nutrients": { + "caloriesKCal": 203.555, + "totalCarbs": 5.903, + "sugar": 0.572, + "protein": 1.779, + "fat": 21.015, + "cholesterol": 45.755, + "alcohol": 0.631, + "gluten": 0 + }, + "image": "https://tinyurl.com/2p82zzca/ccef957b-351f-4c14-b33c-ef050045b877.png" + }, + { + "number": 3, + "id": "348b12ac-3cf3-44a7-b193-4c686a07c0f2", + "name": "Keto Bird Nests", + "description": "This deliciously low-carb breakfast is light to eat and totally instagram-worthy! Spiralized zucchini noodles are gently seasoned and are each a vibrant bed to a single egg. Feel free to expand on this recipe and add additional seasonings or herbs. You can even add a little cheese for more fat and protein!\n\nJessica L.", + "prepareTime": "10 minutes", + "cookTime": "20 minutes", + "ingredients": [ + "Zucchini 8-½ ounce", + "Olive Oil 2-½ teaspoon", + "Raw Egg 4 large", + "Salt 1 teaspoon", + "Black Pepper 1 teaspoon" + ], + "steps": [ + "Preheat an oven to 350 degrees and line a sheet tray with aluminum foil or simply grease it. Spiralize a large zucchini, and break up the spirals slightly with your fingers.", + "Divide the zucchini into 4 sections (just slightly over 2 oz. each), and arrange them into “nests” on your sheet tray. Create a small space in the center of each nest. Then, drizzle the olive oil gently across the zucchini.", + "Crack an egg into each zucchini nest. Sprinkle about ¼ tsp each of the salt and pepper over each bird nest.", + "Bake the bird nests for approx 20 minutes. This is enough time for the eggs to fully cook and the zucchini to turn just slightly golden on the edges. Allow the tray to cool for 2-3 minutes before transferring the finished recipe to plates with a spatula.", + "You may cook the bird nests for less than 20 minutes, if you want runnier eggs!" + ], + "nutrients": { + "caloriesKCal": 114.047, + "totalCarbs": 2.801, + "sugar": 2.07, + "protein": 7.079, + "fat": 8.329, + "cholesterol": 186.5, + "alcohol": 0, + "gluten": 0 + }, + "image": "https://tinyurl.com/2p82zzca/348b12ac-3cf3-44a7-b193-4c686a07c0f2.png" + }, + { + "number": 4, + "id": "2b3146d9-f265-48f1-8142-6d445ad8f4a8", + "name": "Keto Thai Spicy Peanut Chicken With Asian Slaw", + "description": "Do you love Thai food? This Thai-inspired chicken dish is a little spicy, a little nutty from some peanut butter, and tossed with fresh herbs. A cool and creamy Asian slaw is paired on the side to combat any heat from the chili sauce. You can have your lunch or dinner ready in 20 minutes or less! You can double this recipe for 4 days of meal prep as well.\n\nJessica L.", + "prepareTime": "10 minutes", + "cookTime": "8 minutes", + "ingredients": [ + "Packaged Salads Fresh Favorites Colorful Coleslaw by Dole 1 cups", + "Salt 0.25 teaspoon", + "Rice Vinegar 1 teaspoon", + "Mayo 1 tablespoon", + "Sesame Seeds ¼ teaspoon, whole pieces", + "Lime Juice, Fresh 1 wedge - juice from one wedge or slice", + "Boneless Skinless Chicken Breast 10 ounce", + "Olive Oil ½ tablespoon", + "Black Pepper 0.25 teaspoon", + "Peanut Butter 1 tablespoon", + "Sambal Oelek Paste Of Chili by Rooster 1 tablespoon", + "Soy Sauce 2 tablespoon", + "Cilantro ½ tablespoon, chopped", + "Basil 4 leaf", + "Lime Juice, Fresh 2 wedge - juice from one wedge or slice" + ], + "steps": [ + "In a bowl, combine the slaw mix, salt, vinegar, mayo, sesame seeds, and first amount of lime juice. Set this slaw mixture aside for later, giving the salt time to draw out the juices.", + "Trim any unwanted fat from your chicken, and chop the chicken into 1-inch cubes.", + "Heat the olive oil in a medium sized pan on medium-high heat. Toss in the chicken and black pepper, and cook these ingredients until the chicken just cooks through.", + "Use a wooden spoon to move the chicken the the outer edges of the pan and turn the heat to very low. Place the peanut butter, chili sauce, and soy sauce in the exposed center of the pan.", + "Let the heat melt the peanut butter slightly, then stir and combine the chicken and sauce into a consistent and creamy mixture.", + "Finally, chop the cilantro and basil and toss these with the chicken.", + "Serve the hot chicken with the cool slaw and two lime wedges. You can add extra cilantro as a garnish at your discretion. Enjoy!" + ], + "nutrients": { + "caloriesKCal": 394.486, + "totalCarbs": 5.723, + "sugar": 2.351, + "protein": 47.457, + "fat": 19.403, + "cholesterol": 123.372, + "alcohol": 0, + "gluten": 0 + }, + "image": "https://tinyurl.com/2p82zzca/2b3146d9-f265-48f1-8142-6d445ad8f4a8.png" + }, + { + "number": 5, + "id": "1a69a125-7c33-4664-b4c2-f4fdddab8781", + "name": "Keto Green Beans and Scallions", + "description": "These aromatic green beans are cooked with tender scallions, spicy chili flakes, a hint of zesty lime and sesame oil.\n\nThese are a great accompaniment to Asian inspired dishes or grilled fish.", + "prepareTime": "5 minutes", + "cookTime": "12 minutes", + "ingredients": [ + "Green Beans (string Beans), Raw 5 ounce", + "Lime Juice, Fresh 2 teaspoon", + "Scallions 2 medium - 4 1/8\" long", + "Olive Oil 1-½ tablespoon", + "Hot Chili Pepper, Dried, With Seeds 1 teaspoon", + "Sesame Oil 1 teaspoon", + "Salt, Sea Salt ¼ teaspoon", + "Black Pepper ⅛ teaspoon" + ], + "steps": [ + "Add the green beans to a pan of boiling water and simmer for 4-5 minutes until tender. Drain completely.", + "Thinly slice the scallions. Heat a tablespoon of olive oil in a skillet over a low/medium heat. Add the scallions and sauté for 2-3 minutes until tender.", + "Whisk together the remaining olive oil, sesame oil, lime juice, chili flakes, salt and pepper. Add the green beans to the pan and drizzle over the spiced oil. Stir well to combine and cook for a further minute or two to heat the beans through." + ], + "nutrients": { + "caloriesKCal": 70.591, + "totalCarbs": 3.578, + "sugar": 1.619, + "protein": 0.869, + "fat": 6.341, + "cholesterol": 0, + "alcohol": 0, + "gluten": 0 + }, + "image": "https://tinyurl.com/2p82zzca/1a69a125-7c33-4664-b4c2-f4fdddab8781.png" + }, + { + "number": 6, + "id": "682207ec-7c88-4eb4-84d8-ddd1703d9346", + "name": "Low Carb Spiced Bok Choy and Broccoli Soup", + "description": "This warming soup is packed with low carb broccoli and Bok choy, aromatic cumin, fiery ginger and topped with spicy chili flakes to serve.\n\nThis makes a great lunch or dinner option served with a slice of keto bread.", + "prepareTime": "8 minutes", + "cookTime": "13 minutes", + "ingredients": [ + "Vegetable Broth, Bouillon Or Consomme 4 cup", + "Cilantro 2 tablespoon", + "Bok Choy 1 cup", + "Olive Oil 1 tablespoon", + "Ginger Root, Raw 1 slice - 1\" diameter", + "Broccoli Florets 1 cups", + "Shallot 1 tablespoon, chopped", + "Garlic 1 clove", + "Hot Chili Pepper, Dried, With Seeds 1 teaspoon", + "Cumin, Seed ½ teaspoon" + ], + "steps": [ + "Thinly slice the shallot and garlic. Heat the oil in a large saucepan over a low/medium heat. Add the garlic and shallots and sweat gently until tender.", + "Slice the tough base from the Bok choy to separate the individual stalks. Cut the leaves from the stalks and reserve to one side. Grate the ginger and add to the pan along with the broccoli florets, Bok choy stalks and cumin seeds. Stir to combine and cook for 2-3 minutes to soften the vegetables and toast the cumin seeds.", + "Add the stock and bring to a gentle boil. Simmer for 5-6 minutes or until the vegetables are fork tender.", + "Add the Bok choy leaves and stir until just wilted. Transfer to a blender or use a handheld stick blender and process until smooth.", + "Scatter with dried chili flakes and fresh cilantro to serve." + ], + "nutrients": { + "caloriesKCal": 68, + "totalCarbs": 6.005, + "sugar": 1.978, + "protein": 2.983, + "fat": 3.912, + "cholesterol": 0, + "alcohol": 0, + "gluten": 0 + }, + "image": "https://tinyurl.com/2p82zzca/682207ec-7c88-4eb4-84d8-ddd1703d9346.png" + }, + { + "number": 7, + "id": "2a4fac4d-b935-4360-b72c-934e34895681", + "name": "Keto Blue Cheese Dressing", + "description": "This quick and easy blue cheese dressing is rich, creamy and perfect for adding a boost of fats and flavor to your Keto meals. Simple to prepare, this blue cheese dressing does not require any cooking and is ready in just a matter of minutes. A great dressing to have on hand in the fridge, ready to drizzle over salads and meats or for dipping low carb vegetables.\n\n### Is Blue Cheese Dressing Keto?\n\nIt’s very simple to make your own Keto-friendly blue cheese dressing. Store-bought versions may contain added fillers and sugars that are not Keto-friendly, but our low carb dressing is so quick to prepare there’s no need to reach for shop-bought again! Our quick and easy Keto dressing combines natural yogurt, mayonnaise, a hint of lemon, a touch of garlic and tangy blue cheese. The ingredients are mixed together until creamy, creating a delicious and pourable dressing.\n\n### Serving Suggestions\n\nThis thick and creamy dressing is versatile in its uses. The dressing can be thinned as desired, perfect for drizzling over your favorite Keto salads. Alternatively, you can keep the dressing slightly thicker, perfect served with meats. We love to pair our blue cheese dressing with Keto burgers. It is also delicious added to salads with crispy bacon.", + "prepareTime": "8 minutes", + "cookTime": "0 minutes", + "ingredients": [ + "Water 1 tablespoon", + "Lemon Juice 1 teaspoon", + "Dannon All Natural Yogurt, Plain ⅓ cup", + "Mayonnaise ⅓ cup", + "Blue Cheese ⅓ cup", + "Black Pepper ⅛ teaspoon", + "Garlic, Powder ¼ teaspoon" + ], + "steps": [ + "Add the yogurt, mayonnaise and lemon juice to a mixing bowl. You may use a homemade or sugar-free store-bought mayonnaise. You may also use any plain yogurt you desire. Be sure to adjust your macros for any changes made. Whisk the yogurt, mayonnaise and lemon juice together to combine until smooth.", + "Crumble the blue cheese into the mayo and yogurt mix. You can crumble the cheese roughly so you have a mixture of large chunks and smaller pieces. Add the garlic powder and black pepper. You can omit or adjust the quantities of these seasonings as desired. Mix together well to combine, distributing the cheese evenly throughout the dressing. If you would prefer an entirely smooth dressing you can prepare this in a food processor, blending until smooth.", + "Add the water to the dressing and whisk well to combine. This can be omitted or increased to suit your personal preference. Add more water for a thinner salad dressing or omit entirely if you want a thicker dressing for topping meats and burgers. Refrigerate in an airtight container until ready to serve." + ], + "nutrients": { + "caloriesKCal": 211.721, + "totalCarbs": 1.823, + "sugar": 1.197, + "protein": 5.384, + "fat": 20.375, + "cholesterol": 25.964, + "alcohol": 0, + "gluten": 0 + }, + "image": "https://tinyurl.com/2p82zzca/2a4fac4d-b935-4360-b72c-934e34895681.png" + }, + { + "number": 8, + "id": "1f11e666-3601-4f21-9cf2-c9c7427481b7", + "name": "Keto One Pan Garlic Butter Chicken Thighs", + "description": "These simple one pan chicken thighs are cooked in a generous helping of butter alongside aromatic garlic, rosemary and lemon.\n\nPerfect for a quick and easy weeknight meal with minimal washing up!", + "prepareTime": "5 minutes", + "cookTime": "20 minutes", + "ingredients": [ + "Chicken Thigh, Skin Removed Before Cooking 4 medium", + "Garlic 3 clove", + "Unsalted Butter 2 tablespoon", + "Lemon Peel Or Zest Raw 2 tsp", + "Olive Oil 1 tablespoon", + "Rosemary 1 x 1 x sprig", + "Salt, Sea Salt ¼ teaspoon", + "Black Pepper ⅛ teaspoon" + ], + "steps": [ + "Heat the olive oil in a large frying pan or skillet over a medium heat and add the chicken thighs. Fry the thighs until they just start to colour.", + "Add the butter to the pan and continue to cook the chicken until golden brown all over. Set aside.", + "Reduce the heat, slice the garlic and add to the pan cooking gently in the butter until fragrant, taking care not to burn the butter or garlic.", + "Return the chicken thighs to the pan along with the lemon zest and tear in the rosemary.", + "Continue to cook over a medium heat, spooning the hot butter over the chicken until completely cooked through and no longer pink. Serve with steamed green beans or broccoli." + ], + "nutrients": { + "caloriesKCal": 418.52, + "totalCarbs": 1.96, + "sugar": 0.138, + "protein": 40.354, + "fat": 26.988, + "cholesterol": 214.822, + "alcohol": 0, + "gluten": 0 + }, + "image": "https://tinyurl.com/2p82zzca/1f11e666-3601-4f21-9cf2-c9c7427481b7.png" + } +] \ No newline at end of file diff --git a/server/middlewares/authorization.js b/server/middlewares/authorization.js new file mode 100644 index 0000000..549d71b --- /dev/null +++ b/server/middlewares/authorization.js @@ -0,0 +1,21 @@ +const {Comment} = require("../models"); + +async function authorization(req, res, next) { + try { + let comment = await Comment.findByPk(req.params.id); + + if(!comment) throw ({name: "NotFound"}) + + if(comment.userId !== req.user.id){ + throw {name: "Forbidden"} + } + + next(); + + } catch (error) { + next(error); + } +} + + +module.exports = authorization; \ No newline at end of file diff --git a/server/middlewares/errorHandler.js b/server/middlewares/errorHandler.js index 5b3a530..6d59b99 100644 --- a/server/middlewares/errorHandler.js +++ b/server/middlewares/errorHandler.js @@ -3,7 +3,7 @@ module.exports = function errorHandler(error, req, res, next) { switch (error.name) { case "SequelizeValidationError": case "SequelizeUniqueConstraintError": - res.status(400).json({ message: error.errors[0].message}); + res.status(400).json({ message: error.errors[0].message }); break; case "IncompleteData": @@ -15,6 +15,14 @@ module.exports = function errorHandler(error, req, res, next) { res.status(401).json({ message: error.message ?? "Unauthenticated" }); break; + case "Forbidden": + res.status(403).json({ message: "You are not authorized" }); + break; + + case "NotFound": + res.status(404).json({ message: "Comment not found" }); + break; + case "AxiosError": res.status(404).json({ message: "Recipe not found" }); break; diff --git a/server/migrations/20231116023751-create-recipe.js b/server/migrations/20231116023751-create-recipe.js new file mode 100644 index 0000000..8cbb5df --- /dev/null +++ b/server/migrations/20231116023751-create-recipe.js @@ -0,0 +1,49 @@ +'use strict'; +/** @type {import('sequelize-cli').Migration} */ +module.exports = { + async up(queryInterface, Sequelize) { + await queryInterface.createTable('Recipes', { + id: { + allowNull: false, + autoIncrement: true, + primaryKey: true, + type: Sequelize.INTEGER + }, + name: { + type: Sequelize.STRING + }, + description: { + type: Sequelize.STRING(10000) + }, + prepareTime: { + type: Sequelize.STRING + }, + cookTime: { + type: Sequelize.STRING + }, + ingredients: { + type: Sequelize.ARRAY(Sequelize.STRING(20000)) + }, + steps: { + type: Sequelize.ARRAY(Sequelize.STRING(20000)) + }, + nutrients: { + type: Sequelize.JSON + }, + image: { + type: Sequelize.STRING + }, + createdAt: { + allowNull: false, + type: Sequelize.DATE + }, + updatedAt: { + allowNull: false, + type: Sequelize.DATE + } + }); + }, + async down(queryInterface, Sequelize) { + await queryInterface.dropTable('Recipes'); + } +}; \ No newline at end of file diff --git a/server/migrations/20231116035529-create-comment.js b/server/migrations/20231116035529-create-comment.js new file mode 100644 index 0000000..585f68c --- /dev/null +++ b/server/migrations/20231116035529-create-comment.js @@ -0,0 +1,37 @@ +'use strict'; +/** @type {import('sequelize-cli').Migration} */ +module.exports = { + async up(queryInterface, Sequelize) { + await queryInterface.createTable('Comments', { + id: { + allowNull: false, + autoIncrement: true, + primaryKey: true, + type: Sequelize.INTEGER + }, + description: { + type: Sequelize.STRING + }, + userId: { + type: Sequelize.INTEGER, + references: { + model: { + tableName: 'Users' + }, + key: 'id' + }, + }, + createdAt: { + allowNull: false, + type: Sequelize.DATE + }, + updatedAt: { + allowNull: false, + type: Sequelize.DATE + } + }); + }, + async down(queryInterface, Sequelize) { + await queryInterface.dropTable('Comments'); + } +}; \ No newline at end of file diff --git a/server/models/comment.js b/server/models/comment.js new file mode 100644 index 0000000..a397e2b --- /dev/null +++ b/server/models/comment.js @@ -0,0 +1,25 @@ +'use strict'; +const { + Model +} = require('sequelize'); +module.exports = (sequelize, DataTypes) => { + class Comment extends Model { + /** + * Helper method for defining associations. + * This method is not a part of Sequelize lifecycle. + * The `models/index` file will call this method automatically. + */ + static associate(models) { + // define association here + Comment.belongsTo(models.User, {foreignKey: "userId"}) + } + } + Comment.init({ + description: DataTypes.STRING, + userId: DataTypes.INTEGER + }, { + sequelize, + modelName: 'Comment', + }); + return Comment; +}; \ No newline at end of file diff --git a/server/models/recipe.js b/server/models/recipe.js new file mode 100644 index 0000000..10444dc --- /dev/null +++ b/server/models/recipe.js @@ -0,0 +1,30 @@ +'use strict'; +const { + Model +} = require('sequelize'); +module.exports = (sequelize, DataTypes) => { + class Recipe extends Model { + /** + * Helper method for defining associations. + * This method is not a part of Sequelize lifecycle. + * The `models/index` file will call this method automatically. + */ + static associate(models) { + // define association here + } + } + Recipe.init({ + name: DataTypes.STRING, + description: DataTypes.STRING(10000), + prepareTime: DataTypes.STRING, + cookTime: DataTypes.STRING, + ingredients: DataTypes.ARRAY(DataTypes.STRING(20000)), + steps: DataTypes.ARRAY(DataTypes.STRING(20000)), + nutrients: DataTypes.JSON, + image: DataTypes.STRING + }, { + sequelize, + modelName: 'Recipe', + }); + return Recipe; +}; \ No newline at end of file diff --git a/server/models/user.js b/server/models/user.js index 86a459c..ef05cb7 100644 --- a/server/models/user.js +++ b/server/models/user.js @@ -12,6 +12,7 @@ module.exports = (sequelize, DataTypes) => { */ static associate(models) { // define association here + User.hasMany(models.Comment, {foreignKey: 'userId'}); } } User.init({ diff --git a/server/routes/index.js b/server/routes/index.js index 7b19d0c..77e369b 100644 --- a/server/routes/index.js +++ b/server/routes/index.js @@ -1,7 +1,9 @@ const MailController = require('../controllers/MailController'); const RecipeController = require('../controllers/RecipeController'); const UserController = require('../controllers/UserController'); +const CommentController = require('../controllers/CommentController'); const authentication = require('../middlewares/authentication'); +const authorization = require('../middlewares/authorization'); const errorHandler = require('../middlewares/errorHandler'); const router = require('express').Router(); @@ -13,15 +15,26 @@ router.post("/login", UserController.login); //login router.post("/google-login", UserController.googleLogin); //GOOGLE LOGIN + +// ======================== Authentication START ============================ + router.use(authentication) router.get("/recipes", RecipeController.recipes); -router.get("/recipes/:id", RecipeController.recipeById); +router.get("/recipe/:id", RecipeController.recipeById); router.post("/contact-mail", MailController.sendMail); +// ==================================== Comment ====================================== + +router.post("/comment/add", CommentController.addComment); + +router.put("/comment/edit/:id", authorization, CommentController.editComment); + +router.delete("/comment/delete/:id", authorization, CommentController.deleteComment); + router.use(errorHandler); diff --git a/server/seeders/20231116030505-recipe-seed.js b/server/seeders/20231116030505-recipe-seed.js new file mode 100644 index 0000000..a6b1fe4 --- /dev/null +++ b/server/seeders/20231116030505-recipe-seed.js @@ -0,0 +1,27 @@ +'use strict'; + +/** @type {import('sequelize-cli').Migration} */ +module.exports = { + async up (queryInterface, Sequelize) { + + const recipes = require("../data/recipe.json"); + recipes.forEach((el) => { + delete el.number; + delete el.id; + el.nutrients = JSON.stringify(el.nutrients); + el.createdAt = el.updatedAt = new Date(); + }) + + await queryInterface.bulkInsert("Recipes", recipes); + }, + + async down (queryInterface, Sequelize) { + + await queryInterface.bulkDelete("Recipes", null, { + truncate: true, + cascade: true, + restartIdentity: true + }); + + } +}; From b69caf7a0be2b4ba4ed49ead1096699bb05bb98b Mon Sep 17 00:00:00 2001 From: pramesthias Date: Thu, 16 Nov 2023 13:47:35 +0700 Subject: [PATCH 16/28] fix: model recipe --- server/migrations/20231116023751-create-recipe.js | 6 +++--- server/models/recipe.js | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/server/migrations/20231116023751-create-recipe.js b/server/migrations/20231116023751-create-recipe.js index 8cbb5df..3af8bac 100644 --- a/server/migrations/20231116023751-create-recipe.js +++ b/server/migrations/20231116023751-create-recipe.js @@ -13,7 +13,7 @@ module.exports = { type: Sequelize.STRING }, description: { - type: Sequelize.STRING(10000) + type: Sequelize.TEXT }, prepareTime: { type: Sequelize.STRING @@ -22,10 +22,10 @@ module.exports = { type: Sequelize.STRING }, ingredients: { - type: Sequelize.ARRAY(Sequelize.STRING(20000)) + type: Sequelize.ARRAY(Sequelize.TEXT) }, steps: { - type: Sequelize.ARRAY(Sequelize.STRING(20000)) + type: Sequelize.ARRAY(Sequelize.TEXT) }, nutrients: { type: Sequelize.JSON diff --git a/server/models/recipe.js b/server/models/recipe.js index 10444dc..f0a80d2 100644 --- a/server/models/recipe.js +++ b/server/models/recipe.js @@ -15,11 +15,11 @@ module.exports = (sequelize, DataTypes) => { } Recipe.init({ name: DataTypes.STRING, - description: DataTypes.STRING(10000), + description: DataTypes.TEXT, prepareTime: DataTypes.STRING, cookTime: DataTypes.STRING, - ingredients: DataTypes.ARRAY(DataTypes.STRING(20000)), - steps: DataTypes.ARRAY(DataTypes.STRING(20000)), + ingredients: DataTypes.ARRAY(DataTypes.TEXT), + steps: DataTypes.ARRAY(DataTypes.TEXT), nutrients: DataTypes.JSON, image: DataTypes.STRING }, { From 564e1d1cecf36c192479667368edb117f69e5379 Mon Sep 17 00:00:00 2001 From: pramesthias Date: Thu, 16 Nov 2023 18:33:16 +0700 Subject: [PATCH 17/28] feat: add home and detail page for client side --- README.md | 4 + client/index.html | 2 - client/package-lock.json | 260 +++++++++++++++++- client/package.json | 5 +- client/src/components/Card.jsx | 22 ++ client/src/components/Navbar.jsx | 6 + client/src/components/Sidebar.jsx | 1 + client/src/main.jsx | 2 +- client/src/router/index.jsx | 41 ++- client/src/views/Detail.jsx | 93 +++++++ client/src/views/Home.jsx | 113 ++++++-- server/__tests__/comment-add.test.js | 6 +- server/__tests__/comment-delete.test.js | 6 + server/__tests__/comment-edit.test.js | 12 +- server/__tests__/contact-mail.test.js | 0 server/__tests__/google-login.test.js | 109 -------- server/__tests__/recipe-id.test.js | 6 +- server/controllers/CommentController.js | 2 +- server/controllers/MailController.js | 2 - server/controllers/RecipeController.js | 20 +- server/coverage/clover.xml | 113 ++++---- server/coverage/coverage-final.json | 20 +- server/coverage/lcov-report/index.html | 52 ++-- .../coverage/lcov-report/server/app.js.html | 2 +- .../controllers/CommentController.js.html | 44 +-- .../server/controllers/MailController.js.html | 2 +- .../controllers/RecipeController.js.html | 50 +++- .../server/controllers/UserController.js.html | 2 +- .../lcov-report/server/controllers/index.html | 38 +-- .../server/helpers/bcryptjs.js.html | 6 +- .../lcov-report/server/helpers/index.html | 2 +- .../lcov-report/server/helpers/jwt.js.html | 6 +- server/coverage/lcov-report/server/index.html | 2 +- .../server/middlewares/authentication.js.html | 20 +- .../server/middlewares/authorization.js.html | 50 ++-- .../server/middlewares/errorHandler.js.html | 38 +-- .../lcov-report/server/middlewares/index.html | 58 ++-- .../lcov-report/server/models/comment.js.html | 12 +- .../lcov-report/server/models/index.html | 2 +- .../lcov-report/server/models/index.js.html | 2 +- .../lcov-report/server/models/recipe.js.html | 8 +- .../lcov-report/server/models/user.js.html | 4 +- .../lcov-report/server/routes/index.html | 2 +- .../lcov-report/server/routes/index.js.html | 17 +- server/coverage/lcov.info | 156 +++++------ server/data/comment.json | 8 + .../20231116035529-create-comment.js | 8 +- server/models/comment.js | 4 +- server/routes/index.js | 5 +- server/seeders/20231116075119-comment-seed.js | 25 ++ 50 files changed, 967 insertions(+), 503 deletions(-) create mode 100644 client/src/components/Card.jsx create mode 100644 client/src/components/Sidebar.jsx create mode 100644 client/src/views/Detail.jsx delete mode 100644 server/__tests__/contact-mail.test.js delete mode 100644 server/__tests__/google-login.test.js create mode 100644 server/data/comment.json create mode 100644 server/seeders/20231116075119-comment-seed.js diff --git a/README.md b/README.md index ddcec95..c810ab4 100644 --- a/README.md +++ b/README.md @@ -366,6 +366,8 @@ not needed _Request Body_ ``` { + "imgUrl": "string", + "username": "string", "description": "string", "userId": integer } @@ -375,6 +377,7 @@ _Response (201 - Created)_ ``` { "id": integer, + "username": "string", "description": "string", "userId": integer, "createdAt": "date", @@ -412,6 +415,7 @@ _Response (201 - Created)_ ``` { "id": integer, + "username": "string", "description": "string", "userId": integer, "createdAt": "date", diff --git a/client/index.html b/client/index.html index 5781c9b..6950ff6 100644 --- a/client/index.html +++ b/client/index.html @@ -15,8 +15,6 @@ integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL" crossorigin="anonymous"> - - diff --git a/client/package-lock.json b/client/package-lock.json index dd42455..3b5f907 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -10,11 +10,14 @@ "dependencies": { "@react-oauth/google": "^0.11.1", "axios": "^1.6.2", + "bootstrap": "^5.3.2", "google-auth-library": "^9.2.0", "react": "^18.2.0", + "react-bootstrap": "^2.9.1", "react-dom": "^18.2.0", "react-router-dom": "^6.18.0", - "react-toastify": "^9.1.3" + "react-toastify": "^9.1.3", + "sharethis-reactjs": "^1.6.0" }, "devDependencies": { "@types/react": "^18.2.15", @@ -327,6 +330,17 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/runtime": { + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.2.tgz", + "integrity": "sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/template": { "version": "7.22.15", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", @@ -915,6 +929,29 @@ "node": ">= 8" } }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@react-aria/ssr": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.0.tgz", + "integrity": "sha512-Bz6BqP6ZorCme9tSWHZVmmY+s7AU8l6Vl2NUYmBzezD//fVHHfFo4lFBn5tBuAaJEm3AuCLaJQ6H2qhxNSb7zg==", + "dependencies": { + "@swc/helpers": "^0.5.0" + }, + "engines": { + "node": ">= 12" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0" + } + }, "node_modules/@react-oauth/google": { "version": "0.11.1", "resolved": "https://registry.npmjs.org/@react-oauth/google/-/google-0.11.1.tgz", @@ -932,6 +969,53 @@ "node": ">=14.0.0" } }, + "node_modules/@restart/hooks": { + "version": "0.4.11", + "resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.4.11.tgz", + "integrity": "sha512-Ft/ncTULZN6ldGHiF/k5qt72O8JyRMOeg0tApvCni8LkoiEahO+z3TNxfXIVGy890YtWVDvJAl662dVJSJXvMw==", + "dependencies": { + "dequal": "^2.0.3" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@restart/ui": { + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/@restart/ui/-/ui-1.6.6.tgz", + "integrity": "sha512-eC3puKuWE1SRYbojWHXnvCNHGgf3uzHCb6JOhnF4OXPibOIPEkR1sqDSkL643ydigxwh+ruCa1CmYHlzk7ikKA==", + "dependencies": { + "@babel/runtime": "^7.21.0", + "@popperjs/core": "^2.11.6", + "@react-aria/ssr": "^3.5.0", + "@restart/hooks": "^0.4.9", + "@types/warning": "^3.0.0", + "dequal": "^2.0.3", + "dom-helpers": "^5.2.0", + "uncontrollable": "^8.0.1", + "warning": "^4.0.3" + }, + "peerDependencies": { + "react": ">=16.14.0", + "react-dom": ">=16.14.0" + } + }, + "node_modules/@restart/ui/node_modules/uncontrollable": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-8.0.4.tgz", + "integrity": "sha512-ulRWYWHvscPFc0QQXvyJjY6LIXU56f0h8pQFvhxiKk5V1fcI8gp9Ht9leVAhrVjzqMw0BgjspBINx9r6oyJUvQ==", + "peerDependencies": { + "react": ">=16.14.0" + } + }, + "node_modules/@swc/helpers": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.3.tgz", + "integrity": "sha512-FaruWX6KdudYloq1AHD/4nU+UsMTdNE8CKyrseXWEcgjDAbvkwJg2QGPAnfIJLIWsjZOSPLOAykK6fuYp4vp4A==", + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@types/babel__core": { "version": "7.20.4", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.4.tgz", @@ -976,14 +1060,12 @@ "node_modules/@types/prop-types": { "version": "15.7.10", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.10.tgz", - "integrity": "sha512-mxSnDQxPqsZxmeShFH+uwQ4kO4gcJcGahjjMFeLbKE95IAZiiZyiEepGZjtXJ7hN/yfu0bu9xN2ajcU0JcxX6A==", - "dev": true + "integrity": "sha512-mxSnDQxPqsZxmeShFH+uwQ4kO4gcJcGahjjMFeLbKE95IAZiiZyiEepGZjtXJ7hN/yfu0bu9xN2ajcU0JcxX6A==" }, "node_modules/@types/react": { "version": "18.2.37", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.37.tgz", "integrity": "sha512-RGAYMi2bhRgEXT3f4B92WTohopH6bIXw05FuGlmJEnv/omEn190+QYEIYxIAuIBdKgboYYdVved2p1AxZVQnaw==", - "dev": true, "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -999,11 +1081,23 @@ "@types/react": "*" } }, + "node_modules/@types/react-transition-group": { + "version": "4.4.9", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.9.tgz", + "integrity": "sha512-ZVNmWumUIh5NhH8aMD9CR2hdW0fNuYInlocZHaZ+dgk/1K49j1w/HoAuK1ki+pgscQrOFRTlXeoURtuzEkV3dg==", + "dependencies": { + "@types/react": "*" + } + }, "node_modules/@types/scheduler": { "version": "0.16.6", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.6.tgz", - "integrity": "sha512-Vlktnchmkylvc9SnwwwozTv04L/e1NykF5vgoQ0XTmI8DD+wxfjQuHuvHS3p0r2jz2x2ghPs2h1FVeDirIteWA==", - "dev": true + "integrity": "sha512-Vlktnchmkylvc9SnwwwozTv04L/e1NykF5vgoQ0XTmI8DD+wxfjQuHuvHS3p0r2jz2x2ghPs2h1FVeDirIteWA==" + }, + "node_modules/@types/warning": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/warning/-/warning-3.0.3.tgz", + "integrity": "sha512-D1XC7WK8K+zZEveUPY+cf4+kgauk8N4eHr/XIHXGlGYkHLud6hK9lYfZk1ry1TNh798cZUCgb6MqGEG8DkJt6Q==" }, "node_modules/@ungap/structured-clone": { "version": "1.2.0", @@ -1276,6 +1370,24 @@ "node": "*" } }, + "node_modules/bootstrap": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.2.tgz", + "integrity": "sha512-D32nmNWiQHo94BKHLmOrdjlL05q1c8oxbtBphQFb9Z5to6eGRDCm0QgeaZ4zFBHzfg2++rqa2JkqCcxDy0sH0g==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/twbs" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/bootstrap" + } + ], + "peerDependencies": { + "@popperjs/core": "^2.11.8" + } + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -1380,6 +1492,11 @@ "node": ">=4" } }, + "node_modules/classnames": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz", + "integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==" + }, "node_modules/clsx": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", @@ -1443,8 +1560,7 @@ "node_modules/csstype": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", - "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==", - "dev": true + "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" }, "node_modules/debug": { "version": "4.3.4", @@ -1507,6 +1623,14 @@ "node": ">=0.4.0" } }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "engines": { + "node": ">=6" + } + }, "node_modules/doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -1519,6 +1643,15 @@ "node": ">=6.0.0" } }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, "node_modules/ecdsa-sig-formatter": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", @@ -2507,6 +2640,14 @@ "node": ">= 0.4" } }, + "node_modules/invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, "node_modules/is-array-buffer": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", @@ -3101,7 +3242,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -3351,13 +3491,24 @@ "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dev": true, "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, + "node_modules/prop-types-extra": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/prop-types-extra/-/prop-types-extra-1.1.1.tgz", + "integrity": "sha512-59+AHNnHYCdiC+vMwY52WmvP5dM3QLeoumYuEyceQDi9aEhtwN9zIQ2ZNo25sMyXnbh32h+P1ezDsUpUH3JAew==", + "dependencies": { + "react-is": "^16.3.2", + "warning": "^4.0.0" + }, + "peerDependencies": { + "react": ">=0.14.0" + } + }, "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", @@ -3403,6 +3554,35 @@ "node": ">=0.10.0" } }, + "node_modules/react-bootstrap": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-2.9.1.tgz", + "integrity": "sha512-ezgmh/ARCYp18LbZEqPp0ppvy+ytCmycDORqc8vXSKYV3cer4VH7OReV8uMOoKXmYzivJTxgzGHalGrHamryHA==", + "dependencies": { + "@babel/runtime": "^7.22.5", + "@restart/hooks": "^0.4.9", + "@restart/ui": "^1.6.6", + "@types/react-transition-group": "^4.4.6", + "classnames": "^2.3.2", + "dom-helpers": "^5.2.1", + "invariant": "^2.2.4", + "prop-types": "^15.8.1", + "prop-types-extra": "^1.1.0", + "react-transition-group": "^4.4.5", + "uncontrollable": "^7.2.1", + "warning": "^4.0.3" + }, + "peerDependencies": { + "@types/react": ">=16.14.8", + "react": ">=16.14.0", + "react-dom": ">=16.14.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/react-dom": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", @@ -3418,8 +3598,12 @@ "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" }, "node_modules/react-refresh": { "version": "0.14.0", @@ -3472,6 +3656,21 @@ "react-dom": ">=16" } }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, "node_modules/reflect.getprototypeof": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.4.tgz", @@ -3492,6 +3691,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/regenerator-runtime": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", + "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==" + }, "node_modules/regexp.prototype.flags": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz", @@ -3696,6 +3900,11 @@ "node": ">= 0.4" } }, + "node_modules/sharethis-reactjs": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/sharethis-reactjs/-/sharethis-reactjs-1.6.0.tgz", + "integrity": "sha512-cYrf9OgStwfOjPrHhk/0SpFuJNFQQU6OY7pWlhWtu3smuF+rYNF+vOL4aM/pwaP1QGwwrEuHTAqfpG3OWXPIag==" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -3873,6 +4082,11 @@ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" }, + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -3977,6 +4191,20 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/uncontrollable": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-7.2.1.tgz", + "integrity": "sha512-svtcfoTADIB0nT9nltgjujTi7BzVmwjZClOmskKu/E8FW9BXzg9os8OLr4f8Dlnk0rYWJIWr4wv9eKUXiQvQwQ==", + "dependencies": { + "@babel/runtime": "^7.6.3", + "@types/react": ">=16.9.11", + "invariant": "^2.2.4", + "react-lifecycles-compat": "^3.0.4" + }, + "peerDependencies": { + "react": ">=15.0.0" + } + }, "node_modules/update-browserslist-db": { "version": "1.0.13", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", @@ -4071,6 +4299,14 @@ } } }, + "node_modules/warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", diff --git a/client/package.json b/client/package.json index ba22669..7ab95d9 100644 --- a/client/package.json +++ b/client/package.json @@ -12,11 +12,14 @@ "dependencies": { "@react-oauth/google": "^0.11.1", "axios": "^1.6.2", + "bootstrap": "^5.3.2", "google-auth-library": "^9.2.0", "react": "^18.2.0", + "react-bootstrap": "^2.9.1", "react-dom": "^18.2.0", "react-router-dom": "^6.18.0", - "react-toastify": "^9.1.3" + "react-toastify": "^9.1.3", + "sharethis-reactjs": "^1.6.0" }, "devDependencies": { "@types/react": "^18.2.15", diff --git a/client/src/components/Card.jsx b/client/src/components/Card.jsx new file mode 100644 index 0000000..325e0d4 --- /dev/null +++ b/client/src/components/Card.jsx @@ -0,0 +1,22 @@ +import { Link } from "react-router-dom" + +export const Card = ({ recipe }) => { + + return ( +
+
+ ... +
+
{recipe.name}
+ + + +
+
+
+ ) + + +} \ No newline at end of file diff --git a/client/src/components/Navbar.jsx b/client/src/components/Navbar.jsx index 8b13789..c380e20 100644 --- a/client/src/components/Navbar.jsx +++ b/client/src/components/Navbar.jsx @@ -1 +1,7 @@ +export default function Navbar () { + + return( + <> + ) +} \ No newline at end of file diff --git a/client/src/components/Sidebar.jsx b/client/src/components/Sidebar.jsx new file mode 100644 index 0000000..72febe7 --- /dev/null +++ b/client/src/components/Sidebar.jsx @@ -0,0 +1 @@ +export default function Sidebar() {} \ No newline at end of file diff --git a/client/src/main.jsx b/client/src/main.jsx index 5a08c1f..dbf7c94 100644 --- a/client/src/main.jsx +++ b/client/src/main.jsx @@ -4,7 +4,7 @@ import App from './App.jsx' import { ToastContainer } from 'react-toastify'; import 'react-toastify/dist/ReactToastify.css'; -import './index.css' +// import './index.css' ReactDOM.createRoot(document.getElementById('root')).render( diff --git a/client/src/router/index.jsx b/client/src/router/index.jsx index a87c890..7882a8f 100644 --- a/client/src/router/index.jsx +++ b/client/src/router/index.jsx @@ -4,6 +4,7 @@ import Home from "../views/Home"; import Contact from "../views/Contact"; import Register from "../views/Register"; import Landing from "../views/Landing"; +import Detail from "../views/Detail"; const router = createBrowserRouter([ { @@ -19,13 +20,41 @@ const router = createBrowserRouter([ element: , }, { - path: "/home", - element: , - }, - { - path: "/contact-mail", - element: , + loader: () => { + const access_token = localStorage.getItem("access_token") + if (!access_token) { + throw redirect("/login") + } + return null; + }, + children: [ + { + path: "/home", // => /recipes + element: , + }, + { + path: "/recipe/:id", + element: , + }, + { + path: "/contact-mail", + element: , + }, + { + path: "/post/add", // => comment + element: <>, + }, + { + path: "/post/edit/:id", + element: <>, + }, + { + path: "/post/add", + element: <>, + }, + ] }, + {/* { path: "/", diff --git a/client/src/views/Detail.jsx b/client/src/views/Detail.jsx new file mode 100644 index 0000000..2e8b656 --- /dev/null +++ b/client/src/views/Detail.jsx @@ -0,0 +1,93 @@ +import { useEffect, useState } from "react"; +import { useParams } from "react-router-dom"; +import { toast } from "react-toastify"; +import axios from "axios"; +import { InlineReactionButtons } from 'sharethis-reactjs'; + +export default function Detail() { + const { id } = useParams(); + const [data, setData] = useState({}); + const token = localStorage.getItem("access_token"); + + useEffect(() => { + const fetchData = async () => { + try { + const { data } = await axios({ + url: `http://localhost:3000/recipe/${id}`, + method: "get", + headers: { + Authorization: `Bearer ${token}` + } + }) + + setData(data) + + console.log(data) + } + catch ({ response }) { + toast.error(response.data.message, { + position: "top-center", + autoClose: 2000, + hideProgressBar: true, + closeOnClick: true, + pauseOnHover: true, + draggable: true, + progress: undefined, + theme: "dark", + }); + + console.log({ response }) + } + } + fetchData() + }, []) + + + return ( + <> +
+
+
+
{data.name}
+
+

{data.name}

+
+ {data.description} +
+

{data.steps}

+
+
+
+ + +
+ + ) + + +} \ No newline at end of file diff --git a/client/src/views/Home.jsx b/client/src/views/Home.jsx index a21c4cc..6c2a1fe 100644 --- a/client/src/views/Home.jsx +++ b/client/src/views/Home.jsx @@ -1,28 +1,103 @@ -import { useNavigate } from "react-router-dom"; +import { useEffect, useState } from "react"; +import { toast } from "react-toastify"; +import axios from "axios"; +import Pagination from 'react-bootstrap/Pagination'; +import Sidebar from "../components/Sidebar"; +import { Card } from "../components/Card"; export default function Home() { - const navigate = useNavigate(); - - - const handleLogout = async () => { - localStorage.clear(); - // toast.info('🖐️ SEE YOU LATER 🖐️', { - // position: "top-center", - // autoClose: 5000, - // hideProgressBar: false, - // closeOnClick: true, - // pauseOnHover: true, - // draggable: true, - // progress: undefined, - // theme: "light", - // }); - navigate("/landing"); - } + const [data, setData] = useState([]); + const [search, setSearch] = useState(""); + const [filter, setFilter] = useState(""); + const [page, setPage] = useState(1); + const [totalPage, setTotalPage] = useState(1); + + const token = localStorage.getItem("access_token"); + + + useEffect(() => { + const fetchData = async () => { + try { + const { data: result } = await axios({ + url: "http://localhost:3000/recipes", + method: "get", + headers: { + Authorization: `Bearer ${token}` + }, + params: { + search, + filter, + page + } + }) + let { results, totalPage, total } = result + setTotalPage(totalPage) + setData(results) - return( + } + catch (error) { + toast.error(response.data.message, { + position: "top-center", + autoClose: 2000, + hideProgressBar: true, + closeOnClick: true, + pauseOnHover: true, + draggable: true, + progress: undefined, + theme: "dark", + }); + + console.log({ response }) + } + } + fetchData() + }, [search, filter, page]) + + + const PageNumber = () => { + let result = [] + for (let number = 1; number <= totalPage; number++) { + result.push( + setPage(number)} key={number} active={number === page}> + {number} + + ) + } + return result + } + + return ( <> +
+ + + +
+ {data.map((d) => ( + + ))} +
+ +
+ + setPage(1)} /> + setPage(page > 1 ? page - 1 : 1)} /> + + + setPage(page < totalPage ? page + 1 : totalPage)} /> + setPage(totalPage)} /> + + +
+ +
) diff --git a/server/__tests__/comment-add.test.js b/server/__tests__/comment-add.test.js index 73c50eb..4e3585e 100644 --- a/server/__tests__/comment-add.test.js +++ b/server/__tests__/comment-add.test.js @@ -20,6 +20,8 @@ beforeAll(async () => { tokenAdm = signToken({id: admin.id}); comment = { + "imgUrl": "http://www.com", + "username": "Any", "description": "Any", "userId": admin.id, } @@ -37,7 +39,9 @@ describe("/comment/add", () => { expect(status).toBe(201); expect(body).toBeInstanceOf(Object); - expect(body).toHaveProperty("id", expect.any(Number)); + expect(body).toHaveProperty("id", expect.any(Number)); + expect(body).toHaveProperty("imgUrl", expect.any(String));; + expect(body).toHaveProperty("username", expect.any(String)); expect(body).toHaveProperty("description", expect.any(String));; expect(body).toHaveProperty("userId", expect.any(Number)); }) diff --git a/server/__tests__/comment-delete.test.js b/server/__tests__/comment-delete.test.js index 321474c..3d06af3 100644 --- a/server/__tests__/comment-delete.test.js +++ b/server/__tests__/comment-delete.test.js @@ -32,18 +32,24 @@ beforeAll(async () => { await queryInterface.bulkInsert('Comments', [ { + "imgUrl": "http://www.com", + "username": "Any", "description": "Any", "userId": admin.id, "createdAt": dateNow, "updatedAt": dateNow }, { + "imgUrl": "http://www.com", + "username": "Any", "description": "Any", "userId": staff.id, "createdAt": dateNow, "updatedAt": dateNow }, { + "imgUrl": "http://www.com", + "username": "Any", "description": "Any", "userId": admin.id, "createdAt": dateNow, diff --git a/server/__tests__/comment-edit.test.js b/server/__tests__/comment-edit.test.js index 35a1dbd..6194fbc 100644 --- a/server/__tests__/comment-edit.test.js +++ b/server/__tests__/comment-edit.test.js @@ -32,12 +32,16 @@ beforeAll(async () => { await queryInterface.bulkInsert('Comments', [ { + "imgUrl": "http://www.com", + "username": "Any", "description": "Any", "userId": admin.id, "createdAt": dateNow, "updatedAt": dateNow }, { + "imgUrl": "http://www.com", + "username": "Any", "description": "Any", "userId": staff.id, "createdAt": dateNow, @@ -64,11 +68,11 @@ describe("/comment/edit/:id", () => { expect(status).toBe(200); expect(body).toBeInstanceOf(Object); - expect(body).toHaveProperty("id", expect.any(Number)); - expect(body.id).toBe(commentId); - expect(body).toHaveProperty("description", expect.any(String)); + expect(body).toHaveProperty("id", expect.any(Number)); + expect(body).toHaveProperty("imgUrl", expect.any(String));; + expect(body).toHaveProperty("username", expect.any(String)); + expect(body).toHaveProperty("description", expect.any(String));; expect(body).toHaveProperty("userId", expect.any(Number)); - expect(body.userId).toBe(admin.id); }) diff --git a/server/__tests__/contact-mail.test.js b/server/__tests__/contact-mail.test.js deleted file mode 100644 index e69de29..0000000 diff --git a/server/__tests__/google-login.test.js b/server/__tests__/google-login.test.js deleted file mode 100644 index 1a87ed8..0000000 --- a/server/__tests__/google-login.test.js +++ /dev/null @@ -1,109 +0,0 @@ -const app = require('../app'); -const request = require("supertest"); -const { sequelize, User } = require("../models"); -const { queryInterface } = sequelize; - -const user1 = { - email: "admin@email.com", - password: "12345" -} - -const user2 = { - email: "staff@email.com", - password: "12345" -} - - -beforeAll(async () => { - await User.create(user1); -}) - -describe("/google-login", () => { - - //Berhasil login dan diberikan access token - test("success login and get access token", async () => { - let {status, body} = await request(app) - .post("/google-login") - .send(user1); - - console.log(status, body); - expect(status).toBe(200); - expect(body).toBeInstanceOf(Object); - expect(body).toHaveProperty("access_token", expect.any(String)); - }) - - - //Email tidak diberikan / tidak diinput - test("failed login without email", async () => { - let {status, body} = await request(app) - .post("/google-login") - .send({ - email: "", - password: "12345678" - }); - - console.log(status, body); - expect(status).toBe(400); - expect(body).toBeInstanceOf(Object); - expect(body).toHaveProperty("message", expect.any(String)); - expect(body.message).toContain("Error invalid email or Password"); - }) - - - //Password tidak diberikan / tidak diinput - test("failed login without password", async () => { - let {status, body} = await request(app) - .post("/google-login") - .send({ - email: "admin@email.com", - password: "" - }); - - console.log(status, body); - expect(status).toBe(400); - expect(body).toBeInstanceOf(Object); - expect(body).toHaveProperty("message", expect.any(String)); - expect(body.message).toContain("Error invalid email or Password"); - }) - - - // Email diberikan invalid / tidak terdaftar - test("failed login with invalid email", async () => { - let {status, body} = await request(app) - .post("/google-login") - .send(user2); - - console.log(status, body); - expect(status).toBe(401); - expect(body).toBeInstanceOf(Object); - expect(body).toHaveProperty("message", expect.any(String)); - expect(body.message).toContain("User not found or Password not matched"); - }) - - - // Password diberikan salah / tidak match - test("failed login with invalid password", async () => { - let {status, body} = await request(app) - .post("/google-login") - .send({ - email: "admin@email.com", - password: "09876" - }); - - console.log(status, body); - expect(status).toBe(401); - expect(body).toBeInstanceOf(Object); - expect(body).toHaveProperty("message", expect.any(String)); - expect(body.message).toContain("User not found or Password not matched"); - }) - -}) - - -afterAll(async () => { - await queryInterface.bulkDelete("Users",null, { - truncate: true, - cascade: true, - restartIdentity: true - }) -}) \ No newline at end of file diff --git a/server/__tests__/recipe-id.test.js b/server/__tests__/recipe-id.test.js index ac0cd86..a6f0da2 100644 --- a/server/__tests__/recipe-id.test.js +++ b/server/__tests__/recipe-id.test.js @@ -5,7 +5,7 @@ const { signToken } = require('../helpers/jwt'); const { queryInterface } = sequelize; let user = { - username: "string", + username: "admin", email: "admin@email.com", password: "12345" } @@ -22,7 +22,6 @@ beforeAll(async () => { await queryInterface.bulkInsert('Recipes', [ { - "number": 1, "id": "133fcdec-4595-44be-a302-02df4c209451", "name": "Keto Sesame Tuna and Egg Salad", "description": "This quick and easy tuna and egg salad is combined with aromatic scallions and parsley and coated in a sesame, lemon mayo.\n\nThis is a great option for stuffing lettuce wraps or topping low carb crackers.", @@ -62,7 +61,6 @@ beforeAll(async () => { "updatedAt": dateNow, }, { - "number": 2, "id": "ccef957b-351f-4c14-b33c-ef050045b877", "name": "Keto Vanilla Butter Latte", "description": "This creamy Keto vanilla BULLETPROOF® coffee recipe is rich in fats, frothy plant-based milk, and a hint of sweet vanilla. This Keto vanilla butter coffee makes a great fat-fueled breakfast option to kickstart your day. Alternatively, this is perfect served with a slice of low-carb cake as an afternoon treat!\n\n### What ingredients are in this Keto vanilla butter latte?\n\nThis Keto vanilla BULLETPROOF® coffee is prepared with hot almond milk infused with sweet vanilla extract. The milk is then blended with instant coffee and a hearty helping of unsalted butter, before being blitzed until super frothy! We have used a handheld stick blender to froth and blend our Keto coffee, however, you may use a freestanding blender for this if preferred.\n\n### Here are some tips to make Keto vanilla butter coffee?\n\nThis Keto BULLETPROOF® coffee provides a generous serving of fats from unsalted butter. If preferred you may swap the butter for coconut oil or BULLETPROOF® brain octane oil (MCT Oil) or even a blend of fats. You may also swap the erythritol for your preference in low-carb sweetener or omit it entirely if desired. Please be sure to adjust your macros for any changes made.\n\n### Can you use freshly brewed coffee in this Keto recipe?\n\nWhile this recipe calls for instant coffee, you can definitely substitute it for espresso or freshly brewed coffee, just make sure its strong.\n\nBULLETPROOF® is a registered trademark owned by Bulletproof Digital, Inc.", @@ -104,7 +102,7 @@ const validId = "7631d8cb-2973-4f7c-833b-daeb4190c278"; describe("/recipe/:id", () => { //Berhasil mendapatkan 1 Entitas Utama sesuai dengan params id yang diberikan - test("success get one recipe by id (200)", async () => { + test.skip("success get one recipe by id (200)", async () => { let { status, body } = await request(app) .get(`/recipe/${validId}`) .set("Authorization", `Bearer ${tokenAdm}`) diff --git a/server/controllers/CommentController.js b/server/controllers/CommentController.js index d84d8dd..64278cc 100644 --- a/server/controllers/CommentController.js +++ b/server/controllers/CommentController.js @@ -1,6 +1,6 @@ const { Comment } = require("../models"); -module.exports = class MailController { +module.exports = class CommentController { static async addComment(req, res, next) { try { diff --git a/server/controllers/MailController.js b/server/controllers/MailController.js index 44f3760..ca6c87c 100644 --- a/server/controllers/MailController.js +++ b/server/controllers/MailController.js @@ -6,8 +6,6 @@ module.exports = class MailController { try { const { username, email, message } = req.body; - // console.log(username, email, message) - if(!username || !email || !message) throw ({name: "IncompleteData", message: "Please fill the required form!"}) const transporter = nodemailer.createTransport({ diff --git a/server/controllers/RecipeController.js b/server/controllers/RecipeController.js index 54feac4..c18cbb5 100644 --- a/server/controllers/RecipeController.js +++ b/server/controllers/RecipeController.js @@ -5,7 +5,7 @@ module.exports = class RecipeController { static async recipes(req, res, next) { - const { search, filter, page } = req.query; + const { search, filter, page = 1 } = req.query; const options = { method: 'GET', @@ -60,20 +60,28 @@ module.exports = class RecipeController { } }) - const offset = !Number(page) ? 0 : 8 * (page - 1) - const newPage = offset + 8 - const result = mappedRecipes.slice(offset, newPage) + const lim = 8 + const offset = 8 * (page - 1) + const total = recipes.length; + const totalPage = Math.ceil(total / 8) + const results = mappedRecipes.slice(offset, offset + lim) + + res.status(200).json({ + total, + totalPage, + results, + }); - res.status(200).json(result); } catch (error) { next(error); + console.log(error) } } static async recipeById(req, res, next) { - const {id} = req.params + const { id } = req.params const options = { method: 'GET', diff --git a/server/coverage/clover.xml b/server/coverage/clover.xml index 5494c94..6df1984 100644 --- a/server/coverage/clover.xml +++ b/server/coverage/clover.xml @@ -1,7 +1,7 @@ - - - + + + @@ -19,24 +19,24 @@ - + - + - - - + + + - - - - + + + + @@ -54,11 +54,11 @@ - + - + @@ -71,19 +71,22 @@ - + + - - - - - + + + + + - + + + @@ -135,8 +138,8 @@ - - + + @@ -146,57 +149,57 @@ - + - + - + - - - - - - - - - + + + + + + + + + - + - - - - - - - + + + + + + + - + - + - - - - + + + + @@ -211,7 +214,7 @@ - + @@ -252,7 +255,7 @@ - + @@ -274,12 +277,12 @@ - - - - + + + + diff --git a/server/coverage/coverage-final.json b/server/coverage/coverage-final.json index a2accea..4f9505c 100644 --- a/server/coverage/coverage-final.json +++ b/server/coverage/coverage-final.json @@ -1,16 +1,16 @@ {"D:\\IP-RMT42\\server\\app.js": {"path":"D:\\IP-RMT42\\server\\app.js","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":3,"column":3}},"1":{"start":{"line":2,"column":4},"end":{"line":2,"column":30}},"2":{"start":{"line":5,"column":16},"end":{"line":5,"column":34}},"3":{"start":{"line":6,"column":12},"end":{"line":6,"column":21}},"4":{"start":{"line":7,"column":13},"end":{"line":7,"column":28}},"5":{"start":{"line":9,"column":0},"end":{"line":9,"column":15}},"6":{"start":{"line":11,"column":0},"end":{"line":11,"column":48}},"7":{"start":{"line":13,"column":0},"end":{"line":13,"column":24}},"8":{"start":{"line":15,"column":0},"end":{"line":15,"column":29}},"9":{"start":{"line":18,"column":0},"end":{"line":18,"column":21}}},"fnMap":{},"branchMap":{"0":{"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":3}},"type":"if","locations":[{"start":{"line":1,"column":0},"end":{"line":3,"column":3}},{"start":{},"end":{}}],"line":1}},"s":{"0":1,"1":1,"2":1,"3":1,"4":1,"5":1,"6":1,"7":1,"8":1,"9":1},"f":{},"b":{"0":[1,0]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"3c4696dddacd4890423030158e274d3635612ff9"} -,"D:\\IP-RMT42\\server\\controllers\\CommentController.js": {"path":"D:\\IP-RMT42\\server\\controllers\\CommentController.js","statementMap":{"0":{"start":{"line":1,"column":20},"end":{"line":1,"column":40}},"1":{"start":{"line":3,"column":0},"end":{"line":34,"column":1}},"2":{"start":{"line":6,"column":8},"end":{"line":11,"column":9}},"3":{"start":{"line":7,"column":28},"end":{"line":7,"column":86}},"4":{"start":{"line":8,"column":12},"end":{"line":8,"column":42}},"5":{"start":{"line":10,"column":12},"end":{"line":10,"column":24}},"6":{"start":{"line":15,"column":8},"end":{"line":21,"column":9}},"7":{"start":{"line":16,"column":26},"end":{"line":16,"column":63}},"8":{"start":{"line":17,"column":25},"end":{"line":17,"column":76}},"9":{"start":{"line":18,"column":12},"end":{"line":18,"column":41}},"10":{"start":{"line":20,"column":12},"end":{"line":20,"column":24}},"11":{"start":{"line":25,"column":8},"end":{"line":31,"column":9}},"12":{"start":{"line":26,"column":26},"end":{"line":26,"column":63}},"13":{"start":{"line":27,"column":12},"end":{"line":27,"column":36}},"14":{"start":{"line":28,"column":12},"end":{"line":28,"column":75}},"15":{"start":{"line":30,"column":12},"end":{"line":30,"column":24}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":5,"column":4},"end":{"line":5,"column":5}},"loc":{"start":{"line":5,"column":44},"end":{"line":12,"column":5}},"line":5},"1":{"name":"(anonymous_1)","decl":{"start":{"line":14,"column":4},"end":{"line":14,"column":5}},"loc":{"start":{"line":14,"column":45},"end":{"line":22,"column":5}},"line":14},"2":{"name":"(anonymous_2)","decl":{"start":{"line":24,"column":4},"end":{"line":24,"column":5}},"loc":{"start":{"line":24,"column":47},"end":{"line":32,"column":5}},"line":24}},"branchMap":{},"s":{"0":1,"1":1,"2":1,"3":1,"4":1,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0},"f":{"0":1,"1":0,"2":0},"b":{},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"a8b718399e09e4b5cdce82973780d5f2e053a75a"} +,"D:\\IP-RMT42\\server\\controllers\\CommentController.js": {"path":"D:\\IP-RMT42\\server\\controllers\\CommentController.js","statementMap":{"0":{"start":{"line":1,"column":20},"end":{"line":1,"column":40}},"1":{"start":{"line":3,"column":0},"end":{"line":34,"column":1}},"2":{"start":{"line":6,"column":8},"end":{"line":11,"column":9}},"3":{"start":{"line":7,"column":28},"end":{"line":7,"column":86}},"4":{"start":{"line":8,"column":12},"end":{"line":8,"column":42}},"5":{"start":{"line":10,"column":12},"end":{"line":10,"column":24}},"6":{"start":{"line":15,"column":8},"end":{"line":21,"column":9}},"7":{"start":{"line":16,"column":26},"end":{"line":16,"column":63}},"8":{"start":{"line":17,"column":25},"end":{"line":17,"column":76}},"9":{"start":{"line":18,"column":12},"end":{"line":18,"column":41}},"10":{"start":{"line":20,"column":12},"end":{"line":20,"column":24}},"11":{"start":{"line":25,"column":8},"end":{"line":31,"column":9}},"12":{"start":{"line":26,"column":26},"end":{"line":26,"column":63}},"13":{"start":{"line":27,"column":12},"end":{"line":27,"column":36}},"14":{"start":{"line":28,"column":12},"end":{"line":28,"column":75}},"15":{"start":{"line":30,"column":12},"end":{"line":30,"column":24}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":5,"column":4},"end":{"line":5,"column":5}},"loc":{"start":{"line":5,"column":44},"end":{"line":12,"column":5}},"line":5},"1":{"name":"(anonymous_1)","decl":{"start":{"line":14,"column":4},"end":{"line":14,"column":5}},"loc":{"start":{"line":14,"column":45},"end":{"line":22,"column":5}},"line":14},"2":{"name":"(anonymous_2)","decl":{"start":{"line":24,"column":4},"end":{"line":24,"column":5}},"loc":{"start":{"line":24,"column":47},"end":{"line":32,"column":5}},"line":24}},"branchMap":{},"s":{"0":1,"1":1,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":1,"12":1,"13":1,"14":1,"15":0},"f":{"0":0,"1":0,"2":1},"b":{},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"a8b718399e09e4b5cdce82973780d5f2e053a75a"} ,"D:\\IP-RMT42\\server\\controllers\\MailController.js": {"path":"D:\\IP-RMT42\\server\\controllers\\MailController.js","statementMap":{"0":{"start":{"line":1,"column":19},"end":{"line":1,"column":40}},"1":{"start":{"line":3,"column":0},"end":{"line":39,"column":1}},"2":{"start":{"line":6,"column":8},"end":{"line":35,"column":9}},"3":{"start":{"line":7,"column":49},"end":{"line":7,"column":57}},"4":{"start":{"line":11,"column":12},"end":{"line":11,"column":123}},"5":{"start":{"line":11,"column":48},"end":{"line":11,"column":123}},"6":{"start":{"line":13,"column":32},"end":{"line":19,"column":14}},"7":{"start":{"line":21,"column":32},"end":{"line":26,"column":13}},"8":{"start":{"line":28,"column":12},"end":{"line":28,"column":52}},"9":{"start":{"line":30,"column":12},"end":{"line":30,"column":80}},"10":{"start":{"line":33,"column":12},"end":{"line":33,"column":24}},"11":{"start":{"line":34,"column":12},"end":{"line":34,"column":30}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":5,"column":4},"end":{"line":5,"column":5}},"loc":{"start":{"line":5,"column":42},"end":{"line":36,"column":5}},"line":5}},"branchMap":{"0":{"loc":{"start":{"line":11,"column":12},"end":{"line":11,"column":123}},"type":"if","locations":[{"start":{"line":11,"column":12},"end":{"line":11,"column":123}},{"start":{},"end":{}}],"line":11},"1":{"loc":{"start":{"line":11,"column":15},"end":{"line":11,"column":46}},"type":"binary-expr","locations":[{"start":{"line":11,"column":15},"end":{"line":11,"column":24}},{"start":{"line":11,"column":28},"end":{"line":11,"column":34}},{"start":{"line":11,"column":38},"end":{"line":11,"column":46}}],"line":11}},"s":{"0":1,"1":1,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0},"f":{"0":0},"b":{"0":[0,0],"1":[0,0,0]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"ebab0cfc4a56fd1dce951694db907c08d7e4a773"} -,"D:\\IP-RMT42\\server\\controllers\\RecipeController.js": {"path":"D:\\IP-RMT42\\server\\controllers\\RecipeController.js","statementMap":{"0":{"start":{"line":1,"column":14},"end":{"line":1,"column":30}},"1":{"start":{"line":2,"column":19},"end":{"line":2,"column":39}},"2":{"start":{"line":4,"column":0},"end":{"line":118,"column":1}},"3":{"start":{"line":8,"column":37},"end":{"line":8,"column":46}},"4":{"start":{"line":10,"column":20},"end":{"line":26,"column":5}},"5":{"start":{"line":28,"column":4},"end":{"line":30,"column":5}},"6":{"start":{"line":29,"column":6},"end":{"line":29,"column":35}},"7":{"start":{"line":32,"column":4},"end":{"line":35,"column":5}},"8":{"start":{"line":33,"column":6},"end":{"line":33,"column":70}},"9":{"start":{"line":34,"column":6},"end":{"line":34,"column":52}},"10":{"start":{"line":37,"column":4},"end":{"line":70,"column":5}},"11":{"start":{"line":38,"column":23},"end":{"line":38,"column":51}},"12":{"start":{"line":39,"column":22},"end":{"line":39,"column":35}},"13":{"start":{"line":40,"column":28},"end":{"line":61,"column":8}},"14":{"start":{"line":41,"column":8},"end":{"line":60,"column":9}},"15":{"start":{"line":47,"column":60},"end":{"line":47,"column":111}},"16":{"start":{"line":63,"column":21},"end":{"line":63,"column":55}},"17":{"start":{"line":64,"column":22},"end":{"line":64,"column":32}},"18":{"start":{"line":65,"column":21},"end":{"line":65,"column":57}},"19":{"start":{"line":67,"column":6},"end":{"line":67,"column":35}},"20":{"start":{"line":69,"column":6},"end":{"line":69,"column":18}},"21":{"start":{"line":76,"column":17},"end":{"line":76,"column":27}},"22":{"start":{"line":78,"column":20},"end":{"line":85,"column":5}},"23":{"start":{"line":87,"column":4},"end":{"line":114,"column":5}},"24":{"start":{"line":88,"column":23},"end":{"line":88,"column":51}},"25":{"start":{"line":89,"column":21},"end":{"line":89,"column":34}},"26":{"start":{"line":91,"column":6},"end":{"line":110,"column":9}},"27":{"start":{"line":97,"column":58},"end":{"line":97,"column":109}},"28":{"start":{"line":113,"column":6},"end":{"line":113,"column":18}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":6,"column":2},"end":{"line":6,"column":3}},"loc":{"start":{"line":6,"column":39},"end":{"line":71,"column":3}},"line":6},"1":{"name":"(anonymous_1)","decl":{"start":{"line":40,"column":40},"end":{"line":40,"column":41}},"loc":{"start":{"line":40,"column":59},"end":{"line":61,"column":7}},"line":40},"2":{"name":"(anonymous_2)","decl":{"start":{"line":47,"column":46},"end":{"line":47,"column":47}},"loc":{"start":{"line":47,"column":60},"end":{"line":47,"column":111}},"line":47},"3":{"name":"(anonymous_3)","decl":{"start":{"line":74,"column":2},"end":{"line":74,"column":3}},"loc":{"start":{"line":74,"column":42},"end":{"line":115,"column":3}},"line":74},"4":{"name":"(anonymous_4)","decl":{"start":{"line":97,"column":44},"end":{"line":97,"column":45}},"loc":{"start":{"line":97,"column":58},"end":{"line":97,"column":109}},"line":97}},"branchMap":{"0":{"loc":{"start":{"line":28,"column":4},"end":{"line":30,"column":5}},"type":"if","locations":[{"start":{"line":28,"column":4},"end":{"line":30,"column":5}},{"start":{},"end":{}}],"line":28},"1":{"loc":{"start":{"line":32,"column":4},"end":{"line":35,"column":5}},"type":"if","locations":[{"start":{"line":32,"column":4},"end":{"line":35,"column":5}},{"start":{},"end":{}}],"line":32},"2":{"loc":{"start":{"line":63,"column":21},"end":{"line":63,"column":55}},"type":"cond-expr","locations":[{"start":{"line":63,"column":37},"end":{"line":63,"column":38}},{"start":{"line":63,"column":41},"end":{"line":63,"column":55}}],"line":63}},"s":{"0":1,"1":1,"2":1,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0},"f":{"0":0,"1":0,"2":0,"3":0,"4":0},"b":{"0":[0,0],"1":[0,0],"2":[0,0]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"91bf0a2654e4ad11fc7d0cf5f557a16740e6f686"} +,"D:\\IP-RMT42\\server\\controllers\\RecipeController.js": {"path":"D:\\IP-RMT42\\server\\controllers\\RecipeController.js","statementMap":{"0":{"start":{"line":1,"column":14},"end":{"line":1,"column":30}},"1":{"start":{"line":2,"column":19},"end":{"line":2,"column":39}},"2":{"start":{"line":4,"column":0},"end":{"line":126,"column":1}},"3":{"start":{"line":8,"column":41},"end":{"line":8,"column":50}},"4":{"start":{"line":10,"column":20},"end":{"line":26,"column":5}},"5":{"start":{"line":28,"column":4},"end":{"line":30,"column":5}},"6":{"start":{"line":29,"column":6},"end":{"line":29,"column":35}},"7":{"start":{"line":32,"column":4},"end":{"line":35,"column":5}},"8":{"start":{"line":33,"column":6},"end":{"line":33,"column":70}},"9":{"start":{"line":34,"column":6},"end":{"line":34,"column":52}},"10":{"start":{"line":37,"column":4},"end":{"line":78,"column":5}},"11":{"start":{"line":38,"column":23},"end":{"line":38,"column":51}},"12":{"start":{"line":39,"column":22},"end":{"line":39,"column":35}},"13":{"start":{"line":40,"column":28},"end":{"line":61,"column":8}},"14":{"start":{"line":41,"column":8},"end":{"line":60,"column":9}},"15":{"start":{"line":47,"column":60},"end":{"line":47,"column":111}},"16":{"start":{"line":63,"column":18},"end":{"line":63,"column":19}},"17":{"start":{"line":64,"column":21},"end":{"line":64,"column":35}},"18":{"start":{"line":65,"column":20},"end":{"line":65,"column":34}},"19":{"start":{"line":66,"column":24},"end":{"line":66,"column":44}},"20":{"start":{"line":67,"column":22},"end":{"line":67,"column":63}},"21":{"start":{"line":69,"column":6},"end":{"line":73,"column":9}},"22":{"start":{"line":76,"column":6},"end":{"line":76,"column":18}},"23":{"start":{"line":77,"column":6},"end":{"line":77,"column":24}},"24":{"start":{"line":84,"column":19},"end":{"line":84,"column":29}},"25":{"start":{"line":86,"column":20},"end":{"line":93,"column":5}},"26":{"start":{"line":95,"column":4},"end":{"line":122,"column":5}},"27":{"start":{"line":96,"column":23},"end":{"line":96,"column":51}},"28":{"start":{"line":97,"column":21},"end":{"line":97,"column":34}},"29":{"start":{"line":99,"column":6},"end":{"line":118,"column":9}},"30":{"start":{"line":105,"column":58},"end":{"line":105,"column":109}},"31":{"start":{"line":121,"column":6},"end":{"line":121,"column":18}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":6,"column":2},"end":{"line":6,"column":3}},"loc":{"start":{"line":6,"column":39},"end":{"line":79,"column":3}},"line":6},"1":{"name":"(anonymous_1)","decl":{"start":{"line":40,"column":40},"end":{"line":40,"column":41}},"loc":{"start":{"line":40,"column":59},"end":{"line":61,"column":7}},"line":40},"2":{"name":"(anonymous_2)","decl":{"start":{"line":47,"column":46},"end":{"line":47,"column":47}},"loc":{"start":{"line":47,"column":60},"end":{"line":47,"column":111}},"line":47},"3":{"name":"(anonymous_3)","decl":{"start":{"line":82,"column":2},"end":{"line":82,"column":3}},"loc":{"start":{"line":82,"column":42},"end":{"line":123,"column":3}},"line":82},"4":{"name":"(anonymous_4)","decl":{"start":{"line":105,"column":44},"end":{"line":105,"column":45}},"loc":{"start":{"line":105,"column":58},"end":{"line":105,"column":109}},"line":105}},"branchMap":{"0":{"loc":{"start":{"line":8,"column":28},"end":{"line":8,"column":36}},"type":"default-arg","locations":[{"start":{"line":8,"column":35},"end":{"line":8,"column":36}}],"line":8},"1":{"loc":{"start":{"line":28,"column":4},"end":{"line":30,"column":5}},"type":"if","locations":[{"start":{"line":28,"column":4},"end":{"line":30,"column":5}},{"start":{},"end":{}}],"line":28},"2":{"loc":{"start":{"line":32,"column":4},"end":{"line":35,"column":5}},"type":"if","locations":[{"start":{"line":32,"column":4},"end":{"line":35,"column":5}},{"start":{},"end":{}}],"line":32}},"s":{"0":1,"1":1,"2":1,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":0,"31":0},"f":{"0":0,"1":0,"2":0,"3":0,"4":0},"b":{"0":[0],"1":[0,0],"2":[0,0]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"0bc3423447e8ca0eb987af0a13abb8b431b3a237"} ,"D:\\IP-RMT42\\server\\controllers\\UserController.js": {"path":"D:\\IP-RMT42\\server\\controllers\\UserController.js","statementMap":{"0":{"start":{"line":1,"column":28},"end":{"line":1,"column":58}},"1":{"start":{"line":2,"column":22},"end":{"line":2,"column":47}},"2":{"start":{"line":3,"column":17},"end":{"line":3,"column":37}},"3":{"start":{"line":4,"column":25},"end":{"line":4,"column":55}},"4":{"start":{"line":5,"column":15},"end":{"line":5,"column":33}},"5":{"start":{"line":8,"column":0},"end":{"line":92,"column":1}},"6":{"start":{"line":11,"column":8},"end":{"line":22,"column":9}},"7":{"start":{"line":12,"column":50},"end":{"line":12,"column":58}},"8":{"start":{"line":13,"column":25},"end":{"line":13,"column":73}},"9":{"start":{"line":15,"column":12},"end":{"line":18,"column":15}},"10":{"start":{"line":21,"column":12},"end":{"line":21,"column":24}},"11":{"start":{"line":27,"column":8},"end":{"line":58,"column":9}},"12":{"start":{"line":28,"column":40},"end":{"line":28,"column":48}},"13":{"start":{"line":30,"column":12},"end":{"line":33,"column":13}},"14":{"start":{"line":31,"column":16},"end":{"line":31,"column":85}},"15":{"start":{"line":32,"column":16},"end":{"line":32,"column":23}},"16":{"start":{"line":35,"column":12},"end":{"line":38,"column":13}},"17":{"start":{"line":36,"column":16},"end":{"line":36,"column":85}},"18":{"start":{"line":37,"column":16},"end":{"line":37,"column":23}},"19":{"start":{"line":40,"column":25},"end":{"line":40,"column":65}},"20":{"start":{"line":41,"column":12},"end":{"line":44,"column":13}},"21":{"start":{"line":42,"column":16},"end":{"line":42,"column":101}},"22":{"start":{"line":43,"column":16},"end":{"line":43,"column":23}},"23":{"start":{"line":46,"column":36},"end":{"line":46,"column":76}},"24":{"start":{"line":47,"column":12},"end":{"line":50,"column":13}},"25":{"start":{"line":48,"column":16},"end":{"line":48,"column":101}},"26":{"start":{"line":49,"column":16},"end":{"line":49,"column":23}},"27":{"start":{"line":52,"column":33},"end":{"line":52,"column":59}},"28":{"start":{"line":53,"column":12},"end":{"line":53,"column":51}},"29":{"start":{"line":56,"column":12},"end":{"line":56,"column":30}},"30":{"start":{"line":57,"column":12},"end":{"line":57,"column":24}},"31":{"start":{"line":63,"column":8},"end":{"line":63,"column":40}},"32":{"start":{"line":65,"column":8},"end":{"line":89,"column":9}},"33":{"start":{"line":66,"column":27},"end":{"line":69,"column":14}},"34":{"start":{"line":70,"column":28},"end":{"line":70,"column":47}},"35":{"start":{"line":72,"column":12},"end":{"line":72,"column":32}},"36":{"start":{"line":74,"column":40},"end":{"line":82,"column":14}},"37":{"start":{"line":84,"column":33},"end":{"line":84,"column":59}},"38":{"start":{"line":85,"column":12},"end":{"line":85,"column":71}},"39":{"start":{"line":88,"column":12},"end":{"line":88,"column":24}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":10,"column":4},"end":{"line":10,"column":5}},"loc":{"start":{"line":10,"column":42},"end":{"line":23,"column":5}},"line":10},"1":{"name":"(anonymous_1)","decl":{"start":{"line":26,"column":4},"end":{"line":26,"column":5}},"loc":{"start":{"line":26,"column":39},"end":{"line":59,"column":5}},"line":26},"2":{"name":"(anonymous_2)","decl":{"start":{"line":62,"column":4},"end":{"line":62,"column":5}},"loc":{"start":{"line":62,"column":45},"end":{"line":90,"column":5}},"line":62}},"branchMap":{"0":{"loc":{"start":{"line":30,"column":12},"end":{"line":33,"column":13}},"type":"if","locations":[{"start":{"line":30,"column":12},"end":{"line":33,"column":13}},{"start":{},"end":{}}],"line":30},"1":{"loc":{"start":{"line":35,"column":12},"end":{"line":38,"column":13}},"type":"if","locations":[{"start":{"line":35,"column":12},"end":{"line":38,"column":13}},{"start":{},"end":{}}],"line":35},"2":{"loc":{"start":{"line":41,"column":12},"end":{"line":44,"column":13}},"type":"if","locations":[{"start":{"line":41,"column":12},"end":{"line":44,"column":13}},{"start":{},"end":{}}],"line":41},"3":{"loc":{"start":{"line":47,"column":12},"end":{"line":50,"column":13}},"type":"if","locations":[{"start":{"line":47,"column":12},"end":{"line":50,"column":13}},{"start":{},"end":{}}],"line":47},"4":{"loc":{"start":{"line":85,"column":23},"end":{"line":85,"column":46}},"type":"cond-expr","locations":[{"start":{"line":85,"column":37},"end":{"line":85,"column":40}},{"start":{"line":85,"column":43},"end":{"line":85,"column":46}}],"line":85}},"s":{"0":1,"1":1,"2":1,"3":1,"4":1,"5":1,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":0,"31":0,"32":0,"33":0,"34":0,"35":0,"36":0,"37":0,"38":0,"39":0},"f":{"0":0,"1":0,"2":0},"b":{"0":[0,0],"1":[0,0],"2":[0,0],"3":[0,0],"4":[0,0]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"6ebdeb727e9ad3c3b3d452bf6da7c8c621ab5402"} -,"D:\\IP-RMT42\\server\\helpers\\bcryptjs.js": {"path":"D:\\IP-RMT42\\server\\helpers\\bcryptjs.js","statementMap":{"0":{"start":{"line":1,"column":15},"end":{"line":1,"column":34}},"1":{"start":{"line":3,"column":21},"end":{"line":6,"column":1}},"2":{"start":{"line":4,"column":17},"end":{"line":4,"column":39}},"3":{"start":{"line":5,"column":4},"end":{"line":5,"column":43}},"4":{"start":{"line":8,"column":24},"end":{"line":10,"column":1}},"5":{"start":{"line":9,"column":4},"end":{"line":9,"column":56}},"6":{"start":{"line":13,"column":0},"end":{"line":13,"column":49}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":3,"column":21},"end":{"line":3,"column":22}},"loc":{"start":{"line":3,"column":35},"end":{"line":6,"column":1}},"line":3},"1":{"name":"(anonymous_1)","decl":{"start":{"line":8,"column":24},"end":{"line":8,"column":25}},"loc":{"start":{"line":8,"column":54},"end":{"line":10,"column":1}},"line":8}},"branchMap":{},"s":{"0":1,"1":1,"2":1,"3":1,"4":1,"5":0,"6":1},"f":{"0":1,"1":0},"b":{},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"f4e7adf820f9a1e12aa3d842fa380a19157d320f"} -,"D:\\IP-RMT42\\server\\helpers\\jwt.js": {"path":"D:\\IP-RMT42\\server\\helpers\\jwt.js","statementMap":{"0":{"start":{"line":1,"column":12},"end":{"line":1,"column":35}},"1":{"start":{"line":3,"column":19},"end":{"line":3,"column":41}},"2":{"start":{"line":5,"column":18},"end":{"line":7,"column":1}},"3":{"start":{"line":6,"column":4},"end":{"line":6,"column":41}},"4":{"start":{"line":9,"column":20},"end":{"line":11,"column":1}},"5":{"start":{"line":10,"column":4},"end":{"line":10,"column":41}},"6":{"start":{"line":14,"column":0},"end":{"line":14,"column":42}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":5,"column":18},"end":{"line":5,"column":19}},"loc":{"start":{"line":5,"column":31},"end":{"line":7,"column":1}},"line":5},"1":{"name":"(anonymous_1)","decl":{"start":{"line":9,"column":20},"end":{"line":9,"column":21}},"loc":{"start":{"line":9,"column":31},"end":{"line":11,"column":1}},"line":9}},"branchMap":{},"s":{"0":1,"1":1,"2":1,"3":1,"4":1,"5":2,"6":1},"f":{"0":1,"1":2},"b":{},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"4af7a390dc8d5bb0010375dba86fe5e4fb533ffa"} -,"D:\\IP-RMT42\\server\\middlewares\\authentication.js": {"path":"D:\\IP-RMT42\\server\\middlewares\\authentication.js","statementMap":{"0":{"start":{"line":1,"column":17},"end":{"line":1,"column":37}},"1":{"start":{"line":2,"column":24},"end":{"line":2,"column":49}},"2":{"start":{"line":5,"column":4},"end":{"line":26,"column":5}},"3":{"start":{"line":6,"column":27},"end":{"line":6,"column":52}},"4":{"start":{"line":8,"column":8},"end":{"line":8,"column":60}},"5":{"start":{"line":8,"column":26},"end":{"line":8,"column":60}},"6":{"start":{"line":10,"column":23},"end":{"line":10,"column":57}},"7":{"start":{"line":12,"column":19},"end":{"line":12,"column":42}},"8":{"start":{"line":14,"column":8},"end":{"line":14,"column":52}},"9":{"start":{"line":14,"column":18},"end":{"line":14,"column":52}},"10":{"start":{"line":16,"column":8},"end":{"line":18,"column":9}},"11":{"start":{"line":20,"column":8},"end":{"line":20,"column":29}},"12":{"start":{"line":21,"column":8},"end":{"line":21,"column":15}},"13":{"start":{"line":24,"column":8},"end":{"line":24,"column":20}},"14":{"start":{"line":25,"column":8},"end":{"line":25,"column":26}},"15":{"start":{"line":29,"column":0},"end":{"line":29,"column":31}}},"fnMap":{"0":{"name":"authentication","decl":{"start":{"line":4,"column":15},"end":{"line":4,"column":29}},"loc":{"start":{"line":4,"column":45},"end":{"line":27,"column":1}},"line":4}},"branchMap":{"0":{"loc":{"start":{"line":8,"column":8},"end":{"line":8,"column":60}},"type":"if","locations":[{"start":{"line":8,"column":8},"end":{"line":8,"column":60}},{"start":{},"end":{}}],"line":8},"1":{"loc":{"start":{"line":14,"column":8},"end":{"line":14,"column":52}},"type":"if","locations":[{"start":{"line":14,"column":8},"end":{"line":14,"column":52}},{"start":{},"end":{}}],"line":14}},"s":{"0":1,"1":1,"2":3,"3":3,"4":3,"5":1,"6":2,"7":1,"8":1,"9":0,"10":1,"11":1,"12":1,"13":2,"14":2,"15":1},"f":{"0":3},"b":{"0":[1,2],"1":[0,1]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"c52a6eb796d691cdb6d74c76b8ddbccfc81d8162"} -,"D:\\IP-RMT42\\server\\middlewares\\authorization.js": {"path":"D:\\IP-RMT42\\server\\middlewares\\authorization.js","statementMap":{"0":{"start":{"line":1,"column":18},"end":{"line":1,"column":38}},"1":{"start":{"line":4,"column":4},"end":{"line":17,"column":5}},"2":{"start":{"line":5,"column":22},"end":{"line":5,"column":59}},"3":{"start":{"line":7,"column":8},"end":{"line":7,"column":47}},"4":{"start":{"line":7,"column":21},"end":{"line":7,"column":47}},"5":{"start":{"line":9,"column":8},"end":{"line":11,"column":9}},"6":{"start":{"line":10,"column":12},"end":{"line":10,"column":37}},"7":{"start":{"line":13,"column":8},"end":{"line":13,"column":15}},"8":{"start":{"line":16,"column":8},"end":{"line":16,"column":20}},"9":{"start":{"line":21,"column":0},"end":{"line":21,"column":31}}},"fnMap":{"0":{"name":"authorization","decl":{"start":{"line":3,"column":15},"end":{"line":3,"column":28}},"loc":{"start":{"line":3,"column":45},"end":{"line":18,"column":1}},"line":3}},"branchMap":{"0":{"loc":{"start":{"line":7,"column":8},"end":{"line":7,"column":47}},"type":"if","locations":[{"start":{"line":7,"column":8},"end":{"line":7,"column":47}},{"start":{},"end":{}}],"line":7},"1":{"loc":{"start":{"line":9,"column":8},"end":{"line":11,"column":9}},"type":"if","locations":[{"start":{"line":9,"column":8},"end":{"line":11,"column":9}},{"start":{},"end":{}}],"line":9}},"s":{"0":1,"1":0,"2":0,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":1},"f":{"0":0},"b":{"0":[0,0],"1":[0,0]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"fd4b46832ec27473ac744561f94dc9125c34a7f5"} -,"D:\\IP-RMT42\\server\\middlewares\\errorHandler.js": {"path":"D:\\IP-RMT42\\server\\middlewares\\errorHandler.js","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":35,"column":1}},"1":{"start":{"line":3,"column":4},"end":{"line":33,"column":5}},"2":{"start":{"line":6,"column":12},"end":{"line":6,"column":71}},"3":{"start":{"line":7,"column":12},"end":{"line":7,"column":18}},"4":{"start":{"line":10,"column":12},"end":{"line":10,"column":61}},"5":{"start":{"line":11,"column":12},"end":{"line":11,"column":18}},"6":{"start":{"line":15,"column":12},"end":{"line":15,"column":82}},"7":{"start":{"line":16,"column":12},"end":{"line":16,"column":18}},"8":{"start":{"line":19,"column":12},"end":{"line":19,"column":72}},"9":{"start":{"line":20,"column":12},"end":{"line":20,"column":18}},"10":{"start":{"line":23,"column":12},"end":{"line":23,"column":67}},"11":{"start":{"line":24,"column":12},"end":{"line":24,"column":18}},"12":{"start":{"line":27,"column":12},"end":{"line":27,"column":66}},"13":{"start":{"line":28,"column":12},"end":{"line":28,"column":18}},"14":{"start":{"line":31,"column":12},"end":{"line":31,"column":71}},"15":{"start":{"line":32,"column":12},"end":{"line":32,"column":18}}},"fnMap":{"0":{"name":"errorHandler","decl":{"start":{"line":1,"column":26},"end":{"line":1,"column":38}},"loc":{"start":{"line":1,"column":62},"end":{"line":35,"column":1}},"line":1}},"branchMap":{"0":{"loc":{"start":{"line":3,"column":4},"end":{"line":33,"column":5}},"type":"switch","locations":[{"start":{"line":4,"column":8},"end":{"line":4,"column":40}},{"start":{"line":5,"column":8},"end":{"line":7,"column":18}},{"start":{"line":9,"column":8},"end":{"line":11,"column":18}},{"start":{"line":13,"column":8},"end":{"line":13,"column":33}},{"start":{"line":14,"column":8},"end":{"line":16,"column":18}},{"start":{"line":18,"column":8},"end":{"line":20,"column":18}},{"start":{"line":22,"column":8},"end":{"line":24,"column":18}},{"start":{"line":26,"column":8},"end":{"line":28,"column":18}},{"start":{"line":30,"column":8},"end":{"line":32,"column":18}}],"line":3},"1":{"loc":{"start":{"line":15,"column":44},"end":{"line":15,"column":78}},"type":"binary-expr","locations":[{"start":{"line":15,"column":44},"end":{"line":15,"column":57}},{"start":{"line":15,"column":61},"end":{"line":15,"column":78}}],"line":15}},"s":{"0":1,"1":2,"2":0,"3":0,"4":0,"5":0,"6":2,"7":2,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0},"f":{"0":2},"b":{"0":[0,0,0,1,2,0,0,0,0],"1":[2,1]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"21faaae869c2555b110e907fb153b538d48e758a"} -,"D:\\IP-RMT42\\server\\models\\comment.js": {"path":"D:\\IP-RMT42\\server\\models\\comment.js","statementMap":{"0":{"start":{"line":4,"column":4},"end":{"line":4,"column":24}},"1":{"start":{"line":5,"column":0},"end":{"line":25,"column":2}},"2":{"start":{"line":14,"column":6},"end":{"line":14,"column":60}},"3":{"start":{"line":17,"column":2},"end":{"line":23,"column":5}},"4":{"start":{"line":24,"column":2},"end":{"line":24,"column":17}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":5,"column":17},"end":{"line":5,"column":18}},"loc":{"start":{"line":5,"column":43},"end":{"line":25,"column":1}},"line":5},"1":{"name":"(anonymous_1)","decl":{"start":{"line":12,"column":4},"end":{"line":12,"column":5}},"loc":{"start":{"line":12,"column":29},"end":{"line":15,"column":5}},"line":12}},"branchMap":{},"s":{"0":1,"1":1,"2":1,"3":1,"4":1},"f":{"0":1,"1":1},"b":{},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"6d6f8423a2d02432d92d3b5fc92e0a77f98b0c1a"} +,"D:\\IP-RMT42\\server\\helpers\\bcryptjs.js": {"path":"D:\\IP-RMT42\\server\\helpers\\bcryptjs.js","statementMap":{"0":{"start":{"line":1,"column":15},"end":{"line":1,"column":34}},"1":{"start":{"line":3,"column":21},"end":{"line":6,"column":1}},"2":{"start":{"line":4,"column":17},"end":{"line":4,"column":39}},"3":{"start":{"line":5,"column":4},"end":{"line":5,"column":43}},"4":{"start":{"line":8,"column":24},"end":{"line":10,"column":1}},"5":{"start":{"line":9,"column":4},"end":{"line":9,"column":56}},"6":{"start":{"line":13,"column":0},"end":{"line":13,"column":49}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":3,"column":21},"end":{"line":3,"column":22}},"loc":{"start":{"line":3,"column":35},"end":{"line":6,"column":1}},"line":3},"1":{"name":"(anonymous_1)","decl":{"start":{"line":8,"column":24},"end":{"line":8,"column":25}},"loc":{"start":{"line":8,"column":54},"end":{"line":10,"column":1}},"line":8}},"branchMap":{},"s":{"0":1,"1":1,"2":2,"3":2,"4":1,"5":0,"6":1},"f":{"0":2,"1":0},"b":{},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"f4e7adf820f9a1e12aa3d842fa380a19157d320f"} +,"D:\\IP-RMT42\\server\\helpers\\jwt.js": {"path":"D:\\IP-RMT42\\server\\helpers\\jwt.js","statementMap":{"0":{"start":{"line":1,"column":12},"end":{"line":1,"column":35}},"1":{"start":{"line":3,"column":19},"end":{"line":3,"column":41}},"2":{"start":{"line":5,"column":18},"end":{"line":7,"column":1}},"3":{"start":{"line":6,"column":4},"end":{"line":6,"column":41}},"4":{"start":{"line":9,"column":20},"end":{"line":11,"column":1}},"5":{"start":{"line":10,"column":4},"end":{"line":10,"column":41}},"6":{"start":{"line":14,"column":0},"end":{"line":14,"column":42}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":5,"column":18},"end":{"line":5,"column":19}},"loc":{"start":{"line":5,"column":31},"end":{"line":7,"column":1}},"line":5},"1":{"name":"(anonymous_1)","decl":{"start":{"line":9,"column":20},"end":{"line":9,"column":21}},"loc":{"start":{"line":9,"column":31},"end":{"line":11,"column":1}},"line":9}},"branchMap":{},"s":{"0":1,"1":1,"2":1,"3":2,"4":1,"5":4,"6":1},"f":{"0":2,"1":4},"b":{},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"4af7a390dc8d5bb0010375dba86fe5e4fb533ffa"} +,"D:\\IP-RMT42\\server\\middlewares\\authentication.js": {"path":"D:\\IP-RMT42\\server\\middlewares\\authentication.js","statementMap":{"0":{"start":{"line":1,"column":17},"end":{"line":1,"column":37}},"1":{"start":{"line":2,"column":24},"end":{"line":2,"column":49}},"2":{"start":{"line":5,"column":4},"end":{"line":26,"column":5}},"3":{"start":{"line":6,"column":27},"end":{"line":6,"column":52}},"4":{"start":{"line":8,"column":8},"end":{"line":8,"column":60}},"5":{"start":{"line":8,"column":26},"end":{"line":8,"column":60}},"6":{"start":{"line":10,"column":23},"end":{"line":10,"column":57}},"7":{"start":{"line":12,"column":19},"end":{"line":12,"column":42}},"8":{"start":{"line":14,"column":8},"end":{"line":14,"column":52}},"9":{"start":{"line":14,"column":18},"end":{"line":14,"column":52}},"10":{"start":{"line":16,"column":8},"end":{"line":18,"column":9}},"11":{"start":{"line":20,"column":8},"end":{"line":20,"column":29}},"12":{"start":{"line":21,"column":8},"end":{"line":21,"column":15}},"13":{"start":{"line":24,"column":8},"end":{"line":24,"column":20}},"14":{"start":{"line":25,"column":8},"end":{"line":25,"column":26}},"15":{"start":{"line":29,"column":0},"end":{"line":29,"column":31}}},"fnMap":{"0":{"name":"authentication","decl":{"start":{"line":4,"column":15},"end":{"line":4,"column":29}},"loc":{"start":{"line":4,"column":45},"end":{"line":27,"column":1}},"line":4}},"branchMap":{"0":{"loc":{"start":{"line":8,"column":8},"end":{"line":8,"column":60}},"type":"if","locations":[{"start":{"line":8,"column":8},"end":{"line":8,"column":60}},{"start":{},"end":{}}],"line":8},"1":{"loc":{"start":{"line":14,"column":8},"end":{"line":14,"column":52}},"type":"if","locations":[{"start":{"line":14,"column":8},"end":{"line":14,"column":52}},{"start":{},"end":{}}],"line":14}},"s":{"0":1,"1":1,"2":5,"3":5,"4":5,"5":1,"6":4,"7":3,"8":3,"9":0,"10":3,"11":3,"12":3,"13":2,"14":2,"15":1},"f":{"0":5},"b":{"0":[1,4],"1":[0,3]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"c52a6eb796d691cdb6d74c76b8ddbccfc81d8162"} +,"D:\\IP-RMT42\\server\\middlewares\\authorization.js": {"path":"D:\\IP-RMT42\\server\\middlewares\\authorization.js","statementMap":{"0":{"start":{"line":1,"column":18},"end":{"line":1,"column":38}},"1":{"start":{"line":4,"column":4},"end":{"line":17,"column":5}},"2":{"start":{"line":5,"column":22},"end":{"line":5,"column":59}},"3":{"start":{"line":7,"column":8},"end":{"line":7,"column":47}},"4":{"start":{"line":7,"column":21},"end":{"line":7,"column":47}},"5":{"start":{"line":9,"column":8},"end":{"line":11,"column":9}},"6":{"start":{"line":10,"column":12},"end":{"line":10,"column":37}},"7":{"start":{"line":13,"column":8},"end":{"line":13,"column":15}},"8":{"start":{"line":16,"column":8},"end":{"line":16,"column":20}},"9":{"start":{"line":21,"column":0},"end":{"line":21,"column":31}}},"fnMap":{"0":{"name":"authorization","decl":{"start":{"line":3,"column":15},"end":{"line":3,"column":28}},"loc":{"start":{"line":3,"column":45},"end":{"line":18,"column":1}},"line":3}},"branchMap":{"0":{"loc":{"start":{"line":7,"column":8},"end":{"line":7,"column":47}},"type":"if","locations":[{"start":{"line":7,"column":8},"end":{"line":7,"column":47}},{"start":{},"end":{}}],"line":7},"1":{"loc":{"start":{"line":9,"column":8},"end":{"line":11,"column":9}},"type":"if","locations":[{"start":{"line":9,"column":8},"end":{"line":11,"column":9}},{"start":{},"end":{}}],"line":9}},"s":{"0":1,"1":3,"2":3,"3":3,"4":1,"5":2,"6":1,"7":1,"8":2,"9":1},"f":{"0":3},"b":{"0":[1,2],"1":[1,1]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"fd4b46832ec27473ac744561f94dc9125c34a7f5"} +,"D:\\IP-RMT42\\server\\middlewares\\errorHandler.js": {"path":"D:\\IP-RMT42\\server\\middlewares\\errorHandler.js","statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":35,"column":1}},"1":{"start":{"line":3,"column":4},"end":{"line":33,"column":5}},"2":{"start":{"line":6,"column":12},"end":{"line":6,"column":71}},"3":{"start":{"line":7,"column":12},"end":{"line":7,"column":18}},"4":{"start":{"line":10,"column":12},"end":{"line":10,"column":61}},"5":{"start":{"line":11,"column":12},"end":{"line":11,"column":18}},"6":{"start":{"line":15,"column":12},"end":{"line":15,"column":82}},"7":{"start":{"line":16,"column":12},"end":{"line":16,"column":18}},"8":{"start":{"line":19,"column":12},"end":{"line":19,"column":72}},"9":{"start":{"line":20,"column":12},"end":{"line":20,"column":18}},"10":{"start":{"line":23,"column":12},"end":{"line":23,"column":67}},"11":{"start":{"line":24,"column":12},"end":{"line":24,"column":18}},"12":{"start":{"line":27,"column":12},"end":{"line":27,"column":66}},"13":{"start":{"line":28,"column":12},"end":{"line":28,"column":18}},"14":{"start":{"line":31,"column":12},"end":{"line":31,"column":71}},"15":{"start":{"line":32,"column":12},"end":{"line":32,"column":18}}},"fnMap":{"0":{"name":"errorHandler","decl":{"start":{"line":1,"column":26},"end":{"line":1,"column":38}},"loc":{"start":{"line":1,"column":62},"end":{"line":35,"column":1}},"line":1}},"branchMap":{"0":{"loc":{"start":{"line":3,"column":4},"end":{"line":33,"column":5}},"type":"switch","locations":[{"start":{"line":4,"column":8},"end":{"line":4,"column":40}},{"start":{"line":5,"column":8},"end":{"line":7,"column":18}},{"start":{"line":9,"column":8},"end":{"line":11,"column":18}},{"start":{"line":13,"column":8},"end":{"line":13,"column":33}},{"start":{"line":14,"column":8},"end":{"line":16,"column":18}},{"start":{"line":18,"column":8},"end":{"line":20,"column":18}},{"start":{"line":22,"column":8},"end":{"line":24,"column":18}},{"start":{"line":26,"column":8},"end":{"line":28,"column":18}},{"start":{"line":30,"column":8},"end":{"line":32,"column":18}}],"line":3},"1":{"loc":{"start":{"line":15,"column":44},"end":{"line":15,"column":78}},"type":"binary-expr","locations":[{"start":{"line":15,"column":44},"end":{"line":15,"column":57}},{"start":{"line":15,"column":61},"end":{"line":15,"column":78}}],"line":15}},"s":{"0":1,"1":4,"2":0,"3":0,"4":0,"5":0,"6":2,"7":2,"8":1,"9":1,"10":1,"11":1,"12":0,"13":0,"14":0,"15":0},"f":{"0":4},"b":{"0":[0,0,0,1,2,1,1,0,0],"1":[2,1]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"21faaae869c2555b110e907fb153b538d48e758a"} +,"D:\\IP-RMT42\\server\\models\\comment.js": {"path":"D:\\IP-RMT42\\server\\models\\comment.js","statementMap":{"0":{"start":{"line":4,"column":4},"end":{"line":4,"column":24}},"1":{"start":{"line":5,"column":0},"end":{"line":27,"column":2}},"2":{"start":{"line":14,"column":6},"end":{"line":14,"column":60}},"3":{"start":{"line":17,"column":2},"end":{"line":25,"column":5}},"4":{"start":{"line":26,"column":2},"end":{"line":26,"column":17}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":5,"column":17},"end":{"line":5,"column":18}},"loc":{"start":{"line":5,"column":43},"end":{"line":27,"column":1}},"line":5},"1":{"name":"(anonymous_1)","decl":{"start":{"line":12,"column":4},"end":{"line":12,"column":5}},"loc":{"start":{"line":12,"column":29},"end":{"line":15,"column":5}},"line":12}},"branchMap":{},"s":{"0":1,"1":1,"2":1,"3":1,"4":1},"f":{"0":1,"1":1},"b":{},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"7b828688337073f419094a409551672c569437a3"} ,"D:\\IP-RMT42\\server\\models\\index.js": {"path":"D:\\IP-RMT42\\server\\models\\index.js","statementMap":{"0":{"start":{"line":3,"column":11},"end":{"line":3,"column":24}},"1":{"start":{"line":4,"column":13},"end":{"line":4,"column":28}},"2":{"start":{"line":5,"column":18},"end":{"line":5,"column":38}},"3":{"start":{"line":6,"column":16},"end":{"line":6,"column":34}},"4":{"start":{"line":7,"column":17},"end":{"line":7,"column":42}},"5":{"start":{"line":8,"column":12},"end":{"line":8,"column":49}},"6":{"start":{"line":9,"column":15},"end":{"line":9,"column":65}},"7":{"start":{"line":10,"column":11},"end":{"line":10,"column":13}},"8":{"start":{"line":13,"column":0},"end":{"line":17,"column":1}},"9":{"start":{"line":14,"column":2},"end":{"line":14,"column":74}},"10":{"start":{"line":16,"column":2},"end":{"line":16,"column":87}},"11":{"start":{"line":19,"column":0},"end":{"line":32,"column":5}},"12":{"start":{"line":22,"column":4},"end":{"line":27,"column":6}},"13":{"start":{"line":30,"column":18},"end":{"line":30,"column":85}},"14":{"start":{"line":31,"column":4},"end":{"line":31,"column":27}},"15":{"start":{"line":34,"column":0},"end":{"line":38,"column":3}},"16":{"start":{"line":35,"column":2},"end":{"line":37,"column":3}},"17":{"start":{"line":36,"column":4},"end":{"line":36,"column":32}},"18":{"start":{"line":40,"column":0},"end":{"line":40,"column":25}},"19":{"start":{"line":41,"column":0},"end":{"line":41,"column":25}},"20":{"start":{"line":43,"column":0},"end":{"line":43,"column":20}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":21,"column":10},"end":{"line":21,"column":11}},"loc":{"start":{"line":21,"column":18},"end":{"line":28,"column":3}},"line":21},"1":{"name":"(anonymous_1)","decl":{"start":{"line":29,"column":11},"end":{"line":29,"column":12}},"loc":{"start":{"line":29,"column":19},"end":{"line":32,"column":3}},"line":29},"2":{"name":"(anonymous_2)","decl":{"start":{"line":34,"column":24},"end":{"line":34,"column":25}},"loc":{"start":{"line":34,"column":37},"end":{"line":38,"column":1}},"line":34}},"branchMap":{"0":{"loc":{"start":{"line":8,"column":12},"end":{"line":8,"column":49}},"type":"binary-expr","locations":[{"start":{"line":8,"column":12},"end":{"line":8,"column":32}},{"start":{"line":8,"column":36},"end":{"line":8,"column":49}}],"line":8},"1":{"loc":{"start":{"line":13,"column":0},"end":{"line":17,"column":1}},"type":"if","locations":[{"start":{"line":13,"column":0},"end":{"line":17,"column":1}},{"start":{"line":15,"column":7},"end":{"line":17,"column":1}}],"line":13},"2":{"loc":{"start":{"line":23,"column":6},"end":{"line":26,"column":37}},"type":"binary-expr","locations":[{"start":{"line":23,"column":6},"end":{"line":23,"column":29}},{"start":{"line":24,"column":6},"end":{"line":24,"column":23}},{"start":{"line":25,"column":6},"end":{"line":25,"column":30}},{"start":{"line":26,"column":6},"end":{"line":26,"column":37}}],"line":23},"3":{"loc":{"start":{"line":35,"column":2},"end":{"line":37,"column":3}},"type":"if","locations":[{"start":{"line":35,"column":2},"end":{"line":37,"column":3}},{"start":{},"end":{}}],"line":35}},"s":{"0":1,"1":1,"2":1,"3":1,"4":1,"5":1,"6":1,"7":1,"8":1,"9":0,"10":1,"11":1,"12":4,"13":3,"14":3,"15":1,"16":3,"17":3,"18":1,"19":1,"20":1},"f":{"0":4,"1":3,"2":3},"b":{"0":[1,0],"1":[0,1],"2":[4,4,3,3],"3":[3,0]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"212e906caafd968e92070d46b09632103eeaa93c"} ,"D:\\IP-RMT42\\server\\models\\recipe.js": {"path":"D:\\IP-RMT42\\server\\models\\recipe.js","statementMap":{"0":{"start":{"line":4,"column":4},"end":{"line":4,"column":24}},"1":{"start":{"line":5,"column":0},"end":{"line":30,"column":2}},"2":{"start":{"line":16,"column":2},"end":{"line":28,"column":5}},"3":{"start":{"line":29,"column":2},"end":{"line":29,"column":16}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":5,"column":17},"end":{"line":5,"column":18}},"loc":{"start":{"line":5,"column":43},"end":{"line":30,"column":1}},"line":5},"1":{"name":"(anonymous_1)","decl":{"start":{"line":12,"column":4},"end":{"line":12,"column":5}},"loc":{"start":{"line":12,"column":29},"end":{"line":14,"column":5}},"line":12}},"branchMap":{},"s":{"0":1,"1":1,"2":1,"3":1},"f":{"0":1,"1":1},"b":{},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"83a4c86bf2d26b7378b9cb519cd42cf454874ae6"} -,"D:\\IP-RMT42\\server\\models\\user.js": {"path":"D:\\IP-RMT42\\server\\models\\user.js","statementMap":{"0":{"start":{"line":2,"column":18},"end":{"line":2,"column":38}},"1":{"start":{"line":3,"column":25},"end":{"line":3,"column":55}},"2":{"start":{"line":6,"column":0},"end":{"line":65,"column":2}},"3":{"start":{"line":15,"column":6},"end":{"line":15,"column":59}},"4":{"start":{"line":18,"column":2},"end":{"line":58,"column":5}},"5":{"start":{"line":60,"column":2},"end":{"line":62,"column":4}},"6":{"start":{"line":61,"column":4},"end":{"line":61,"column":48}},"7":{"start":{"line":64,"column":2},"end":{"line":64,"column":14}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":6,"column":17},"end":{"line":6,"column":18}},"loc":{"start":{"line":6,"column":43},"end":{"line":65,"column":1}},"line":6},"1":{"name":"(anonymous_1)","decl":{"start":{"line":13,"column":4},"end":{"line":13,"column":5}},"loc":{"start":{"line":13,"column":29},"end":{"line":16,"column":5}},"line":13},"2":{"name":"(anonymous_2)","decl":{"start":{"line":60,"column":20},"end":{"line":60,"column":21}},"loc":{"start":{"line":60,"column":30},"end":{"line":62,"column":3}},"line":60}},"branchMap":{},"s":{"0":1,"1":1,"2":1,"3":1,"4":1,"5":1,"6":1,"7":1},"f":{"0":1,"1":1,"2":1},"b":{},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"93c11956d0a296188dcc2a2958bb6f4e084876d9"} -,"D:\\IP-RMT42\\server\\routes\\index.js": {"path":"D:\\IP-RMT42\\server\\routes\\index.js","statementMap":{"0":{"start":{"line":1,"column":23},"end":{"line":1,"column":63}},"1":{"start":{"line":2,"column":25},"end":{"line":2,"column":67}},"2":{"start":{"line":3,"column":23},"end":{"line":3,"column":63}},"3":{"start":{"line":4,"column":26},"end":{"line":4,"column":69}},"4":{"start":{"line":5,"column":23},"end":{"line":5,"column":63}},"5":{"start":{"line":6,"column":22},"end":{"line":6,"column":61}},"6":{"start":{"line":7,"column":21},"end":{"line":7,"column":59}},"7":{"start":{"line":9,"column":15},"end":{"line":9,"column":42}},"8":{"start":{"line":12,"column":0},"end":{"line":12,"column":50}},"9":{"start":{"line":14,"column":0},"end":{"line":14,"column":44}},"10":{"start":{"line":16,"column":0},"end":{"line":16,"column":57}},"11":{"start":{"line":21,"column":0},"end":{"line":21,"column":26}},"12":{"start":{"line":23,"column":0},"end":{"line":23,"column":49}},"13":{"start":{"line":25,"column":0},"end":{"line":25,"column":55}},"14":{"start":{"line":27,"column":0},"end":{"line":27,"column":54}},"15":{"start":{"line":32,"column":0},"end":{"line":32,"column":58}},"16":{"start":{"line":34,"column":0},"end":{"line":34,"column":78}},"17":{"start":{"line":36,"column":0},"end":{"line":36,"column":85}},"18":{"start":{"line":39,"column":0},"end":{"line":39,"column":25}},"19":{"start":{"line":42,"column":0},"end":{"line":42,"column":24}}},"fnMap":{},"branchMap":{},"s":{"0":1,"1":1,"2":1,"3":1,"4":1,"5":1,"6":1,"7":1,"8":1,"9":1,"10":1,"11":1,"12":1,"13":1,"14":1,"15":1,"16":1,"17":1,"18":1,"19":1},"f":{},"b":{},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"da6975d7843f5bf7a0e013236865af8855ab8da1"} +,"D:\\IP-RMT42\\server\\models\\user.js": {"path":"D:\\IP-RMT42\\server\\models\\user.js","statementMap":{"0":{"start":{"line":2,"column":18},"end":{"line":2,"column":38}},"1":{"start":{"line":3,"column":25},"end":{"line":3,"column":55}},"2":{"start":{"line":6,"column":0},"end":{"line":65,"column":2}},"3":{"start":{"line":15,"column":6},"end":{"line":15,"column":59}},"4":{"start":{"line":18,"column":2},"end":{"line":58,"column":5}},"5":{"start":{"line":60,"column":2},"end":{"line":62,"column":4}},"6":{"start":{"line":61,"column":4},"end":{"line":61,"column":48}},"7":{"start":{"line":64,"column":2},"end":{"line":64,"column":14}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":6,"column":17},"end":{"line":6,"column":18}},"loc":{"start":{"line":6,"column":43},"end":{"line":65,"column":1}},"line":6},"1":{"name":"(anonymous_1)","decl":{"start":{"line":13,"column":4},"end":{"line":13,"column":5}},"loc":{"start":{"line":13,"column":29},"end":{"line":16,"column":5}},"line":13},"2":{"name":"(anonymous_2)","decl":{"start":{"line":60,"column":20},"end":{"line":60,"column":21}},"loc":{"start":{"line":60,"column":30},"end":{"line":62,"column":3}},"line":60}},"branchMap":{},"s":{"0":1,"1":1,"2":1,"3":1,"4":1,"5":1,"6":2,"7":1},"f":{"0":1,"1":1,"2":2},"b":{},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"93c11956d0a296188dcc2a2958bb6f4e084876d9"} +,"D:\\IP-RMT42\\server\\routes\\index.js": {"path":"D:\\IP-RMT42\\server\\routes\\index.js","statementMap":{"0":{"start":{"line":1,"column":26},"end":{"line":1,"column":69}},"1":{"start":{"line":2,"column":23},"end":{"line":2,"column":63}},"2":{"start":{"line":3,"column":25},"end":{"line":3,"column":67}},"3":{"start":{"line":4,"column":23},"end":{"line":4,"column":63}},"4":{"start":{"line":5,"column":23},"end":{"line":5,"column":63}},"5":{"start":{"line":6,"column":22},"end":{"line":6,"column":61}},"6":{"start":{"line":7,"column":21},"end":{"line":7,"column":59}},"7":{"start":{"line":9,"column":15},"end":{"line":9,"column":42}},"8":{"start":{"line":12,"column":0},"end":{"line":12,"column":50}},"9":{"start":{"line":14,"column":0},"end":{"line":14,"column":44}},"10":{"start":{"line":16,"column":0},"end":{"line":16,"column":57}},"11":{"start":{"line":21,"column":0},"end":{"line":21,"column":26}},"12":{"start":{"line":23,"column":0},"end":{"line":23,"column":49}},"13":{"start":{"line":25,"column":0},"end":{"line":25,"column":55}},"14":{"start":{"line":30,"column":0},"end":{"line":30,"column":54}},"15":{"start":{"line":35,"column":0},"end":{"line":35,"column":58}},"16":{"start":{"line":37,"column":0},"end":{"line":37,"column":78}},"17":{"start":{"line":39,"column":0},"end":{"line":39,"column":85}},"18":{"start":{"line":42,"column":0},"end":{"line":42,"column":25}},"19":{"start":{"line":45,"column":0},"end":{"line":45,"column":24}}},"fnMap":{},"branchMap":{},"s":{"0":1,"1":1,"2":1,"3":1,"4":1,"5":1,"6":1,"7":1,"8":1,"9":1,"10":1,"11":1,"12":1,"13":1,"14":1,"15":1,"16":1,"17":1,"18":1,"19":1},"f":{},"b":{},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"94bbe19de863bcacb60d6d691622e7563d07ba86"} } diff --git a/server/coverage/lcov-report/index.html b/server/coverage/lcov-report/index.html index 2648351..af67309 100644 --- a/server/coverage/lcov-report/index.html +++ b/server/coverage/lcov-report/index.html @@ -23,30 +23,30 @@

All files

- 52.94% + 58.03% Statements - 117/221 + 130/224
- 28.84% + 41.17% Branches - 15/52 + 21/51
- 55.17% + 58.62% Functions - 16/29 + 17/29
- 53.45% + 58.18% Lines - 116/217 + 128/220
@@ -95,17 +95,17 @@

All files

server/controllers - -
+ +
- 16.49% - 16/97 + 17% + 17/100 0% - 0/21 + 0/20 8.33% 1/12 - 16.66% - 16/96 + 17.17% + 17/99 @@ -125,17 +125,17 @@

All files

server/middlewares - -
+ +
- 50% - 21/42 - 36.84% - 7/19 - 66.66% - 2/3 - 51.28% - 20/39 + 78.57% + 33/42 + 68.42% + 13/19 + 100% + 3/3 + 79.48% + 31/39 @@ -176,7 +176,7 @@

All files