diff --git a/backend/backend.js b/backend/backend.js index d90cf1e..85f902f 100644 --- a/backend/backend.js +++ b/backend/backend.js @@ -1,23 +1,27 @@ -//https -const https = require("https"); -const fs = require("fs"); - // express js & cors middleware const express = require("express"); const cors = require("cors"); -//web token for user auth -const jwt = require("jsonwebtoken"); +const cookieParser = require("cookie-parser"); // Using .env file for environment variables (DB connection) const dotenv = require("dotenv"); dotenv.config(); +//import middleware functions +const middleware = require("./middleware"); + +//import routes +const authRoutes = require("./routes/authRoutes"); +const userRoutes = require("./routes/userRoutes"); +const ingredientRoutes = require("./routes/ingredientRoutes"); +const recipeRoutes = require("./routes/recipeRoutes"); + // require mongoose const mongoose = require("mongoose"); +//connect to remote DB mongoose.set("debug", process.env.DEBUG); - mongoose .connect( "mongodb+srv://" + @@ -38,352 +42,26 @@ mongoose ) .catch((error) => console.log(error)); -console.log("Connected to MongoDB."); - -// -------------------------------------- -// Services -// -------------------------------------- -// Spoonacular API -const recipeAPI = require("./recipeAPI.js"); -// MongoDB / Mongoose -const userServices = require("./controllers/user-services"); -const recipeServices = require("./controllers/recipe-services"); - +//express session const app = express(); const port = 8000; app.use(cors()); app.use(express.json()); +app.use(cookieParser()); -app.get("/", (req, res) => { - res.send("Hello World!"); -}); - -// designate protected routes -app.use("/recipes", authenticateToken); -app.use("/ingredients", authenticateToken); - -// -------------------------------------- -// Token -// -------------------------------------- -function generateAccessToken(id) { - return jwt.sign(id, process.env.TOKEN_SECRET, { expiresIn: "1h" }); -} - -//use case -//app.get(..., authenticateToken, function (req, res) => ...); -//middleware to authenticate token, used for /services and all nested paths -async function authenticateToken(req, res, next) { - token = req.headers["token"]; - // if token is null return a response with status 401 and end the request - if (token == null) res.status(401).send("Unauthorized"); - else { - jwt.verify(token, process.env.TOKEN_SECRET, (err, user) => { - if (err) res.status(403).send("Forbidden"); - else { - req._id = user.id; - next(); - } - }); - } -} +// auth middleware for protected routes +app.use("/recipes", middleware.authenticateToken); +app.use("/ingredients", middleware.authenticateToken); -// -------------------------------------------------- -// AUTHENTICATION ENDPOINTS -// -------------------------------------------------- -// login endpoint: -// get username and password from request body and pass to -// userServices.login() which authenticates the user from -// the database -app.post("/login", async (req, res) => { - const user = req.body; - try { - const result = await userServices.login(user.email, user.password); +//use routes defined by express router +app.use("/", authRoutes); +app.use("/users", userRoutes); +app.use("/recipes", recipeRoutes); +app.use("/ingredients", ingredientRoutes); - if (result === undefined || result.length === 0) { - res.status(404).send("Resource not found."); - } else { - const token = generateAccessToken({ id: result._id }); - res.status(201).json({ token: token }); - //res.send({ users_list: result }); - } - } catch (error) { - console.log(error); - res.status(500).send("Internal Server Error."); - } -}); - -// register endpoint: -// get username and password from request body and pass to -// userServices.register() which adds the user to the database -// after using bcrypt to hash the password -app.post("/register", async (req, res) => { - try { - const user = req.body; - const result = await userServices.register(user.email, user.password); - - if (result === undefined || result.length === 0) { - res.status(404).send("Resource not found."); - } else { - const token = generateAccessToken({ id: result._id }); - res.json({ token: token }).status(201); - //res.send({ users_list: result}); - } - } catch (error) { - console.log(error); - res.status(500).send("Internal Server Error."); - } -}); - -// -------------------------------------------------- -// USER ENDPOINTS -// -------------------------------------------------- -// Get users endpoint: -app.get("/users", async (req, res) => { - const name = req.query["name"]; - try { - const result = await userServices.getUsers(name); - res.send({ users_list: result }); // can be empty array (no error if nothing found) - } catch (error) { - console.log(error); - res.status(500).send("Internal Server Error."); - } -}); - -// Get User by Id endpoint: -app.get("/users/:id", async (req, res) => { - const id = req.params.id; - try { - const result = await userServices.findUserById(id); - if (result === undefined || result.length === 0) { - res.status(404).send("Resource not found."); - } else { - res.send({ users_list: result }); - } - } catch (error) { - console.log(error); - res.status(500).send("Internal Server Error."); - } -}); - -// Add a Recipe to a User's Saved Recipes endpoint: -// - body of the request to this endpoint contains 1 field: recipe_id -app.post("/users/:id/recipes", async (req, res) => { - const user_id = req.params.id; - const user = await userServices.findUserById(user_id); - const recipe_id = req.body.recipe_id; - try { - const result = await userServices.addRecipe(user, recipe_id); - if (result === undefined || result.length === 0) { - res.status(404).send("Resource not found."); - } else { - res.send({ users_list: result }); - } - } catch (error) { - console.log(error); - res.status(500).send("Internal Server Error."); - } -}); - -app.delete("/recipes/:id", async (req, res) => { - try { - const recipe_id = req.params.id; - const user_id = req._id; - const result = await userServices.removeRecipe(user_id, recipe_id); - if (!result) { - res.status(404).send("Resource not found."); - } else { - res.status(201).send({ users_list: result }); - } - } catch (error) { - console.log(error); - res.status(500).send("Internal Server Error."); - } -}); - -// Populate the Recipes for a specific User -app.get("/users/:id/recipes", async (req, res) => { - const user_id = req.params.id; - try { - const user = await userServices.findUserById(user_id); - const result = await userServices.getRecipes(user); - if (result === undefined || result.length === 0) { - res.status(404).send("Resource not found."); - } else { - res.send({ users_list: result }); - } - } catch (error) { - console.log(error); - res.status(500).send("Internal Server Error."); - } -}); - -//load in saved recipes -app.get("/recipes", async (req, res) => { - try { - //send back recipes associated with user - const id = req._id; - const recipes = await userServices.getRecipes(id); - if (recipes === undefined || recipes.length === 0) { - res.status(404).send("Recipes not Found for User"); - } else { - res.status(201).send(recipes).end(); - } - } catch (error) { - console.log(error); - res.status(505).send("Recipes not Found for User"); - } -}); - -// Get recipe by Id endpoint: -app.get("/recipes/:id", async (req, res) => { - const id = req.params.id; - try { - const result = await recipeServices.getRecipeByID(id); - console.log("GOT RESULT: ", result); - if (result === undefined || result.length === 0) { - res.status(404).send("Resource not found."); - } else { - res.send({ result }); - } - } catch (error) { - console.log(error); - res.status(500).send("Internal Server Error."); - } -}); - -//search API for recipes -app.post("/recipes", async (req, res) => { - try { - const id = req._id; - parameters = req.body; - recipes = await recipeAPI.getRecipes(parameters); - - if (recipes === undefined || recipes.length === 0) { - res.status(404).send("Resource not found."); - } else { - //check if recipe already exists in DB - for (let i = 0; i < recipes.length; i++) { - let recipe = await recipeServices.getRecipeByWebID(recipes[i].id); - if (recipe) { - recipes[i] = recipe; - } else { - //add recipe to database - recipes[i] = await recipeServices.addRecipe(recipes[i]); - } - } - const favorites = await userServices.getRecipes(id); - res.status(201).send({ - favorites: favorites, - recipes_list: recipes, - }); - } - } catch (error) { - console.log("ERROR IN RECIPE POST"); - console.log(error); - res.status(500).send("BAD AUTH /services/recipes"); - } -}); - -//Favorite Recipe -app.patch("/recipes/:id", async (req, res) => { - try { - //data base id for user and recipe - const userID = req._id; - const recipeID = req.params["id"]; - const result = await userServices.addRecipe(userID, recipeID); - if (!result) { - res.status(500).end(); - } else { - res.status(201).end(); - } - } catch (error) { - console.log(error); - res.status(500); - } -}); - -//Get ratings for recipe -// - most of this functionality is handled in the frontend, -// this endpoint sends a json object of a recipe -app.get("/recipes/:id/ratings", async (req, res) => { - try { - //get recipe ID - const recipeID = req.params["id"]; - const ratings = await recipeServices.getRatings(recipeID); - - if (ratings === undefined) { - res.status(404).send("Resource not Found").end(); - } else { - res.status(201).send(ratings).end(); - } - } catch (error) { - console.log(error); - res.status(500); - } -}); - -//add rating to recipe -app.patch("/recipes/:id/ratings", async (req, res) => { - try { - //get recipeID and new rating to add to recipe - const recipeID = req.params["id"]; - const rating = req.body["rating"]; - - //add rating to recipe - const result = await recipeServices.addRating(recipeID, rating); - - if (!result) { - res.status(404).end(); - } else { - res.status(201).send(result).end(); - } - } catch (error) { - console.log(error); - res.status(500); - } -}); - -// -------------------------------------------------- -// INGREDIENT ENDPOINTS -// -------------------------------------------------- -// Get all ingredients endpoint: - -//return ingredients associated with user -app.get("/ingredients", async (req, res) => { - try { - const id = req._id; - const result = await userServices.getIngredients(id); - if (result === undefined || result.length === 0) { - res.status(404).send("Resource not found."); - } else { - res.status(201).send({ ingredients_list: result }); // can be empty array (no error if nothing found) - } - } catch (error) { - console.log(error); - res.status(500).send("Internal Server Error."); - } -}); - -// Create ingredient endpoint: -app.put("/ingredients", async (req, res) => { - const data = req.body; - try { - const id = req._id; - const updatedUser = await userServices.updateIngredients( - id, - data.ingredients - ); - - if (updatedUser) { - res.status(201).send(updatedUser).end(); - } else { - res.status(400).send("Bad Request."); - } - } catch (error) { - console.log(error); - res.status(500).send("Internal Server Error."); - } +app.get("/", (req, res) => { + res.send("Hello World!"); }); // export mongoose connection @@ -397,27 +75,3 @@ app.listen(process.env.PORT || port, () => { ); } else console.log(`REST API is listening on: http://localhost:${port}.`); }); - -/* -https - .createServer( - // Provide the private and public key to the server by reading each - // file's content with the readFileSync() method. - { - key: fs.readFileSync("certificates/key.pem"), - cert: fs.readFileSync("certificates/cert.pem"), - }, - app - ) - .listen(process.env.PORT || port, () => { - if (process.env.PORT) - console.log(`REST API is listening on: https://localhost:${process.env.PORT}`); - else - console.log(`REST API is listening on: http://localhost:${port}.`); -}); -*/ - -// recipeAPI.getRecipe( { -// "ingredients" : ["banana", "milk"], -// "maxCal" : 1500 -// }); diff --git a/backend/middleware.js b/backend/middleware.js new file mode 100644 index 0000000..c43079f --- /dev/null +++ b/backend/middleware.js @@ -0,0 +1,26 @@ +const jwt = require("jsonwebtoken"); + +//env for JWT token secret +const dotenv = require("dotenv"); +dotenv.config(); + +//middleware to authenticate token, used for protected routes +async function authenticateToken(req, res, next) { + //let token = req.headers.Authorization.split(" ")[1]; + let token = req.headers["token"]; + // if token is null return a response with status 401 and end the request + if (token == null) res.status(401).send("Unauthorized"); + else { + jwt.verify(token, process.env.TOKEN_SECRET, (err, user) => { + if (err) res.status(403).send("Forbidden"); + else { + req._id = user.id; + next(); + } + }); + } +} + +module.exports = { + authenticateToken, +}; diff --git a/backend/package-lock.json b/backend/package-lock.json index 3ec8d2a..82171f6 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -11,6 +11,7 @@ "dependencies": { "axios": "^1.4.0", "bcrypt": "^5.1.0", + "cookie-parser": "^1.4.6", "cores": "^0.8.0", "cors": "^2.8.5", "dotenv": "^16.0.3", @@ -1838,6 +1839,26 @@ "node": ">= 0.6" } }, + "node_modules/cookie-parser": { + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz", + "integrity": "sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==", + "dependencies": { + "cookie": "0.4.1", + "cookie-signature": "1.0.6" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/cookie-parser/node_modules/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", + "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", diff --git a/backend/package.json b/backend/package.json index d347706..0c7612b 100644 --- a/backend/package.json +++ b/backend/package.json @@ -5,6 +5,7 @@ "dependencies": { "axios": "^1.4.0", "bcrypt": "^5.1.0", + "cookie-parser": "^1.4.6", "cores": "^0.8.0", "cors": "^2.8.5", "dotenv": "^16.0.3", diff --git a/backend/routes/authRoutes.js b/backend/routes/authRoutes.js new file mode 100644 index 0000000..8fccd73 --- /dev/null +++ b/backend/routes/authRoutes.js @@ -0,0 +1,76 @@ +const express = require("express"); +const router = express.Router(); + +//DB models +const userServices = require("../controllers/user-services"); + +//JWT module +const jwt = require("jsonwebtoken"); + +//env for JWT token secret +const dotenv = require("dotenv"); +dotenv.config(); + +function generateAccessToken(id) { + /* + Generates JWT + :param id: user DB ._id + :return: JWT token + */ + return jwt.sign(id, process.env.TOKEN_SECRET, { expiresIn: "1h" }); +} + +// -------------------------------------------------- +// AUTHENTICATION ENDPOINTS +// -------------------------------------------------- +// login endpoint: +// get username and password from request body and pass to +// userServices.login() which authenticates the user from +// the database +router.post("/login", async (req, res) => { + const user = req.body; + try { + const result = await userServices.login(user.email, user.password); + console.log(result); + + if (result === undefined || result.length === 0) { + res.status(404).send("Resource not found."); + } else { + const token = generateAccessToken({ id: result._id }); + res.status(201).json({ token: token }); + //res.send({ users_list: result }); + } + } catch (error) { + console.log(error); + res.status(500).send("Internal Server Error."); + } +}); + +// register endpoint: +// get username and password from request body and pass to +// userServices.register() which adds the user to the database +// after using bcrypt to hash the password +router.post("/register", async (req, res) => { + try { + const user = req.body; + console.log(user); + const result = await userServices.register(user.email, user.password); + + if (result === undefined || result.length === 0) { + res.status(404).send("Resource not found."); + } else { + const token = generateAccessToken({ id: result._id }); + res.json({ token: token }).status(201); + //res.send({ users_list: result}); + } + } catch (error) { + console.log(error); + res.status(500).send("Internal Server Error."); + } +}); + +router.get("/login", async (req, res) => { + res.send("

