├── .gitignore ├── client ├── public │ ├── robots.txt │ ├── favicon.ico │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── index.html ├── src │ ├── index.js │ ├── apis │ │ └── RestaurantFinder.js │ ├── components │ │ ├── Header.jsx │ │ ├── StarRating.jsx │ │ ├── AddRestaurant.jsx │ │ ├── UpdateRestaurant.jsx │ │ ├── AddReview.jsx │ │ ├── Reviews.jsx │ │ └── RestaurantList.jsx │ ├── routes │ │ ├── UpdatePage.jsx │ │ ├── Home.jsx │ │ └── RestaurantDetailPage.jsx │ ├── context │ │ └── RestaurantsContext.js │ └── App.jsx ├── package.json └── README.md ├── server ├── db │ ├── index.js │ └── db.sql ├── package.json ├── server.js └── package-lock.json └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .env -------------------------------------------------------------------------------- /client/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /client/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sanjeev-Thiyagarajan/PERN-STACK-DEPLOYMENT/HEAD/client/public/favicon.ico -------------------------------------------------------------------------------- /client/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sanjeev-Thiyagarajan/PERN-STACK-DEPLOYMENT/HEAD/client/public/logo192.png -------------------------------------------------------------------------------- /client/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sanjeev-Thiyagarajan/PERN-STACK-DEPLOYMENT/HEAD/client/public/logo512.png -------------------------------------------------------------------------------- /server/db/index.js: -------------------------------------------------------------------------------- 1 | const { Pool } = require("pg"); 2 | 3 | const pool = new Pool(); 4 | module.exports = { 5 | query: (text, params) => pool.query(text, params), 6 | }; 7 | -------------------------------------------------------------------------------- /client/src/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | import App from "./App"; 4 | 5 | ReactDOM.render(, document.getElementById("root")); 6 | -------------------------------------------------------------------------------- /client/src/apis/RestaurantFinder.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | 3 | const baseURL = 4 | process.env.NODE_ENV === "production" 5 | ? "/api/v1/restaurants" 6 | : "http://localhost:3001/api/v1/restaurants"; 7 | 8 | export default axios.create({ 9 | baseURL, 10 | }); 11 | -------------------------------------------------------------------------------- /client/src/components/Header.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const Header = () => { 4 | return ( 5 |
6 |

7 | Restaurant Finder 8 |

9 |
10 | ); 11 | }; 12 | 13 | export default Header; 14 | -------------------------------------------------------------------------------- /client/src/routes/UpdatePage.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import UpdateRestaurant from "../components/UpdateRestaurant"; 3 | 4 | const UpdatePage = () => { 5 | return ( 6 |
7 |

Update Restaurant

8 | 9 |
10 | ); 11 | }; 12 | 13 | export default UpdatePage; 14 | -------------------------------------------------------------------------------- /client/src/routes/Home.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Header from "../components/Header"; 3 | import AddRestaurant from "../components/AddRestaurant"; 4 | import RestaurantList from "../components/RestaurantList"; 5 | 6 | const Home = () => { 7 | return ( 8 |
9 |
10 | 11 | 12 |
13 | ); 14 | }; 15 | 16 | export default Home; 17 | -------------------------------------------------------------------------------- /server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node server.js", 8 | "dev": "nodemon server.js" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "dependencies": { 14 | "cors": "^2.8.5", 15 | "dotenv": "^8.2.0", 16 | "express": "^4.17.1", 17 | "morgan": "^1.10.0", 18 | "nodemon": "^2.0.4", 19 | "pg": "^8.2.1" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /server/db/db.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE reviews ( 2 | id BIGSERIAL NOT NULL PRIMARY KEY, 3 | restaurant_id BIGINT NOT NULL REFERENCES restaurants(id), 4 | name VARCHAR(50) NOT NULL, 5 | review TEXT NOT NULL, 6 | rating INT NOT NULL check( 7 | rating >= 1 8 | and rating <= 5 9 | ) 10 | ); 11 | select * 12 | from restaurants 13 | left join( 14 | select restaurant_id, 15 | count(*), 16 | TRUNC(AVG(rating, 1)) as average_rating 17 | from reviews 18 | group by restaurant_id 19 | ) reviews on restaurants.id = reviews.restaurant_id; -------------------------------------------------------------------------------- /client/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /client/src/components/StarRating.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const StarRating = ({ rating }) => { 4 | //rating =4 5 | const stars = []; 6 | for (let i = 1; i <= 5; i++) { 7 | if (i <= rating) { 8 | stars.push(); 9 | } else if (i === Math.ceil(rating) && !Number.isInteger(rating)) { 10 | stars.push(); 11 | } else { 12 | stars.push(); 13 | } 14 | } 15 | return <>{stars}; 16 | }; 17 | 18 | export default StarRating; 19 | -------------------------------------------------------------------------------- /client/src/context/RestaurantsContext.js: -------------------------------------------------------------------------------- 1 | import React, { useState, createContext } from "react"; 2 | 3 | export const RestaurantsContext = createContext(); 4 | 5 | export const RestaurantsContextProvider = (props) => { 6 | const [restaurants, setRestaurants] = useState([]); 7 | const [selectedRestaurant, setSelectedRestaurant] = useState(null); 8 | 9 | const addRestaurants = (restaurant) => { 10 | setRestaurants([...restaurants, restaurant]); 11 | }; 12 | return ( 13 | 22 | {props.children} 23 | 24 | ); 25 | }; 26 | -------------------------------------------------------------------------------- /client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "client", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^4.2.4", 7 | "@testing-library/react": "^9.5.0", 8 | "@testing-library/user-event": "^7.2.1", 9 | "axios": "^0.19.2", 10 | "react": "^16.13.1", 11 | "react-dom": "^16.13.1", 12 | "react-router-dom": "^5.2.0", 13 | "react-scripts": "3.4.1" 14 | }, 15 | "scripts": { 16 | "start": "react-scripts start", 17 | "build": "react-scripts build", 18 | "test": "react-scripts test", 19 | "eject": "react-scripts eject" 20 | }, 21 | "eslintConfig": { 22 | "extends": "react-app" 23 | }, 24 | "browserslist": { 25 | "production": [ 26 | ">0.2%", 27 | "not dead", 28 | "not op_mini all" 29 | ], 30 | "development": [ 31 | "last 1 chrome version", 32 | "last 1 firefox version", 33 | "last 1 safari version" 34 | ] 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /client/src/App.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { BrowserRouter as Router, Switch, Route } from "react-router-dom"; 3 | import Home from "./routes/Home"; 4 | import UpdatePage from "./routes/UpdatePage"; 5 | import RestaurantDetailPage from "./routes/RestaurantDetailPage"; 6 | import { RestaurantsContextProvider } from "./context/RestaurantsContext"; 7 | const App = () => { 8 | return ( 9 | 10 |
11 | 12 | 13 | 14 | 19 | 24 | 25 | 26 |
27 |
28 | ); 29 | }; 30 | 31 | export default App; 32 | -------------------------------------------------------------------------------- /client/src/routes/RestaurantDetailPage.jsx: -------------------------------------------------------------------------------- 1 | import React, { useContext, useEffect } from "react"; 2 | import { useParams } from "react-router-dom"; 3 | import { RestaurantsContext } from "../context/RestaurantsContext"; 4 | import RestaurantFinder from "../apis/RestaurantFinder"; 5 | import StarRating from "../components/StarRating"; 6 | import Reviews from "../components/Reviews"; 7 | import AddReview from "../components/AddReview"; 8 | 9 | const RestaurantDetailPage = () => { 10 | const { id } = useParams(); 11 | const { selectedRestaurant, setSelectedRestaurant } = useContext( 12 | RestaurantsContext 13 | ); 14 | 15 | useEffect(() => { 16 | const fetchData = async () => { 17 | try { 18 | const response = await RestaurantFinder.get(`/${id}`); 19 | console.log(response); 20 | 21 | setSelectedRestaurant(response.data.data); 22 | } catch (err) { 23 | console.log(err); 24 | } 25 | }; 26 | 27 | fetchData(); 28 | }, []); 29 | return ( 30 |
31 | {selectedRestaurant && ( 32 | <> 33 |

34 | {selectedRestaurant.restaurant.name} 35 |

36 |
37 | 38 | 39 | {selectedRestaurant.restaurant.count 40 | ? `(${selectedRestaurant.restaurant.count})` 41 | : "(0)"} 42 | 43 |
44 |
45 | 46 |
47 | 48 | 49 | )} 50 |
51 | ); 52 | }; 53 | 54 | export default RestaurantDetailPage; 55 | -------------------------------------------------------------------------------- /client/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 24 | 28 | 37 | React App 38 | 39 | 40 | 41 |
42 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /client/src/components/AddRestaurant.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useContext } from "react"; 2 | import RestaurantFinder from "../apis/RestaurantFinder"; 3 | import { RestaurantsContext } from "../context/RestaurantsContext"; 4 | 5 | const AddRestaurant = () => { 6 | const { addRestaurants } = useContext(RestaurantsContext); 7 | const [name, setName] = useState(""); 8 | const [location, setLocation] = useState(""); 9 | const [priceRange, setPriceRange] = useState("Price Range"); 10 | 11 | const handleSubmit = async (e) => { 12 | e.preventDefault(); 13 | try { 14 | const response = await RestaurantFinder.post("/", { 15 | name, 16 | location, 17 | price_range: priceRange, 18 | }); 19 | console.log(response.data.data); 20 | addRestaurants(response.data.data.restaurant); 21 | } catch (err) { 22 | console.log(err); 23 | } 24 | }; 25 | return ( 26 |
27 |
28 |
29 |
30 | setName(e.target.value)} 33 | type="text" 34 | className="form-control" 35 | placeholder="name" 36 | /> 37 |
38 |
39 | setLocation(e.target.value)} 42 | className="form-control" 43 | type="text" 44 | placeholder="location" 45 | /> 46 |
47 |
48 | 60 |
61 | 68 |
69 |
70 |
71 | ); 72 | }; 73 | 74 | export default AddRestaurant; 75 | -------------------------------------------------------------------------------- /client/src/components/UpdateRestaurant.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useContext, useEffect } from "react"; 2 | import { useParams, useHistory } from "react-router-dom"; 3 | import { RestaurantsContext } from "../context/RestaurantsContext"; 4 | import RestaurantFinder from "../apis/RestaurantFinder"; 5 | 6 | const UpdateRestaurant = (props) => { 7 | const { id } = useParams(); 8 | let history = useHistory(); 9 | const { restaurants } = useContext(RestaurantsContext); 10 | const [name, setName] = useState(""); 11 | const [location, setLocation] = useState(""); 12 | const [priceRange, setPriceRange] = useState(""); 13 | 14 | useEffect(() => { 15 | const fetchData = async () => { 16 | const response = await RestaurantFinder.get(`/${id}`); 17 | console.log(response.data.data); 18 | setName(response.data.data.restaurant.name); 19 | setLocation(response.data.data.restaurant.location); 20 | setPriceRange(response.data.data.restaurant.price_range); 21 | }; 22 | 23 | fetchData(); 24 | }, []); 25 | 26 | const handleSubmit = async (e) => { 27 | e.preventDefault(); 28 | const updatedRestaurant = await RestaurantFinder.put(`/${id}`, { 29 | name, 30 | location, 31 | price_range: priceRange, 32 | }); 33 | history.push("/"); 34 | }; 35 | 36 | return ( 37 |
38 |
39 |
40 | 41 | setName(e.target.value)} 44 | id="name" 45 | className="form-control" 46 | type="text" 47 | /> 48 |
49 | 50 |
51 | 52 | setLocation(e.target.value)} 55 | id="location" 56 | className="form-control" 57 | type="text" 58 | /> 59 |
60 |
61 | 62 | setPriceRange(e.target.value)} 65 | id="price_range" 66 | className="form-control" 67 | type="number" 68 | /> 69 |
70 | 77 |
78 |
79 | ); 80 | }; 81 | 82 | export default UpdateRestaurant; 83 | -------------------------------------------------------------------------------- /client/src/components/AddReview.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import RestaurantFinder from "../apis/RestaurantFinder"; 3 | import { useLocation, useParams, useHistory } from "react-router-dom"; 4 | 5 | const AddReview = () => { 6 | const { id } = useParams(); 7 | const location = useLocation(); 8 | console.log(location); 9 | const history = useHistory(); 10 | console.log(id); 11 | 12 | const [name, setName] = useState(""); 13 | const [reviewText, setReviewText] = useState(""); 14 | const [rating, setRating] = useState("Rating"); 15 | 16 | const handleSubmitReview = async (e) => { 17 | e.preventDefault(); 18 | try { 19 | const response = await RestaurantFinder.post(`/${id}/addReview`, { 20 | name, 21 | review: reviewText, 22 | rating, 23 | }); 24 | history.push("/"); 25 | history.push(location.pathname); 26 | } catch (err) {} 27 | }; 28 | return ( 29 |
30 |
31 |
32 |
33 | 34 | setName(e.target.value)} 37 | id="name" 38 | placeholder="name" 39 | type="text" 40 | className="form-control" 41 | /> 42 |
43 |
44 | 45 | 58 |
59 |
60 |
61 | 62 | 68 |
69 | 76 |
77 |
78 | ); 79 | }; 80 | 81 | export default AddReview; 82 | -------------------------------------------------------------------------------- /client/src/components/Reviews.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import StarRating from "./StarRating"; 3 | 4 | const Reviews = ({ reviews }) => { 5 | return ( 6 |
7 | {reviews.map((review) => { 8 | return ( 9 |
14 |
15 | {review.name} 16 | 17 | 18 | 19 |
20 |
21 |

{review.review}

22 |
23 |
24 | ); 25 | })} 26 | {/*
30 |
31 | Joan 32 | 33 | 34 | 35 |
36 |
37 |

This restaurant was awesome

38 |
39 |
40 | 41 |
45 |
46 | Joan 47 | 48 | 49 | 50 |
51 |
52 |

This restaurant was awesome

53 |
54 |
55 | 56 |
60 |
61 | Joan 62 | 63 | 64 | 65 |
66 |
67 |

This restaurant was awesome

68 |
69 |
70 |
74 |
75 | Joan 76 | 77 | 78 | 79 |
80 |
81 |

This restaurant was awesome

82 |
83 |
*/} 84 |
85 | ); 86 | }; 87 | 88 | export default Reviews; 89 | -------------------------------------------------------------------------------- /client/README.md: -------------------------------------------------------------------------------- 1 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 2 | 3 | ## Available Scripts 4 | 5 | In the project directory, you can run: 6 | 7 | ### `npm start` 8 | 9 | Runs the app in the development mode.
10 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser. 11 | 12 | The page will reload if you make edits.
13 | You will also see any lint errors in the console. 14 | 15 | ### `npm test` 16 | 17 | Launches the test runner in the interactive watch mode.
18 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. 19 | 20 | ### `npm run build` 21 | 22 | Builds the app for production to the `build` folder.
23 | It correctly bundles React in production mode and optimizes the build for the best performance. 24 | 25 | The build is minified and the filenames include the hashes.
26 | Your app is ready to be deployed! 27 | 28 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. 29 | 30 | ### `npm run eject` 31 | 32 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!** 33 | 34 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. 35 | 36 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own. 37 | 38 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. 39 | 40 | ## Learn More 41 | 42 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). 43 | 44 | To learn React, check out the [React documentation](https://reactjs.org/). 45 | 46 | ### Code Splitting 47 | 48 | This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting 49 | 50 | ### Analyzing the Bundle Size 51 | 52 | This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size 53 | 54 | ### Making a Progressive Web App 55 | 56 | This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app 57 | 58 | ### Advanced Configuration 59 | 60 | This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration 61 | 62 | ### Deployment 63 | 64 | This section has moved here: https://facebook.github.io/create-react-app/docs/deployment 65 | 66 | ### `npm run build` fails to minify 67 | 68 | This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify 69 | -------------------------------------------------------------------------------- /server/server.js: -------------------------------------------------------------------------------- 1 | require("dotenv").config(); 2 | const express = require("express"); 3 | const cors = require("cors"); 4 | const db = require("./db"); 5 | 6 | const morgan = require("morgan"); 7 | 8 | const app = express(); 9 | 10 | app.use(cors()); 11 | app.use(express.json()); 12 | 13 | // Get all Restaurants 14 | app.get("/api/v1/restaurants", async (req, res) => { 15 | try { 16 | //const results = await db.query("select * from restaurants"); 17 | const restaurantRatingsData = await db.query( 18 | "select * from restaurants left join (select restaurant_id, COUNT(*), TRUNC(AVG(rating),1) as average_rating from reviews group by restaurant_id) reviews on restaurants.id = reviews.restaurant_id;" 19 | ); 20 | 21 | res.status(200).json({ 22 | status: "success", 23 | results: restaurantRatingsData.rows.length, 24 | data: { 25 | restaurants: restaurantRatingsData.rows, 26 | }, 27 | }); 28 | } catch (err) { 29 | console.log(err); 30 | } 31 | }); 32 | 33 | //Get a Restaurant 34 | app.get("/api/v1/restaurants/:id", async (req, res) => { 35 | console.log(req.params.id); 36 | 37 | try { 38 | const restaurant = await db.query( 39 | "select * from restaurants left join (select restaurant_id, COUNT(*), TRUNC(AVG(rating),1) as average_rating from reviews group by restaurant_id) reviews on restaurants.id = reviews.restaurant_id where id = $1", 40 | [req.params.id] 41 | ); 42 | // select * from restaurants wehre id = req.params.id 43 | 44 | const reviews = await db.query( 45 | "select * from reviews where restaurant_id = $1", 46 | [req.params.id] 47 | ); 48 | console.log(reviews); 49 | 50 | res.status(200).json({ 51 | status: "succes", 52 | data: { 53 | restaurant: restaurant.rows[0], 54 | reviews: reviews.rows, 55 | }, 56 | }); 57 | } catch (err) { 58 | console.log(err); 59 | } 60 | }); 61 | 62 | // Create a Restaurant 63 | 64 | app.post("/api/v1/restaurants", async (req, res) => { 65 | console.log(req.body); 66 | 67 | try { 68 | const results = await db.query( 69 | "INSERT INTO restaurants (name, location, price_range) values ($1, $2, $3) returning *", 70 | [req.body.name, req.body.location, req.body.price_range] 71 | ); 72 | console.log(results); 73 | res.status(201).json({ 74 | status: "succes", 75 | data: { 76 | restaurant: results.rows[0], 77 | }, 78 | }); 79 | } catch (err) { 80 | console.log(err); 81 | } 82 | }); 83 | 84 | // Update Restaurants 85 | 86 | app.put("/api/v1/restaurants/:id", async (req, res) => { 87 | try { 88 | const results = await db.query( 89 | "UPDATE restaurants SET name = $1, location = $2, price_range = $3 where id = $4 returning *", 90 | [req.body.name, req.body.location, req.body.price_range, req.params.id] 91 | ); 92 | 93 | res.status(200).json({ 94 | status: "succes", 95 | data: { 96 | retaurant: results.rows[0], 97 | }, 98 | }); 99 | } catch (err) { 100 | console.log(err); 101 | } 102 | console.log(req.params.id); 103 | console.log(req.body); 104 | }); 105 | 106 | // Delete Restaurant 107 | 108 | app.delete("/api/v1/restaurants/:id", async (req, res) => { 109 | try { 110 | const results = db.query("DELETE FROM restaurants where id = $1", [ 111 | req.params.id, 112 | ]); 113 | res.status(204).json({ 114 | status: "sucess", 115 | }); 116 | } catch (err) { 117 | console.log(err); 118 | } 119 | }); 120 | 121 | app.post("/api/v1/restaurants/:id/addReview", async (req, res) => { 122 | try { 123 | const newReview = await db.query( 124 | "INSERT INTO reviews (restaurant_id, name, review, rating) values ($1, $2, $3, $4) returning *;", 125 | [req.params.id, req.body.name, req.body.review, req.body.rating] 126 | ); 127 | console.log(newReview); 128 | res.status(201).json({ 129 | status: "success", 130 | data: { 131 | review: newReview.rows[0], 132 | }, 133 | }); 134 | } catch (err) { 135 | console.log(err); 136 | } 137 | }); 138 | 139 | const port = process.env.PORT || 3001; 140 | app.listen(port, () => { 141 | console.log(`server is up and listening on port ${port}`); 142 | }); 143 | -------------------------------------------------------------------------------- /client/src/components/RestaurantList.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useContext } from "react"; 2 | import RestaurantFinder from "../apis/RestaurantFinder"; 3 | import { RestaurantsContext } from "../context/RestaurantsContext"; 4 | import { useHistory } from "react-router-dom"; 5 | import StarRating from "./StarRating"; 6 | 7 | const RestaurantList = (props) => { 8 | const { restaurants, setRestaurants } = useContext(RestaurantsContext); 9 | let history = useHistory(); 10 | useEffect(() => { 11 | const fetchData = async () => { 12 | try { 13 | const response = await RestaurantFinder.get("/"); 14 | console.log(response.data.data); 15 | setRestaurants(response.data.data.restaurants); 16 | } catch (err) {} 17 | }; 18 | 19 | fetchData(); 20 | }, []); 21 | 22 | const handleDelete = async (e, id) => { 23 | e.stopPropagation(); 24 | try { 25 | const response = await RestaurantFinder.delete(`/${id}`); 26 | setRestaurants( 27 | restaurants.filter((restaurant) => { 28 | return restaurant.id !== id; 29 | }) 30 | ); 31 | } catch (err) { 32 | console.log(err); 33 | } 34 | }; 35 | 36 | const handleUpdate = (e, id) => { 37 | e.stopPropagation(); 38 | history.push(`/restaurants/${id}/update`); 39 | }; 40 | 41 | const handleRestaurantSelect = (id) => { 42 | history.push(`/restaurants/${id}`); 43 | }; 44 | 45 | const renderRating = (restaurant) => { 46 | if (!restaurant.count) { 47 | return 0 reviews; 48 | } 49 | return ( 50 | <> 51 | 52 | ({restaurant.count}) 53 | 54 | ); 55 | }; 56 | 57 | return ( 58 |
59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | {restaurants && 72 | restaurants.map((restaurant) => { 73 | return ( 74 | handleRestaurantSelect(restaurant.id)} 76 | key={restaurant.id} 77 | > 78 | 79 | 80 | 81 | 82 | 90 | 98 | 99 | ); 100 | })} 101 | {/* 102 | 103 | 104 | 105 | 106 | 109 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 122 | 125 | */} 126 | 127 |
RestaurantLocationPrice RangeRatingsEditDelete
{restaurant.name}{restaurant.location}{"$".repeat(restaurant.price_range)}{renderRating(restaurant)} 83 | 89 | 91 | 97 |
mcdonaldsNew YOrk$$Rating 107 | 108 | 110 | 111 |
mcdonaldsNew YOrk$$Rating 120 | 121 | 123 | 124 |
128 |
129 | ); 130 | }; 131 | 132 | export default RestaurantList; 133 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Deploying PERN stack on Ubuntu 20.04 2 | 3 | > Detailed step by step procedure to deploying PERN(Postgres, Express, React, Node) stack on Ubuntu 20.04 with NGINX and SSL 4 | 5 | ## 1. Install and Configure PostgreSQL 6 | 7 | Update packages 8 | ``` 9 | sudo apt update && sudo apt upgrade -y 10 | ``` 11 | 12 | install PostgreSQL 13 | ``` 14 | sudo apt install postgresql postgresql-contrib -y 15 | ``` 16 | 17 | PostgreSQL will create an initial user called `postgres`. In addition, PostgreSQL uses what's reffered to as peer authentication for local connections. This means that PostgreSQL obtains the username from the linux kernel and uses that to connect to the database. This requires that any user configured on postgres to have an equivalent user defined on Ubuntu. Postgres installation will have automatically created a `postgres` user on Ubuntu as well to allow local connection. this can be verified by running the command: 18 | 19 | ``` 20 | ubuntu@ip-172-31-20-1:~$ sudo cat /etc/passwd | grep -i postgres 21 | postgres:x:113:120:PostgreSQL administrator,,,:/var/lib/postgresql:/bin/bash 22 | ``` 23 | 24 | To connect to Postgres, switch to the `postgres` user and run psql: 25 | 26 | ``` 27 | ubuntu@ip-172-31-20-1:~$ sudo -i -u postgres 28 | postgres@ip-172-31-20-1:~$ psql 29 | psql (12.4 (Ubuntu 12.4-0ubuntu0.20.04.1)) 30 | Type "help" for help. 31 | 32 | postgres=# 33 | ``` 34 | 35 | For this tutorial, the node process process will be run under the `ubuntu` user and for the sake of simplicity, an `ubuntu` user will be created on Postgres as well. If you want to use a different user feel free to create a different user in postgres. 36 | 37 | To create a Postgres user run the following command which will give an interactive prompt for configuring the new user. For the sake of simplicity, the ubuntu user will be a superuser, which is the equivalent of being a root user on linux. The super user will have the ability to create/delete/modify databases and users. 38 | 39 | ``` 40 | postgres@ip-172-31-20-1:~$ createuser --interactive 41 | Enter name of role to add: ubuntu 42 | Shall the new role be a superuser? (y/n) y 43 | ``` 44 | 45 | login to postgres using the `postgres` user for now to verify the new `ubuntu` user was created successfully 46 | 47 | ``` 48 | postgres@ip-172-31-20-1:~$ psql 49 | psql (12.4 (Ubuntu 12.4-0ubuntu0.20.04.1)) 50 | Type "help" for help. 51 | 52 | postgres=# \du 53 | List of roles 54 | Role name | Attributes | Member of 55 | -----------+------------------------------------------------------------+----------- 56 | postgres | Superuser, Create role, Create DB, Replication, Bypass RLS | {} 57 | sanjeev | Superuser, Create role, Create DB | {} 58 | test | | {} 59 | test1 | Superuser, Create role, Create DB | {} 60 | ubuntu | Superuser, Create role, Create DB | {} 61 | 62 | postgres=# 63 | ``` 64 | 65 | Exit out of the psql by running `\q` and also exit out of the `postgres` user by running `exit` on the command line 66 | 67 | Let's try to run `psql` as the ubuntu user now. An error similar to the one below should be observed 68 | 69 | ``` 70 | ubuntu@ip-172-31-20-1:~$ psql 71 | psql: error: could not connect to server: FATAL: database "ubuntu" does not exist 72 | ``` 73 | 74 | The reason for this is that Postgres by default tries to connect to a database that is the same name as the user. Since the user is `ubuntu` it tries to connect to a database called `ubuntu` as well which does not exist. We can go in and create a database called `ubuntu` so that it will automtically connect, however I find this unnecessry. Instead we can pass in the `-d` flag and connect to a database that we know exists like the `postgres` 75 | 76 | ``` 77 | psql -d postgres 78 | ``` 79 | 80 | Right now the `ubuntu` user in Postgres does not have a password associated with it. We will need to add a password: 81 | 82 | ``` 83 | ubuntu@ip-172-31-20-1:~$ psql -d postgres 84 | psql (12.4 (Ubuntu 12.4-0ubuntu0.20.04.1)) 85 | Type "help" for help. 86 | 87 | postgres=# \q 88 | ubuntu@ip-172-31-20-1:~$ psql -d postgres 89 | psql (12.4 (Ubuntu 12.4-0ubuntu0.20.04.1)) 90 | Type "help" for help. 91 | 92 | postgres=# ALTER USER ubuntu PASSWORD 'password'; 93 | ALTER ROLE 94 | postgres=# 95 | ``` 96 | 97 | ## 2. Migrate Database Scheme & data 98 | As with most sql database PostgreSQL allows us to easily copy our database schema and data from our local development postgres instance and copy it over to the Postgres isntance running in our production server. 99 | 100 | On your local dev machine, open up a terminal and run: 101 | 102 | ``` 103 | pg_dump -U postgres -f yelp.pgsql -C yelp 104 | ``` 105 | The `-U` flag specifies the user u want to login as, if you are using a different username, update it accordingly. 106 | The `-f yelp.pgsql` flag will write the database schema and data to a file called `yelp.pgsql` in the current directory 107 | `-C` flag add the create database command to the file as well 108 | `yelp` is the name of the database in our local dev server that we want to dump. If your database is called something else update that accordingly. If you leave out the database name alltogether it will dump all databases 109 | 110 | Copy the yelp.pgsql file to the production server 111 | ``` 112 | scp -i [path to pem file] [path to yelp.pgsql] username@[server-ip]:[directory to copy file to] 113 | ``` 114 | 115 | In this example the pem file and yelp.gsql file are located in the current directory and my server ip is `1.1.1.1` and the username is `ubuntu` 116 | 117 | ``` 118 | scp -i yelp.pem yelp.pgsql ubuntu@1.1.1.1:/home/ubuntu/ 119 | ``` 120 | 121 | Login to the production server and create a database called `yelp` 122 | ``` 123 | ubuntu@ip-172-31-20-1:~$ psql -d yelp 124 | postgres=# create database yelp; 125 | CREATE DATABASE 126 | ``` 127 | 128 | Import the database schema & data from the `yelp.pgsql` file 129 | 130 | ``` 131 | psql yelp < /home/ubuntu/yelp.pgsql 132 | ``` 133 | 134 | login to the yelp database and verify that everything looks ok 135 | ``` 136 | ubuntu@ip-172-31-20-1:~$ psql -d yelp 137 | psql (12.4 (Ubuntu 12.4-0ubuntu0.20.04.1)) 138 | Type "help" for help. 139 | 140 | yelp=# \d 141 | List of relations 142 | Schema | Name | Type | Owner 143 | --------+--------------------+----------+---------- 144 | public | restaurants | table | postgres 145 | public | restaurants_id_seq | sequence | postgres 146 | public | reviews | table | postgres 147 | public | reviews_id_seq | sequence | postgres 148 | (4 rows) 149 | 150 | yelp=# select * from restaurants; 151 | id | name | location | price_range 152 | ----+--------------------------+-----------------+------------- 153 | 2 | taco bell | san fran | 3 154 | 3 | taco bell | New York | 4 155 | 4 | cheesecake factory | dallas | 2 156 | 5 | cheesecake factory | dallas | 2 157 | 6 | cheesecake factory | houston | 2 158 | 10 | chiptle | virgini | 1 159 | 11 | chiptle | virgini | 1 160 | 13 | california pizza kitchen | vegas | 1 161 | 1 | wendys | Lincoln | 4 162 | 14 | california pizza kitchen | New mexico city | 3 163 | (10 rows) 164 | 165 | yelp=# 166 | ``` 167 | 168 | 169 | ## 3. Copy github repo to sever 170 | 171 | Find a place to store your application code. In this example in the `ubuntu` home directory a new directory called `apps` will be created. Within the new `apps` directory another directory called `yelp-app`. Feel free to store your application code anywhere you see fit 172 | 173 | ``` 174 | cd ~ 175 | mkdir apps 176 | cd apps 177 | mkdir yelp-app 178 | ``` 179 | 180 | move inside the `yelp-app` directory and clone the project repo 181 | ``` 182 | cd yelp-app 183 | git clone https://github.com/Sanjeev-Thiyagarajan/PERN-STACK-DEPLOYMENT.git . 184 | ``` 185 | 186 | ## 4. Install Node 187 | To install Node on Ubuntu follow the steps detailed in: 188 | https://github.com/nodesource/distributions/blob/master/README.md 189 | 190 | ``` 191 | curl -sL https://deb.nodesource.com/setup_14.x | sudo -E bash - 192 | sudo apt-get install -y nodejs 193 | ``` 194 | 195 | ## 5. Install and Configure PM2 196 | We never want to run *node* directly in production. Instead we want to use a process manager like PM2 to handle running our backend server. PM2 will be responsible for restarting the App if/when it crashes :grin: 197 | 198 | ``` 199 | sudo npm install pm2 -g 200 | ``` 201 | Point pm2 to the location of the server.js file so it can start the app. We can add the `--name` flag to give the process a descriptive name 202 | ``` 203 | pm2 start /home/ubuntu/apps/yelp-app/server/server.js --name yelp-app 204 | ``` 205 | 206 | Configure PM2 to automatically startup the process after a reboot 207 | 208 | ``` 209 | ubuntu@ip-172-31-20-1:~$ pm2 startup 210 | [PM2] Init System found: systemd 211 | [PM2] To setup the Startup Script, copy/paste the following command: 212 | sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u ubuntu --hp /home/ubuntu 213 | ``` 214 | The output above gives you a specific command to run, copy and paste it into the terminal. The command given will be different on your machine depending on the username, so do not copy the output above, instead run the command that is given in your output. 215 | 216 | ``` 217 | sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u ubuntu --hp /home/ubuntu 218 | ``` 219 | 220 | Verify that the App is running 221 | 222 | ``` 223 | pm2 status 224 | ``` 225 | After verify App is running, save the current list of processes so that the same processes are started during bootup. If the list of processes ever changes in the future, you'll want to do another `pm2 save` 226 | 227 | ``` 228 | pm2 save 229 | ``` 230 | 231 | ## 6. Deploy React Frontend 232 | Navigate to the client directory in our App code and run `npm run build`. 233 | 234 | This will create a finalized production ready version of our react frontent in directory called `build`. The build folder is what the NGINX server will be configured to serve. 235 | 236 | ``` 237 | ubuntu@ip-172-31-20-1:~/apps/yelp-app/client$ ls 238 | README.md build node_modules package-lock.json package.json public src 239 | ubuntu@ip-172-31-20-1:~/apps/yelp-app/client$ cd build/ 240 | ubuntu@ip-172-31-20-1:~/apps/yelp-app/client/build$ ls 241 | asset-manifest.json favicon.ico index.html logo192.png logo512.png manifest.json precache-manifest.ee13f4c95d9882a5229da70669bb264c.js robots.txt service-worker.js static 242 | ubuntu@ip-172-31-20-1:~/apps/yelp-app/client/build$ 243 | ``` 244 | 245 | ## 7. Install and Configure NGINX 246 | 247 | Install and enable NGINX 248 | ``` 249 | sudo apt install nginx -y 250 | sudo systemctl enable nginx 251 | ``` 252 | 253 | NGINX is a feature-rich webserver that can serve multiple websites/web-apps on one machine. Each website that NGINX is responsible for serving needs to have a seperate server block configured for it. 254 | 255 | Navigate to '/etc/nginx/sites-available' 256 | 257 | ``` 258 | cd /etc/nginx/sites-available 259 | ``` 260 | 261 | There should be a server block called `default` 262 | 263 | ``` 264 | ubuntu@ip-172-31-20-1:/etc/nginx/sites-available$ ls 265 | default 266 | ``` 267 | The default server block is what will be responsible for handling requests that don't match any other server blocks. Right now if you navigate to your server ip, you will see a pretty bland html page that says NGINX is installed. That is the `default` server block in action. 268 | 269 | We will need to configure a new server block for our website. To do that let's create a new file in `/etc/nginx/sites-available/` directory. We can call this file whatever you want, but I recommend that you name it the same name as your domain name for your app. In this example my website will be hosted at *sanjeev.xyz* so I will also name the new file `sanjeev.xyz`. But instead of creating a brand new file, since most of the configs will be fairly similar to the `default` server block, I recommend copying the `default` config. 270 | 271 | ``` 272 | cd /etc/nginx/sites-available 273 | sudo cp default sanjeev.xyz 274 | ``` 275 | 276 | open the new server block file `sanjeev.xyz` and modify it so it matches below: 277 | 278 | ``` 279 | server { 280 | listen 80; 281 | listen [::]:80; 282 | 283 | root /home/ubuntu/apps/yelp-app/client/build; 284 | 285 | # Add index.php to the list if you are using PHP 286 | index index.html index.htm index.nginx-debian.html; 287 | 288 | server_name sanjeev.xyz www.sanjeev.xyz; 289 | 290 | location / { 291 | try_files $uri /index.html; 292 | } 293 | 294 | location /api { 295 | proxy_pass http://localhost:3001; 296 | proxy_http_version 1.1; 297 | proxy_set_header Upgrade $http_upgrade; 298 | proxy_set_header Connection 'upgrade'; 299 | proxy_set_header Host $host; 300 | proxy_cache_bypass $http_upgrade; 301 | } 302 | 303 | } 304 | ``` 305 | 306 | **Let's go over what each line does** 307 | 308 | The first two lines `listen 80` and `listen [::]:80;` tell nginx to listen for traffic on port 80 which is the default port for http traffic. Note that I removed the `default_server` keyword on these lines. If you want this server block to be the default then keep it in 309 | 310 | `root /home/ubuntu/apps/yelp-app/client/build;` tells nginx the path to the index.html file it will server. Here we passed the path to the build directory in our react app code. This directory has the finalized html/js/css files for the frontend. 311 | 312 | `server_name sanjeev.xyz www.sanjeev.xyz;` tells nginx what domain names it should listen for. Make sure to replace this with your specific domains. If you don't have a domain then you can put the ip address of your ubuntu server. 313 | 314 | The configuration block below is needed due to the fact that React is a Singe-Page-App. So if a user directly goes to a url that is not the root url like `https://sanjeev.xyz/restaurants/4` you will get a 404 cause NGINX has not been configured to handle any path ohter than the `/`. This config block tells nginx to redirect everything back to the `/` path so that react can then handle the routing. 315 | 316 | ``` 317 | location / { 318 | try_files $uri /index.html; 319 | } 320 | ``` 321 | 322 | The last section is so that nginx can handle traffic destined to the backend. Notice the location is for `/api`. So any url with a path of `/api` will automatically follow the instructions associated with this config block. The first line in the config block `proxy_pass http://localhost:3001;` tells nginx to redirect it to the localhost on port 3001 which is the port that our backend process is running on. This is how traffic gets forwarded to the Node backend. If you are using a different port, make sure to update that in this line. 323 | 324 | **Enable the new site** 325 | ``` 326 | sudo ln -s /etc/nginx/sites-available/sanjeev.xyz /etc/nginx/sites-enabled/ 327 | systemctl restart nginx 328 | ``` 329 | 330 | ## 8. Configure Environment Variables 331 | We now need to make sure that all of the proper environment variables are setup on our production Ubuntu Server. In our development environment, we made use of a package called dotenv to load up environment variables. In the production environment the environment variables are going to be set on the OS instead of within Node. 332 | 333 | Create a file called `.env` in `/home/ubuntu/`. The file does not need to be named `.env` and it does not need to be stored in `/home/ubuntu`, these were just the name/location of my choosing. The only thing I recommend avoid doing is placing the file in the same directory as the app code as we want to make sure we don't accidentally check our environment variables into git and end up exposing our credentials. 334 | 335 | Within the .env file paste all the required environment variables 336 | 337 | ``` 338 | PORT=3001 339 | PGUSER=postgres 340 | PGHOST=localhost 341 | PGPASSWORD=password123 342 | PGDATABASE=yelp 343 | PGPORT=5432 344 | NODE_ENV=production 345 | ``` 346 | 347 | You'll notice I also set `NODE_ENV=production`. Although its not required for this example project, it is common practice. For man other projects(depending on how the backend is setup) they may require this to be set in a production environment. 348 | 349 | 350 | In the `/home/ubuntu/.profile` add the following line to the bottom of the file 351 | 352 | ``` 353 | set -o allexport; source /home/ubuntu/.env; set +o allexport 354 | ``` 355 | 356 | For these changes to take affect, close the current terminal session and open a new one. 357 | 358 | Verify that the environment variables are set by running the `printenv` 359 | 360 | ``` 361 | # printenv 362 | ``` 363 | 364 | ## 9. Enable Firewall 365 | 366 | ``` 367 | sudo ufw status 368 | sudo ufw allow ssh 369 | sudo ufw allow http 370 | sudo ufw allow https 371 | sudo ufw enable 372 | sudo ufw status 373 | ``` 374 | 375 | ## 10. Enable SSL with Let's Encrypt 376 | Nowadays almost all websites use HTTPS exclusively. Let's use Let's Encrypt to generate SSL certificates and also configure NGINX to use these certificates and redirect http traffic to HTTPS. 377 | 378 | The step by step procedure is listed at: 379 | https://certbot.eff.org/lets-encrypt/ubuntufocal-nginx.html 380 | 381 | 382 | Install Certbot 383 | 384 | ``` 385 | sudo snap install --classic certbot 386 | ``` 387 | 388 | Prepare the Certbot command 389 | 390 | ``` 391 | sudo ln -s /snap/bin/certbot /usr/bin/certbot 392 | ``` 393 | 394 | Get and install certificates using interactive prompt 395 | 396 | ``` 397 | sudo certbot --nginx 398 | ``` 399 | 400 | ## Authors 401 | * **Sanjeev Thiyagarajan** - *CEO of Nothing* 402 | 403 | -------------------------------------------------------------------------------- /server/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@sindresorhus/is": { 8 | "version": "0.14.0", 9 | "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", 10 | "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==" 11 | }, 12 | "@szmarczak/http-timer": { 13 | "version": "1.1.2", 14 | "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", 15 | "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", 16 | "requires": { 17 | "defer-to-connect": "^1.0.1" 18 | } 19 | }, 20 | "@types/color-name": { 21 | "version": "1.1.1", 22 | "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", 23 | "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==" 24 | }, 25 | "abbrev": { 26 | "version": "1.1.1", 27 | "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", 28 | "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" 29 | }, 30 | "accepts": { 31 | "version": "1.3.7", 32 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", 33 | "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", 34 | "requires": { 35 | "mime-types": "~2.1.24", 36 | "negotiator": "0.6.2" 37 | } 38 | }, 39 | "ansi-align": { 40 | "version": "3.0.0", 41 | "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz", 42 | "integrity": "sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw==", 43 | "requires": { 44 | "string-width": "^3.0.0" 45 | }, 46 | "dependencies": { 47 | "string-width": { 48 | "version": "3.1.0", 49 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", 50 | "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", 51 | "requires": { 52 | "emoji-regex": "^7.0.1", 53 | "is-fullwidth-code-point": "^2.0.0", 54 | "strip-ansi": "^5.1.0" 55 | } 56 | } 57 | } 58 | }, 59 | "ansi-regex": { 60 | "version": "4.1.0", 61 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", 62 | "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" 63 | }, 64 | "ansi-styles": { 65 | "version": "4.2.1", 66 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", 67 | "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", 68 | "requires": { 69 | "@types/color-name": "^1.1.1", 70 | "color-convert": "^2.0.1" 71 | } 72 | }, 73 | "anymatch": { 74 | "version": "3.1.1", 75 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", 76 | "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", 77 | "requires": { 78 | "normalize-path": "^3.0.0", 79 | "picomatch": "^2.0.4" 80 | } 81 | }, 82 | "array-flatten": { 83 | "version": "1.1.1", 84 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 85 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" 86 | }, 87 | "balanced-match": { 88 | "version": "1.0.0", 89 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 90 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" 91 | }, 92 | "basic-auth": { 93 | "version": "2.0.1", 94 | "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", 95 | "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", 96 | "requires": { 97 | "safe-buffer": "5.1.2" 98 | } 99 | }, 100 | "binary-extensions": { 101 | "version": "2.0.0", 102 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz", 103 | "integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==" 104 | }, 105 | "body-parser": { 106 | "version": "1.19.0", 107 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", 108 | "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", 109 | "requires": { 110 | "bytes": "3.1.0", 111 | "content-type": "~1.0.4", 112 | "debug": "2.6.9", 113 | "depd": "~1.1.2", 114 | "http-errors": "1.7.2", 115 | "iconv-lite": "0.4.24", 116 | "on-finished": "~2.3.0", 117 | "qs": "6.7.0", 118 | "raw-body": "2.4.0", 119 | "type-is": "~1.6.17" 120 | } 121 | }, 122 | "boxen": { 123 | "version": "4.2.0", 124 | "resolved": "https://registry.npmjs.org/boxen/-/boxen-4.2.0.tgz", 125 | "integrity": "sha512-eB4uT9RGzg2odpER62bBwSLvUeGC+WbRjjyyFhGsKnc8wp/m0+hQsMUvUe3H2V0D5vw0nBdO1hCJoZo5mKeuIQ==", 126 | "requires": { 127 | "ansi-align": "^3.0.0", 128 | "camelcase": "^5.3.1", 129 | "chalk": "^3.0.0", 130 | "cli-boxes": "^2.2.0", 131 | "string-width": "^4.1.0", 132 | "term-size": "^2.1.0", 133 | "type-fest": "^0.8.1", 134 | "widest-line": "^3.1.0" 135 | } 136 | }, 137 | "brace-expansion": { 138 | "version": "1.1.11", 139 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 140 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 141 | "requires": { 142 | "balanced-match": "^1.0.0", 143 | "concat-map": "0.0.1" 144 | } 145 | }, 146 | "braces": { 147 | "version": "3.0.2", 148 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", 149 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", 150 | "requires": { 151 | "fill-range": "^7.0.1" 152 | } 153 | }, 154 | "buffer-writer": { 155 | "version": "2.0.0", 156 | "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", 157 | "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==" 158 | }, 159 | "bytes": { 160 | "version": "3.1.0", 161 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", 162 | "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" 163 | }, 164 | "cacheable-request": { 165 | "version": "6.1.0", 166 | "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", 167 | "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", 168 | "requires": { 169 | "clone-response": "^1.0.2", 170 | "get-stream": "^5.1.0", 171 | "http-cache-semantics": "^4.0.0", 172 | "keyv": "^3.0.0", 173 | "lowercase-keys": "^2.0.0", 174 | "normalize-url": "^4.1.0", 175 | "responselike": "^1.0.2" 176 | }, 177 | "dependencies": { 178 | "get-stream": { 179 | "version": "5.1.0", 180 | "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", 181 | "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", 182 | "requires": { 183 | "pump": "^3.0.0" 184 | } 185 | }, 186 | "lowercase-keys": { 187 | "version": "2.0.0", 188 | "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", 189 | "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==" 190 | } 191 | } 192 | }, 193 | "camelcase": { 194 | "version": "5.3.1", 195 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", 196 | "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" 197 | }, 198 | "chalk": { 199 | "version": "3.0.0", 200 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", 201 | "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", 202 | "requires": { 203 | "ansi-styles": "^4.1.0", 204 | "supports-color": "^7.1.0" 205 | }, 206 | "dependencies": { 207 | "has-flag": { 208 | "version": "4.0.0", 209 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 210 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" 211 | }, 212 | "supports-color": { 213 | "version": "7.1.0", 214 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", 215 | "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", 216 | "requires": { 217 | "has-flag": "^4.0.0" 218 | } 219 | } 220 | } 221 | }, 222 | "chokidar": { 223 | "version": "3.4.0", 224 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.0.tgz", 225 | "integrity": "sha512-aXAaho2VJtisB/1fg1+3nlLJqGOuewTzQpd/Tz0yTg2R0e4IGtshYvtjowyEumcBv2z+y4+kc75Mz7j5xJskcQ==", 226 | "requires": { 227 | "anymatch": "~3.1.1", 228 | "braces": "~3.0.2", 229 | "fsevents": "~2.1.2", 230 | "glob-parent": "~5.1.0", 231 | "is-binary-path": "~2.1.0", 232 | "is-glob": "~4.0.1", 233 | "normalize-path": "~3.0.0", 234 | "readdirp": "~3.4.0" 235 | } 236 | }, 237 | "ci-info": { 238 | "version": "2.0.0", 239 | "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", 240 | "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==" 241 | }, 242 | "cli-boxes": { 243 | "version": "2.2.0", 244 | "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.0.tgz", 245 | "integrity": "sha512-gpaBrMAizVEANOpfZp/EEUixTXDyGt7DFzdK5hU+UbWt/J0lB0w20ncZj59Z9a93xHb9u12zF5BS6i9RKbtg4w==" 246 | }, 247 | "clone-response": { 248 | "version": "1.0.2", 249 | "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", 250 | "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", 251 | "requires": { 252 | "mimic-response": "^1.0.0" 253 | } 254 | }, 255 | "color-convert": { 256 | "version": "2.0.1", 257 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 258 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 259 | "requires": { 260 | "color-name": "~1.1.4" 261 | } 262 | }, 263 | "color-name": { 264 | "version": "1.1.4", 265 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 266 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" 267 | }, 268 | "concat-map": { 269 | "version": "0.0.1", 270 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 271 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" 272 | }, 273 | "configstore": { 274 | "version": "5.0.1", 275 | "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", 276 | "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", 277 | "requires": { 278 | "dot-prop": "^5.2.0", 279 | "graceful-fs": "^4.1.2", 280 | "make-dir": "^3.0.0", 281 | "unique-string": "^2.0.0", 282 | "write-file-atomic": "^3.0.0", 283 | "xdg-basedir": "^4.0.0" 284 | } 285 | }, 286 | "content-disposition": { 287 | "version": "0.5.3", 288 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", 289 | "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", 290 | "requires": { 291 | "safe-buffer": "5.1.2" 292 | } 293 | }, 294 | "content-type": { 295 | "version": "1.0.4", 296 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", 297 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" 298 | }, 299 | "cookie": { 300 | "version": "0.4.0", 301 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", 302 | "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" 303 | }, 304 | "cookie-signature": { 305 | "version": "1.0.6", 306 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 307 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" 308 | }, 309 | "cors": { 310 | "version": "2.8.5", 311 | "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", 312 | "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", 313 | "requires": { 314 | "object-assign": "^4", 315 | "vary": "^1" 316 | } 317 | }, 318 | "crypto-random-string": { 319 | "version": "2.0.0", 320 | "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", 321 | "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==" 322 | }, 323 | "debug": { 324 | "version": "2.6.9", 325 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 326 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 327 | "requires": { 328 | "ms": "2.0.0" 329 | } 330 | }, 331 | "decompress-response": { 332 | "version": "3.3.0", 333 | "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", 334 | "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", 335 | "requires": { 336 | "mimic-response": "^1.0.0" 337 | } 338 | }, 339 | "deep-extend": { 340 | "version": "0.6.0", 341 | "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", 342 | "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" 343 | }, 344 | "defer-to-connect": { 345 | "version": "1.1.3", 346 | "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", 347 | "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==" 348 | }, 349 | "depd": { 350 | "version": "1.1.2", 351 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", 352 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" 353 | }, 354 | "destroy": { 355 | "version": "1.0.4", 356 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", 357 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" 358 | }, 359 | "dot-prop": { 360 | "version": "5.2.0", 361 | "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.2.0.tgz", 362 | "integrity": "sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A==", 363 | "requires": { 364 | "is-obj": "^2.0.0" 365 | } 366 | }, 367 | "dotenv": { 368 | "version": "8.2.0", 369 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz", 370 | "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==" 371 | }, 372 | "duplexer3": { 373 | "version": "0.1.4", 374 | "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", 375 | "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=" 376 | }, 377 | "ee-first": { 378 | "version": "1.1.1", 379 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 380 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" 381 | }, 382 | "emoji-regex": { 383 | "version": "7.0.3", 384 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", 385 | "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" 386 | }, 387 | "encodeurl": { 388 | "version": "1.0.2", 389 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 390 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" 391 | }, 392 | "end-of-stream": { 393 | "version": "1.4.4", 394 | "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", 395 | "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", 396 | "requires": { 397 | "once": "^1.4.0" 398 | } 399 | }, 400 | "escape-goat": { 401 | "version": "2.1.1", 402 | "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz", 403 | "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==" 404 | }, 405 | "escape-html": { 406 | "version": "1.0.3", 407 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 408 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" 409 | }, 410 | "etag": { 411 | "version": "1.8.1", 412 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 413 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" 414 | }, 415 | "express": { 416 | "version": "4.17.1", 417 | "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", 418 | "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", 419 | "requires": { 420 | "accepts": "~1.3.7", 421 | "array-flatten": "1.1.1", 422 | "body-parser": "1.19.0", 423 | "content-disposition": "0.5.3", 424 | "content-type": "~1.0.4", 425 | "cookie": "0.4.0", 426 | "cookie-signature": "1.0.6", 427 | "debug": "2.6.9", 428 | "depd": "~1.1.2", 429 | "encodeurl": "~1.0.2", 430 | "escape-html": "~1.0.3", 431 | "etag": "~1.8.1", 432 | "finalhandler": "~1.1.2", 433 | "fresh": "0.5.2", 434 | "merge-descriptors": "1.0.1", 435 | "methods": "~1.1.2", 436 | "on-finished": "~2.3.0", 437 | "parseurl": "~1.3.3", 438 | "path-to-regexp": "0.1.7", 439 | "proxy-addr": "~2.0.5", 440 | "qs": "6.7.0", 441 | "range-parser": "~1.2.1", 442 | "safe-buffer": "5.1.2", 443 | "send": "0.17.1", 444 | "serve-static": "1.14.1", 445 | "setprototypeof": "1.1.1", 446 | "statuses": "~1.5.0", 447 | "type-is": "~1.6.18", 448 | "utils-merge": "1.0.1", 449 | "vary": "~1.1.2" 450 | } 451 | }, 452 | "fill-range": { 453 | "version": "7.0.1", 454 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", 455 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", 456 | "requires": { 457 | "to-regex-range": "^5.0.1" 458 | } 459 | }, 460 | "finalhandler": { 461 | "version": "1.1.2", 462 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", 463 | "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", 464 | "requires": { 465 | "debug": "2.6.9", 466 | "encodeurl": "~1.0.2", 467 | "escape-html": "~1.0.3", 468 | "on-finished": "~2.3.0", 469 | "parseurl": "~1.3.3", 470 | "statuses": "~1.5.0", 471 | "unpipe": "~1.0.0" 472 | } 473 | }, 474 | "forwarded": { 475 | "version": "0.1.2", 476 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", 477 | "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" 478 | }, 479 | "fresh": { 480 | "version": "0.5.2", 481 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 482 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" 483 | }, 484 | "fsevents": { 485 | "version": "2.1.3", 486 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", 487 | "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", 488 | "optional": true 489 | }, 490 | "get-stream": { 491 | "version": "4.1.0", 492 | "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", 493 | "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", 494 | "requires": { 495 | "pump": "^3.0.0" 496 | } 497 | }, 498 | "glob-parent": { 499 | "version": "5.1.1", 500 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", 501 | "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", 502 | "requires": { 503 | "is-glob": "^4.0.1" 504 | } 505 | }, 506 | "global-dirs": { 507 | "version": "2.0.1", 508 | "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-2.0.1.tgz", 509 | "integrity": "sha512-5HqUqdhkEovj2Of/ms3IeS/EekcO54ytHRLV4PEY2rhRwrHXLQjeVEES0Lhka0xwNDtGYn58wyC4s5+MHsOO6A==", 510 | "requires": { 511 | "ini": "^1.3.5" 512 | } 513 | }, 514 | "got": { 515 | "version": "9.6.0", 516 | "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", 517 | "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", 518 | "requires": { 519 | "@sindresorhus/is": "^0.14.0", 520 | "@szmarczak/http-timer": "^1.1.2", 521 | "cacheable-request": "^6.0.0", 522 | "decompress-response": "^3.3.0", 523 | "duplexer3": "^0.1.4", 524 | "get-stream": "^4.1.0", 525 | "lowercase-keys": "^1.0.1", 526 | "mimic-response": "^1.0.1", 527 | "p-cancelable": "^1.0.0", 528 | "to-readable-stream": "^1.0.0", 529 | "url-parse-lax": "^3.0.0" 530 | } 531 | }, 532 | "graceful-fs": { 533 | "version": "4.2.4", 534 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", 535 | "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" 536 | }, 537 | "has-flag": { 538 | "version": "3.0.0", 539 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 540 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" 541 | }, 542 | "has-yarn": { 543 | "version": "2.1.0", 544 | "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", 545 | "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==" 546 | }, 547 | "http-cache-semantics": { 548 | "version": "4.1.0", 549 | "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", 550 | "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==" 551 | }, 552 | "http-errors": { 553 | "version": "1.7.2", 554 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", 555 | "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", 556 | "requires": { 557 | "depd": "~1.1.2", 558 | "inherits": "2.0.3", 559 | "setprototypeof": "1.1.1", 560 | "statuses": ">= 1.5.0 < 2", 561 | "toidentifier": "1.0.0" 562 | } 563 | }, 564 | "iconv-lite": { 565 | "version": "0.4.24", 566 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 567 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 568 | "requires": { 569 | "safer-buffer": ">= 2.1.2 < 3" 570 | } 571 | }, 572 | "ignore-by-default": { 573 | "version": "1.0.1", 574 | "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", 575 | "integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk=" 576 | }, 577 | "import-lazy": { 578 | "version": "2.1.0", 579 | "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", 580 | "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=" 581 | }, 582 | "imurmurhash": { 583 | "version": "0.1.4", 584 | "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", 585 | "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" 586 | }, 587 | "inherits": { 588 | "version": "2.0.3", 589 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 590 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 591 | }, 592 | "ini": { 593 | "version": "1.3.5", 594 | "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", 595 | "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" 596 | }, 597 | "ipaddr.js": { 598 | "version": "1.9.1", 599 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", 600 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" 601 | }, 602 | "is-binary-path": { 603 | "version": "2.1.0", 604 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", 605 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", 606 | "requires": { 607 | "binary-extensions": "^2.0.0" 608 | } 609 | }, 610 | "is-ci": { 611 | "version": "2.0.0", 612 | "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", 613 | "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", 614 | "requires": { 615 | "ci-info": "^2.0.0" 616 | } 617 | }, 618 | "is-extglob": { 619 | "version": "2.1.1", 620 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 621 | "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" 622 | }, 623 | "is-fullwidth-code-point": { 624 | "version": "2.0.0", 625 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", 626 | "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" 627 | }, 628 | "is-glob": { 629 | "version": "4.0.1", 630 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", 631 | "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", 632 | "requires": { 633 | "is-extglob": "^2.1.1" 634 | } 635 | }, 636 | "is-installed-globally": { 637 | "version": "0.3.2", 638 | "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.3.2.tgz", 639 | "integrity": "sha512-wZ8x1js7Ia0kecP/CHM/3ABkAmujX7WPvQk6uu3Fly/Mk44pySulQpnHG46OMjHGXApINnV4QhY3SWnECO2z5g==", 640 | "requires": { 641 | "global-dirs": "^2.0.1", 642 | "is-path-inside": "^3.0.1" 643 | } 644 | }, 645 | "is-npm": { 646 | "version": "4.0.0", 647 | "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-4.0.0.tgz", 648 | "integrity": "sha512-96ECIfh9xtDDlPylNPXhzjsykHsMJZ18ASpaWzQyBr4YRTcVjUvzaHayDAES2oU/3KpljhHUjtSRNiDwi0F0ig==" 649 | }, 650 | "is-number": { 651 | "version": "7.0.0", 652 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 653 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" 654 | }, 655 | "is-obj": { 656 | "version": "2.0.0", 657 | "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", 658 | "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==" 659 | }, 660 | "is-path-inside": { 661 | "version": "3.0.2", 662 | "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.2.tgz", 663 | "integrity": "sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg==" 664 | }, 665 | "is-typedarray": { 666 | "version": "1.0.0", 667 | "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", 668 | "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" 669 | }, 670 | "is-yarn-global": { 671 | "version": "0.3.0", 672 | "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz", 673 | "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==" 674 | }, 675 | "json-buffer": { 676 | "version": "3.0.0", 677 | "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", 678 | "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=" 679 | }, 680 | "keyv": { 681 | "version": "3.1.0", 682 | "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", 683 | "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", 684 | "requires": { 685 | "json-buffer": "3.0.0" 686 | } 687 | }, 688 | "latest-version": { 689 | "version": "5.1.0", 690 | "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz", 691 | "integrity": "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==", 692 | "requires": { 693 | "package-json": "^6.3.0" 694 | } 695 | }, 696 | "lowercase-keys": { 697 | "version": "1.0.1", 698 | "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", 699 | "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==" 700 | }, 701 | "make-dir": { 702 | "version": "3.1.0", 703 | "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", 704 | "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", 705 | "requires": { 706 | "semver": "^6.0.0" 707 | }, 708 | "dependencies": { 709 | "semver": { 710 | "version": "6.3.0", 711 | "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", 712 | "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" 713 | } 714 | } 715 | }, 716 | "media-typer": { 717 | "version": "0.3.0", 718 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 719 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" 720 | }, 721 | "merge-descriptors": { 722 | "version": "1.0.1", 723 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 724 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" 725 | }, 726 | "methods": { 727 | "version": "1.1.2", 728 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 729 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" 730 | }, 731 | "mime": { 732 | "version": "1.6.0", 733 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", 734 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" 735 | }, 736 | "mime-db": { 737 | "version": "1.44.0", 738 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", 739 | "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" 740 | }, 741 | "mime-types": { 742 | "version": "2.1.27", 743 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", 744 | "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", 745 | "requires": { 746 | "mime-db": "1.44.0" 747 | } 748 | }, 749 | "mimic-response": { 750 | "version": "1.0.1", 751 | "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", 752 | "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==" 753 | }, 754 | "minimatch": { 755 | "version": "3.0.4", 756 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 757 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 758 | "requires": { 759 | "brace-expansion": "^1.1.7" 760 | } 761 | }, 762 | "minimist": { 763 | "version": "1.2.5", 764 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", 765 | "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" 766 | }, 767 | "morgan": { 768 | "version": "1.10.0", 769 | "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", 770 | "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==", 771 | "requires": { 772 | "basic-auth": "~2.0.1", 773 | "debug": "2.6.9", 774 | "depd": "~2.0.0", 775 | "on-finished": "~2.3.0", 776 | "on-headers": "~1.0.2" 777 | }, 778 | "dependencies": { 779 | "depd": { 780 | "version": "2.0.0", 781 | "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", 782 | "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" 783 | } 784 | } 785 | }, 786 | "ms": { 787 | "version": "2.0.0", 788 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 789 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 790 | }, 791 | "negotiator": { 792 | "version": "0.6.2", 793 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", 794 | "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" 795 | }, 796 | "nodemon": { 797 | "version": "2.0.4", 798 | "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.4.tgz", 799 | "integrity": "sha512-Ltced+hIfTmaS28Zjv1BM552oQ3dbwPqI4+zI0SLgq+wpJhSyqgYude/aZa/3i31VCQWMfXJVxvu86abcam3uQ==", 800 | "requires": { 801 | "chokidar": "^3.2.2", 802 | "debug": "^3.2.6", 803 | "ignore-by-default": "^1.0.1", 804 | "minimatch": "^3.0.4", 805 | "pstree.remy": "^1.1.7", 806 | "semver": "^5.7.1", 807 | "supports-color": "^5.5.0", 808 | "touch": "^3.1.0", 809 | "undefsafe": "^2.0.2", 810 | "update-notifier": "^4.0.0" 811 | }, 812 | "dependencies": { 813 | "debug": { 814 | "version": "3.2.6", 815 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", 816 | "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", 817 | "requires": { 818 | "ms": "^2.1.1" 819 | } 820 | }, 821 | "ms": { 822 | "version": "2.1.2", 823 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 824 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 825 | } 826 | } 827 | }, 828 | "nopt": { 829 | "version": "1.0.10", 830 | "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", 831 | "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=", 832 | "requires": { 833 | "abbrev": "1" 834 | } 835 | }, 836 | "normalize-path": { 837 | "version": "3.0.0", 838 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 839 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" 840 | }, 841 | "normalize-url": { 842 | "version": "4.5.0", 843 | "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", 844 | "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==" 845 | }, 846 | "object-assign": { 847 | "version": "4.1.1", 848 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 849 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" 850 | }, 851 | "on-finished": { 852 | "version": "2.3.0", 853 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", 854 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", 855 | "requires": { 856 | "ee-first": "1.1.1" 857 | } 858 | }, 859 | "on-headers": { 860 | "version": "1.0.2", 861 | "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", 862 | "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" 863 | }, 864 | "once": { 865 | "version": "1.4.0", 866 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 867 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 868 | "requires": { 869 | "wrappy": "1" 870 | } 871 | }, 872 | "p-cancelable": { 873 | "version": "1.1.0", 874 | "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", 875 | "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==" 876 | }, 877 | "package-json": { 878 | "version": "6.5.0", 879 | "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz", 880 | "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==", 881 | "requires": { 882 | "got": "^9.6.0", 883 | "registry-auth-token": "^4.0.0", 884 | "registry-url": "^5.0.0", 885 | "semver": "^6.2.0" 886 | }, 887 | "dependencies": { 888 | "semver": { 889 | "version": "6.3.0", 890 | "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", 891 | "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" 892 | } 893 | } 894 | }, 895 | "packet-reader": { 896 | "version": "1.0.0", 897 | "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", 898 | "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==" 899 | }, 900 | "parseurl": { 901 | "version": "1.3.3", 902 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", 903 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" 904 | }, 905 | "path-to-regexp": { 906 | "version": "0.1.7", 907 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 908 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" 909 | }, 910 | "pg": { 911 | "version": "8.2.1", 912 | "resolved": "https://registry.npmjs.org/pg/-/pg-8.2.1.tgz", 913 | "integrity": "sha512-DKzffhpkWRr9jx7vKxA+ur79KG+SKw+PdjMb1IRhMiKI9zqYUGczwFprqy+5Veh/DCcFs1Y6V8lRLN5I1DlleQ==", 914 | "requires": { 915 | "buffer-writer": "2.0.0", 916 | "packet-reader": "1.0.0", 917 | "pg-connection-string": "^2.2.3", 918 | "pg-pool": "^3.2.1", 919 | "pg-protocol": "^1.2.4", 920 | "pg-types": "^2.1.0", 921 | "pgpass": "1.x", 922 | "semver": "4.3.2" 923 | }, 924 | "dependencies": { 925 | "semver": { 926 | "version": "4.3.2", 927 | "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.2.tgz", 928 | "integrity": "sha1-x6BxWKgL7dBSNVt3DYLWZA+AO+c=" 929 | } 930 | } 931 | }, 932 | "pg-connection-string": { 933 | "version": "2.2.3", 934 | "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.2.3.tgz", 935 | "integrity": "sha512-I/KCSQGmOrZx6sMHXkOs2MjddrYcqpza3Dtsy0AjIgBr/bZiPJRK9WhABXN1Uy1UDazRbi9gZEzO2sAhL5EqiQ==" 936 | }, 937 | "pg-int8": { 938 | "version": "1.0.1", 939 | "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", 940 | "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==" 941 | }, 942 | "pg-pool": { 943 | "version": "3.2.1", 944 | "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.2.1.tgz", 945 | "integrity": "sha512-BQDPWUeKenVrMMDN9opfns/kZo4lxmSWhIqo+cSAF7+lfi9ZclQbr9vfnlNaPr8wYF3UYjm5X0yPAhbcgqNOdA==" 946 | }, 947 | "pg-protocol": { 948 | "version": "1.2.4", 949 | "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.2.4.tgz", 950 | "integrity": "sha512-/8L/G+vW/VhWjTGXpGh8XVkXOFx1ZDY+Yuz//Ab8CfjInzFkreI+fDG3WjCeSra7fIZwAFxzbGptNbm8xSXenw==" 951 | }, 952 | "pg-types": { 953 | "version": "2.2.0", 954 | "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", 955 | "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", 956 | "requires": { 957 | "pg-int8": "1.0.1", 958 | "postgres-array": "~2.0.0", 959 | "postgres-bytea": "~1.0.0", 960 | "postgres-date": "~1.0.4", 961 | "postgres-interval": "^1.1.0" 962 | } 963 | }, 964 | "pgpass": { 965 | "version": "1.0.2", 966 | "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.2.tgz", 967 | "integrity": "sha1-Knu0G2BltnkH6R2hsHwYR8h3swY=", 968 | "requires": { 969 | "split": "^1.0.0" 970 | } 971 | }, 972 | "picomatch": { 973 | "version": "2.2.2", 974 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", 975 | "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==" 976 | }, 977 | "postgres-array": { 978 | "version": "2.0.0", 979 | "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", 980 | "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==" 981 | }, 982 | "postgres-bytea": { 983 | "version": "1.0.0", 984 | "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", 985 | "integrity": "sha1-AntTPAqokOJtFy1Hz5zOzFIazTU=" 986 | }, 987 | "postgres-date": { 988 | "version": "1.0.5", 989 | "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.5.tgz", 990 | "integrity": "sha512-pdau6GRPERdAYUQwkBnGKxEfPyhVZXG/JiS44iZWiNdSOWE09N2lUgN6yshuq6fVSon4Pm0VMXd1srUUkLe9iA==" 991 | }, 992 | "postgres-interval": { 993 | "version": "1.2.0", 994 | "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", 995 | "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", 996 | "requires": { 997 | "xtend": "^4.0.0" 998 | } 999 | }, 1000 | "prepend-http": { 1001 | "version": "2.0.0", 1002 | "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", 1003 | "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=" 1004 | }, 1005 | "proxy-addr": { 1006 | "version": "2.0.6", 1007 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", 1008 | "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", 1009 | "requires": { 1010 | "forwarded": "~0.1.2", 1011 | "ipaddr.js": "1.9.1" 1012 | } 1013 | }, 1014 | "pstree.remy": { 1015 | "version": "1.1.8", 1016 | "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", 1017 | "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==" 1018 | }, 1019 | "pump": { 1020 | "version": "3.0.0", 1021 | "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", 1022 | "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", 1023 | "requires": { 1024 | "end-of-stream": "^1.1.0", 1025 | "once": "^1.3.1" 1026 | } 1027 | }, 1028 | "pupa": { 1029 | "version": "2.0.1", 1030 | "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.0.1.tgz", 1031 | "integrity": "sha512-hEJH0s8PXLY/cdXh66tNEQGndDrIKNqNC5xmrysZy3i5C3oEoLna7YAOad+7u125+zH1HNXUmGEkrhb3c2VriA==", 1032 | "requires": { 1033 | "escape-goat": "^2.0.0" 1034 | } 1035 | }, 1036 | "qs": { 1037 | "version": "6.7.0", 1038 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", 1039 | "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" 1040 | }, 1041 | "range-parser": { 1042 | "version": "1.2.1", 1043 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", 1044 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" 1045 | }, 1046 | "raw-body": { 1047 | "version": "2.4.0", 1048 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", 1049 | "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", 1050 | "requires": { 1051 | "bytes": "3.1.0", 1052 | "http-errors": "1.7.2", 1053 | "iconv-lite": "0.4.24", 1054 | "unpipe": "1.0.0" 1055 | } 1056 | }, 1057 | "rc": { 1058 | "version": "1.2.8", 1059 | "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", 1060 | "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", 1061 | "requires": { 1062 | "deep-extend": "^0.6.0", 1063 | "ini": "~1.3.0", 1064 | "minimist": "^1.2.0", 1065 | "strip-json-comments": "~2.0.1" 1066 | } 1067 | }, 1068 | "readdirp": { 1069 | "version": "3.4.0", 1070 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz", 1071 | "integrity": "sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==", 1072 | "requires": { 1073 | "picomatch": "^2.2.1" 1074 | } 1075 | }, 1076 | "registry-auth-token": { 1077 | "version": "4.1.1", 1078 | "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.1.1.tgz", 1079 | "integrity": "sha512-9bKS7nTl9+/A1s7tnPeGrUpRcVY+LUh7bfFgzpndALdPfXQBfQV77rQVtqgUV3ti4vc/Ik81Ex8UJDWDQ12zQA==", 1080 | "requires": { 1081 | "rc": "^1.2.8" 1082 | } 1083 | }, 1084 | "registry-url": { 1085 | "version": "5.1.0", 1086 | "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz", 1087 | "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==", 1088 | "requires": { 1089 | "rc": "^1.2.8" 1090 | } 1091 | }, 1092 | "responselike": { 1093 | "version": "1.0.2", 1094 | "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", 1095 | "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", 1096 | "requires": { 1097 | "lowercase-keys": "^1.0.0" 1098 | } 1099 | }, 1100 | "safe-buffer": { 1101 | "version": "5.1.2", 1102 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 1103 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 1104 | }, 1105 | "safer-buffer": { 1106 | "version": "2.1.2", 1107 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 1108 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 1109 | }, 1110 | "semver": { 1111 | "version": "5.7.1", 1112 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", 1113 | "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" 1114 | }, 1115 | "semver-diff": { 1116 | "version": "3.1.1", 1117 | "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz", 1118 | "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==", 1119 | "requires": { 1120 | "semver": "^6.3.0" 1121 | }, 1122 | "dependencies": { 1123 | "semver": { 1124 | "version": "6.3.0", 1125 | "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", 1126 | "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" 1127 | } 1128 | } 1129 | }, 1130 | "send": { 1131 | "version": "0.17.1", 1132 | "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", 1133 | "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", 1134 | "requires": { 1135 | "debug": "2.6.9", 1136 | "depd": "~1.1.2", 1137 | "destroy": "~1.0.4", 1138 | "encodeurl": "~1.0.2", 1139 | "escape-html": "~1.0.3", 1140 | "etag": "~1.8.1", 1141 | "fresh": "0.5.2", 1142 | "http-errors": "~1.7.2", 1143 | "mime": "1.6.0", 1144 | "ms": "2.1.1", 1145 | "on-finished": "~2.3.0", 1146 | "range-parser": "~1.2.1", 1147 | "statuses": "~1.5.0" 1148 | }, 1149 | "dependencies": { 1150 | "ms": { 1151 | "version": "2.1.1", 1152 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", 1153 | "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" 1154 | } 1155 | } 1156 | }, 1157 | "serve-static": { 1158 | "version": "1.14.1", 1159 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", 1160 | "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", 1161 | "requires": { 1162 | "encodeurl": "~1.0.2", 1163 | "escape-html": "~1.0.3", 1164 | "parseurl": "~1.3.3", 1165 | "send": "0.17.1" 1166 | } 1167 | }, 1168 | "setprototypeof": { 1169 | "version": "1.1.1", 1170 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", 1171 | "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" 1172 | }, 1173 | "signal-exit": { 1174 | "version": "3.0.3", 1175 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", 1176 | "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" 1177 | }, 1178 | "split": { 1179 | "version": "1.0.1", 1180 | "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", 1181 | "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", 1182 | "requires": { 1183 | "through": "2" 1184 | } 1185 | }, 1186 | "statuses": { 1187 | "version": "1.5.0", 1188 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", 1189 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" 1190 | }, 1191 | "string-width": { 1192 | "version": "4.2.0", 1193 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", 1194 | "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", 1195 | "requires": { 1196 | "emoji-regex": "^8.0.0", 1197 | "is-fullwidth-code-point": "^3.0.0", 1198 | "strip-ansi": "^6.0.0" 1199 | }, 1200 | "dependencies": { 1201 | "ansi-regex": { 1202 | "version": "5.0.0", 1203 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", 1204 | "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" 1205 | }, 1206 | "emoji-regex": { 1207 | "version": "8.0.0", 1208 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 1209 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" 1210 | }, 1211 | "is-fullwidth-code-point": { 1212 | "version": "3.0.0", 1213 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 1214 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" 1215 | }, 1216 | "strip-ansi": { 1217 | "version": "6.0.0", 1218 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", 1219 | "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", 1220 | "requires": { 1221 | "ansi-regex": "^5.0.0" 1222 | } 1223 | } 1224 | } 1225 | }, 1226 | "strip-ansi": { 1227 | "version": "5.2.0", 1228 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", 1229 | "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", 1230 | "requires": { 1231 | "ansi-regex": "^4.1.0" 1232 | } 1233 | }, 1234 | "strip-json-comments": { 1235 | "version": "2.0.1", 1236 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", 1237 | "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" 1238 | }, 1239 | "supports-color": { 1240 | "version": "5.5.0", 1241 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 1242 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 1243 | "requires": { 1244 | "has-flag": "^3.0.0" 1245 | } 1246 | }, 1247 | "term-size": { 1248 | "version": "2.2.0", 1249 | "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.0.tgz", 1250 | "integrity": "sha512-a6sumDlzyHVJWb8+YofY4TW112G6p2FCPEAFk+59gIYHv3XHRhm9ltVQ9kli4hNWeQBwSpe8cRN25x0ROunMOw==" 1251 | }, 1252 | "through": { 1253 | "version": "2.3.8", 1254 | "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", 1255 | "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" 1256 | }, 1257 | "to-readable-stream": { 1258 | "version": "1.0.0", 1259 | "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", 1260 | "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==" 1261 | }, 1262 | "to-regex-range": { 1263 | "version": "5.0.1", 1264 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 1265 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 1266 | "requires": { 1267 | "is-number": "^7.0.0" 1268 | } 1269 | }, 1270 | "toidentifier": { 1271 | "version": "1.0.0", 1272 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", 1273 | "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" 1274 | }, 1275 | "touch": { 1276 | "version": "3.1.0", 1277 | "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", 1278 | "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", 1279 | "requires": { 1280 | "nopt": "~1.0.10" 1281 | } 1282 | }, 1283 | "type-fest": { 1284 | "version": "0.8.1", 1285 | "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", 1286 | "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==" 1287 | }, 1288 | "type-is": { 1289 | "version": "1.6.18", 1290 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", 1291 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", 1292 | "requires": { 1293 | "media-typer": "0.3.0", 1294 | "mime-types": "~2.1.24" 1295 | } 1296 | }, 1297 | "typedarray-to-buffer": { 1298 | "version": "3.1.5", 1299 | "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", 1300 | "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", 1301 | "requires": { 1302 | "is-typedarray": "^1.0.0" 1303 | } 1304 | }, 1305 | "undefsafe": { 1306 | "version": "2.0.3", 1307 | "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.3.tgz", 1308 | "integrity": "sha512-nrXZwwXrD/T/JXeygJqdCO6NZZ1L66HrxM/Z7mIq2oPanoN0F1nLx3lwJMu6AwJY69hdixaFQOuoYsMjE5/C2A==", 1309 | "requires": { 1310 | "debug": "^2.2.0" 1311 | } 1312 | }, 1313 | "unique-string": { 1314 | "version": "2.0.0", 1315 | "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", 1316 | "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", 1317 | "requires": { 1318 | "crypto-random-string": "^2.0.0" 1319 | } 1320 | }, 1321 | "unpipe": { 1322 | "version": "1.0.0", 1323 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 1324 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" 1325 | }, 1326 | "update-notifier": { 1327 | "version": "4.1.0", 1328 | "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-4.1.0.tgz", 1329 | "integrity": "sha512-w3doE1qtI0/ZmgeoDoARmI5fjDoT93IfKgEGqm26dGUOh8oNpaSTsGNdYRN/SjOuo10jcJGwkEL3mroKzktkew==", 1330 | "requires": { 1331 | "boxen": "^4.2.0", 1332 | "chalk": "^3.0.0", 1333 | "configstore": "^5.0.1", 1334 | "has-yarn": "^2.1.0", 1335 | "import-lazy": "^2.1.0", 1336 | "is-ci": "^2.0.0", 1337 | "is-installed-globally": "^0.3.1", 1338 | "is-npm": "^4.0.0", 1339 | "is-yarn-global": "^0.3.0", 1340 | "latest-version": "^5.0.0", 1341 | "pupa": "^2.0.1", 1342 | "semver-diff": "^3.1.1", 1343 | "xdg-basedir": "^4.0.0" 1344 | } 1345 | }, 1346 | "url-parse-lax": { 1347 | "version": "3.0.0", 1348 | "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", 1349 | "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", 1350 | "requires": { 1351 | "prepend-http": "^2.0.0" 1352 | } 1353 | }, 1354 | "utils-merge": { 1355 | "version": "1.0.1", 1356 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 1357 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" 1358 | }, 1359 | "vary": { 1360 | "version": "1.1.2", 1361 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 1362 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" 1363 | }, 1364 | "widest-line": { 1365 | "version": "3.1.0", 1366 | "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", 1367 | "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", 1368 | "requires": { 1369 | "string-width": "^4.0.0" 1370 | } 1371 | }, 1372 | "wrappy": { 1373 | "version": "1.0.2", 1374 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1375 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" 1376 | }, 1377 | "write-file-atomic": { 1378 | "version": "3.0.3", 1379 | "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", 1380 | "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", 1381 | "requires": { 1382 | "imurmurhash": "^0.1.4", 1383 | "is-typedarray": "^1.0.0", 1384 | "signal-exit": "^3.0.2", 1385 | "typedarray-to-buffer": "^3.1.5" 1386 | } 1387 | }, 1388 | "xdg-basedir": { 1389 | "version": "4.0.0", 1390 | "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", 1391 | "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==" 1392 | }, 1393 | "xtend": { 1394 | "version": "4.0.2", 1395 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", 1396 | "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" 1397 | } 1398 | } 1399 | } 1400 | --------------------------------------------------------------------------------