test

"); +}); + +module.exports = router; diff --git a/backend/routes/ingredientRoutes.js b/backend/routes/ingredientRoutes.js new file mode 100644 index 0000000..84ac384 --- /dev/null +++ b/backend/routes/ingredientRoutes.js @@ -0,0 +1,51 @@ +const express = require("express"); +const router = express.Router(); + + +//DB models +const userServices = require("../controllers/user-services"); + +// -------------------------------------------------- +// INGREDIENT ENDPOINTS +// -------------------------------------------------- +// Get all ingredients endpoint: + +//return ingredients associated with user +router.get("/", async (req, res) => { + try { + const id = req._id; + const result = await userServices.getIngredients(id); + if (result === undefined || result.length === 0) { + res.status(404).send("Resource not found."); + } else { + res.status(201).send({ ingredients_list: result }); // can be empty array (no error if nothing found) + } + } catch (error) { + console.log(error); + res.status(500).send("Internal Server Error."); + } +}); + +// Create ingredient endpoint: +router.put("/", async (req, res) => { + const data = req.body; + try { + const id = req._id; + const updatedUser = await userServices.updateIngredients( + id, + data.ingredients + ); + + if (updatedUser) { + res.status(201).send(updatedUser).end(); + } else { + res.status(400).send("Bad Request."); + } + } catch (error) { + console.log(error); + res.status(500).send("Internal Server Error."); + } +}); + + +module.exports = router; diff --git a/backend/routes/recipeRoutes.js b/backend/routes/recipeRoutes.js new file mode 100644 index 0000000..3dfb7a2 --- /dev/null +++ b/backend/routes/recipeRoutes.js @@ -0,0 +1,152 @@ +const express = require("express"); +const router = express.Router(); + +// Spoonacular API methods +const recipeAPI = require("../recipeAPI.js"); + +//DB models +const userServices = require("../controllers/user-services"); +const recipeServices = require("../controllers/recipe-services"); + +router.delete("/:id", async (req, res) => { + try { + const recipe_id = req.params.id; + const user_id = req._id; + const result = await userServices.removeRecipe(user_id, recipe_id); + if (!result) { + res.status(404).send("Resource not found."); + } else { + res.status(201).send({ users_list: result }); + } + } catch (error) { + console.log(error); + res.status(500).send("Internal Server Error."); + } +}); + +//load in saved recipes +router.get("/", async (req, res) => { + try { + //send back recipes associated with user + const id = req._id; + const recipes = await userServices.getRecipes(id); + if (recipes === undefined || recipes.length === 0) { + res.status(404).send("Recipes not Found for User"); + } else { + res.status(201).send(recipes).end(); + } + } catch (error) { + console.log(error); + res.status(505).send("Recipes not Found for User"); + } +}); + +// Get recipe by Id endpoint: +router.get("/:id", async (req, res) => { + const id = req.params.id; + try { + const result = await recipeServices.getRecipeByID(id); + if (result === undefined || result.length === 0) { + res.status(404).send("Resource not found."); + } else { + res.send({ result }); + } + } catch (error) { + console.log(error); + res.status(500).send("Internal Server Error."); + } +}); + +//search API for recipes +router.post("/", async (req, res) => { + try { + const id = req._id; + parameters = req.body; + recipes = await recipeAPI.getRecipes(parameters); + + if (recipes === undefined || recipes.length === 0) { + res.status(404).send("Resource not found."); + } else { + //check if recipe already exists in DB + for (let i = 0; i < recipes.length; i++) { + let recipe = await recipeServices.getRecipeByWebID(recipes[i].id); + if (recipe) { + recipes[i] = recipe; + } else { + //add recipe to database + recipes[i] = await recipeServices.addRecipe(recipes[i]); + } + } + const favorites = await userServices.getRecipes(id); + res.status(201).send({ + favorites: favorites, + recipes_list: recipes, + }); + } + } catch (error) { + console.log("ERROR IN RECIPE POST"); + console.log(error); + res.status(500).send("BAD AUTH /services/recipes"); + } +}); + +//Favorite Recipe +router.patch("/:id", async (req, res) => { + try { + //data base id for user and recipe + const userID = req._id; + const recipeID = req.params["id"]; + const result = await userServices.addRecipe(userID, recipeID); + if (!result) { + res.status(500).end(); + } else { + res.status(201).end(); + } + } catch (error) { + console.log(error); + res.status(500); + } +}); + +//Get ratings for recipe +// - most of this functionality is handled in the frontend, +// this endpoint sends a json object of a recipe +router.get("/:id/ratings", async (req, res) => { + try { + //get recipe ID + const recipeID = req.params["id"]; + const ratings = await recipeServices.getRatings(recipeID); + + if (ratings === undefined) { + res.status(404).send("Resource not Found").end(); + } else { + res.status(201).send(ratings).end(); + } + } catch (error) { + console.log(error); + res.status(500); + } +}); + +//add rating to recipe +router.patch("/:id/ratings", async (req, res) => { + try { + //get recipeID and new rating to add to recipe + const recipeID = req.params["id"]; + const rating = req.body["rating"]; + + //add rating to recipe + const result = await recipeServices.addRating(recipeID, rating); + + if (!result) { + res.status(404).end(); + } else { + res.status(201).send(result).end(); + } + } catch (error) { + console.log(error); + res.status(500); + } +}); + +module.exports = router; diff --git a/backend/routes/userRoutes.js b/backend/routes/userRoutes.js new file mode 100644 index 0000000..6909f61 --- /dev/null +++ b/backend/routes/userRoutes.js @@ -0,0 +1,79 @@ +//express router +const express = require("express"); +const router = express.Router(); + +//DB models +const userServices = require("../controllers/user-services"); + + +// -------------------------------------------------- +// USER ENDPOINTS +// -------------------------------------------------- +// Get users endpoint: +router.get("/", async (req, res) => { + const name = req.query["name"]; + try { + const result = await userServices.getUsers(name); + res.send({ users_list: result }); // can be empty array (no error if nothing found) + } catch (error) { + console.log(error); + res.status(500).send("Internal Server Error."); + } +}); + +// Get User by Id endpoint: +router.get("/:id", async (req, res) => { + const id = req.params.id; + try { + const result = await userServices.findUserById(id); + if (result === undefined || result.length === 0) { + res.status(404).send("Resource not found."); + } else { + res.send({ users_list: result }); + } + } catch (error) { + console.log(error); + res.status(500).send("Internal Server Error."); + } +}); + + + +// Populate the Recipes for a specific User +router.get("/:id/recipes", async (req, res) => { + const user_id = req.params.id; + try { + const user = await userServices.findUserById(user_id); + const result = await userServices.getRecipes(user); + if (result === undefined || result.length === 0) { + res.status(404).send("Resource not found."); + } else { + res.send({ users_list: result }); + } + } catch (error) { + console.log(error); + res.status(500).send("Internal Server Error."); + } +}); + + +// Add a Recipe to a User's Saved Recipes endpoint: +// - body of the request to this endpoint contains 1 field: recipe_id +router.post("/:id/recipes", async (req, res) => { + const user_id = req.params.id; + const user = await userServices.findUserById(user_id); + const recipe_id = req.body.recipe_id; + try { + const result = await userServices.addRecipe(user, recipe_id); + if (result === undefined || result.length === 0) { + res.status(404).send("Resource not found."); + } else { + res.send({ users_list: result }); + } + } catch (error) { + console.log(error); + res.status(500).send("Internal Server Error."); + } +}); + +module.exports = router; diff --git a/backend/yarn.lock b/backend/yarn.lock index c441234..32ca323 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -1095,11 +1095,24 @@ convert-source-map@^2.0.0: resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz" integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== +cookie-parser@^1.4.6: + version "1.4.6" + resolved "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz" + integrity sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA== + dependencies: + cookie "0.4.1" + cookie-signature "1.0.6" + cookie-signature@1.0.6: version "1.0.6" resolved "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz" integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== +cookie@0.4.1: + version "0.4.1" + resolved "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz" + integrity sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA== + cookie@0.5.0: version "0.5.0" resolved "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz" @@ -1436,11 +1449,6 @@ fs.realpath@^1.0.0: resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== -fsevents@^2.3.2, fsevents@~2.3.2: - version "2.3.2" - resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz" - integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== - function-bind@^1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz"