├── .gitignore ├── server ├── .gitignore ├── Procfile ├── router.js ├── package.json ├── users.js ├── index.js └── package-lock.json ├── .github └── FUNDING.yml ├── .netlify └── state.json ├── client ├── public │ ├── favicon.ico │ └── index.html ├── src │ ├── components │ │ ├── Messages │ │ │ ├── Messages.css │ │ │ ├── Messages.js │ │ │ └── Message │ │ │ │ ├── Message.js │ │ │ │ └── Message.css │ │ ├── Input │ │ │ ├── Input.css │ │ │ └── Input.js │ │ ├── InfoBar │ │ │ ├── InfoBar.css │ │ │ └── InfoBar.js │ │ ├── TextContainer │ │ │ ├── TextContainer.css │ │ │ └── TextContainer.js │ │ ├── Chat │ │ │ ├── Chat.css │ │ │ └── Chat.js │ │ └── Join │ │ │ ├── Join.js │ │ │ └── Join.css │ ├── icons │ │ ├── closeIcon.png │ │ └── onlineIcon.png │ ├── index.js │ └── App.js ├── .gitignore ├── package.json ├── .eslintrc.js └── README.md └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /netlify -------------------------------------------------------------------------------- /server/.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules -------------------------------------------------------------------------------- /server/Procfile: -------------------------------------------------------------------------------- 1 | web: node index.js -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: adrianhajdin 2 | -------------------------------------------------------------------------------- /.netlify/state.json: -------------------------------------------------------------------------------- 1 | { 2 | "siteId": "70e96aae-6490-4d7b-b117-50aa0efd24fc" 3 | } -------------------------------------------------------------------------------- /client/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appledesire/react-chat-application/HEAD/client/public/favicon.ico -------------------------------------------------------------------------------- /client/src/components/Messages/Messages.css: -------------------------------------------------------------------------------- 1 | .messages { 2 | padding: 5% 0; 3 | overflow: auto; 4 | flex: auto; 5 | } -------------------------------------------------------------------------------- /client/src/icons/closeIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appledesire/react-chat-application/HEAD/client/src/icons/closeIcon.png -------------------------------------------------------------------------------- /client/src/icons/onlineIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appledesire/react-chat-application/HEAD/client/src/icons/onlineIcon.png -------------------------------------------------------------------------------- /client/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | 4 | import App from './App'; 5 | 6 | ReactDOM.render(, document.getElementById('root')); -------------------------------------------------------------------------------- /server/router.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const router = express.Router(); 3 | 4 | router.get("/", (req, res) => { 5 | res.send({ response: "Server is up and running." }).status(200); 6 | }); 7 | 8 | module.exports = router; -------------------------------------------------------------------------------- /client/.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | /node_modules 3 | /.pnp 4 | .pnp.js 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env.local 15 | .env.development.local 16 | .env.test.local 17 | .env.production.local 18 | 19 | npm-debug.log* 20 | yarn-debug.log* 21 | yarn-error.log* 22 | -------------------------------------------------------------------------------- /server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "nodemon index.js" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "cors": "^2.8.5", 14 | "express": "^4.17.1", 15 | "socket.io": "^2.2.0" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /client/src/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import Chat from './components/Chat/Chat'; 4 | import Join from './components/Join/Join'; 5 | 6 | import { BrowserRouter as Router, Route } from "react-router-dom"; 7 | 8 | const App = () => { 9 | return ( 10 | 11 | 12 | 13 | 14 | ); 15 | } 16 | 17 | export default App; 18 | -------------------------------------------------------------------------------- /client/src/components/Messages/Messages.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import ScrollToBottom from 'react-scroll-to-bottom'; 4 | 5 | import Message from './Message/Message'; 6 | 7 | import './Messages.css'; 8 | 9 | const Messages = ({ messages, name }) => ( 10 | 11 | {messages.map((message, i) =>
)} 12 |
13 | ); 14 | 15 | export default Messages; -------------------------------------------------------------------------------- /client/src/components/Input/Input.css: -------------------------------------------------------------------------------- 1 | .form { 2 | display: flex; 3 | border-top: 2px solid #D3D3D3; 4 | } 5 | 6 | .input { 7 | border: none; 8 | border-radius: 0; 9 | padding: 5%; 10 | width: 80%; 11 | font-size: 1.2em; 12 | } 13 | 14 | input:focus, textarea:focus, select:focus{ 15 | outline: none; 16 | } 17 | 18 | .sendButton { 19 | color: #fff !important; 20 | text-transform: uppercase; 21 | text-decoration: none; 22 | background: #2979FF; 23 | padding: 20px; 24 | display: inline-block; 25 | border: none; 26 | width: 20%; 27 | } -------------------------------------------------------------------------------- /client/src/components/InfoBar/InfoBar.css: -------------------------------------------------------------------------------- 1 | .infoBar { 2 | display: flex; 3 | align-items: center; 4 | justify-content: space-between; 5 | background: #2979FF; 6 | border-radius: 4px 4px 0 0; 7 | height: 60px; 8 | width: 100%; 9 | } 10 | 11 | .leftInnerContainer { 12 | flex: 0.5; 13 | display: flex; 14 | align-items: center; 15 | margin-left: 5%; 16 | color: white; 17 | } 18 | 19 | .rightInnerContainer { 20 | display: flex; 21 | flex: 0.5; 22 | justify-content: flex-end; 23 | margin-right: 5%; 24 | } 25 | 26 | .onlineIcon { 27 | margin-right: 5%; 28 | } 29 | -------------------------------------------------------------------------------- /client/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | React App 10 | 11 | 12 | 13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /client/src/components/Input/Input.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import './Input.css'; 4 | 5 | const Input = ({ setMessage, sendMessage, message }) => ( 6 |
7 | setMessage(value)} 13 | onKeyPress={event => event.key === 'Enter' ? sendMessage(event) : null} 14 | /> 15 | 16 |
17 | ) 18 | 19 | export default Input; -------------------------------------------------------------------------------- /client/src/components/InfoBar/InfoBar.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import onlineIcon from '../../icons/onlineIcon.png'; 4 | import closeIcon from '../../icons/closeIcon.png'; 5 | 6 | import './InfoBar.css'; 7 | 8 | const InfoBar = ({ room }) => ( 9 |
10 |
11 | online icon 12 |

{room}

13 |
14 |
15 | close icon 16 |
17 |
18 | ); 19 | 20 | export default InfoBar; -------------------------------------------------------------------------------- /client/src/components/TextContainer/TextContainer.css: -------------------------------------------------------------------------------- 1 | .textContainer { 2 | display: flex; 3 | flex-direction: column; 4 | margin-left: 100px; 5 | color: white; 6 | height: 60%; 7 | justify-content: space-between; 8 | } 9 | 10 | .activeContainer { 11 | display: flex; 12 | align-items: center; 13 | margin-bottom: 50%; 14 | } 15 | 16 | .activeItem { 17 | display: flex; 18 | align-items: center; 19 | } 20 | 21 | .activeContainer img { 22 | padding-left: 10px; 23 | } 24 | 25 | .textContainer h1 { 26 | margin-bottom: 0px; 27 | } 28 | 29 | @media (min-width: 320px) and (max-width: 1200px) { 30 | .textContainer { 31 | display: none; 32 | } 33 | } -------------------------------------------------------------------------------- /client/src/components/Chat/Chat.css: -------------------------------------------------------------------------------- 1 | .outerContainer { 2 | display: flex; 3 | justify-content: center; 4 | align-items: center; 5 | height: 100vh; 6 | background-color: #1A1A1D; 7 | } 8 | 9 | .container { 10 | display: flex; 11 | flex-direction: column; 12 | justify-content: space-between; 13 | background: #FFFFFF; 14 | border-radius: 8px; 15 | height: 60%; 16 | width: 35%; 17 | } 18 | 19 | @media (min-width: 320px) and (max-width: 480px) { 20 | .outerContainer { 21 | height: 100%; 22 | } 23 | 24 | .container { 25 | width: 100%; 26 | height: 100%; 27 | } 28 | } 29 | 30 | @media (min-width: 480px) and (max-width: 1200px) { 31 | .container { 32 | width: 60%; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Realtime Chat Application 2 | 3 | ### [Live Site](https://realtime-chat-application.netlify.com) 4 | 5 | ![Chat Application](https://i.ytimg.com/vi/ZwFA3YMfkoc/maxresdefault.jpg) 6 | 7 | ## Introduction 8 | This is a code repository for the corresponding video tutorial. 9 | 10 | In this video, we will create a full Realtime Chat Application. We're going to use React on the front end, with NodeJS + Socket.io web socket library on the back end. 11 | 12 | By the end of this video, you will have a strong understanding of how to send and receive messages using web sockets and Socket.io to make any real-time application. 13 | 14 | ## Launch your development career with project-based coaching - https://www.jsmastery.pro 15 | 16 | Setup: 17 | - run ```npm i && npm start``` for both client and server side to start the development server 18 | -------------------------------------------------------------------------------- /server/users.js: -------------------------------------------------------------------------------- 1 | const users = []; 2 | 3 | const addUser = ({ id, name, room }) => { 4 | name = name.trim().toLowerCase(); 5 | room = room.trim().toLowerCase(); 6 | 7 | const existingUser = users.find((user) => user.room === room && user.name === name); 8 | 9 | if(!name || !room) return { error: 'Username and room are required.' }; 10 | if(existingUser) return { error: 'Username is taken.' }; 11 | 12 | const user = { id, name, room }; 13 | 14 | users.push(user); 15 | 16 | return { user }; 17 | } 18 | 19 | const removeUser = (id) => { 20 | const index = users.findIndex((user) => user.id === id); 21 | 22 | if(index !== -1) return users.splice(index, 1)[0]; 23 | } 24 | 25 | const getUser = (id) => users.find((user) => user.id === id); 26 | 27 | const getUsersInRoom = (room) => users.filter((user) => user.room === room); 28 | 29 | module.exports = { addUser, removeUser, getUser, getUsersInRoom }; -------------------------------------------------------------------------------- /client/src/components/Join/Join.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { Link } from "react-router-dom"; 3 | 4 | import './Join.css'; 5 | 6 | export default function SignIn() { 7 | const [name, setName] = useState(''); 8 | const [room, setRoom] = useState(''); 9 | 10 | return ( 11 |
12 |
13 |

Join

14 |
15 | setName(event.target.value)} /> 16 |
17 |
18 | setRoom(event.target.value)} /> 19 |
20 | (!name || !room) ? e.preventDefault() : null} to={`/chat?name=${name}&room=${room}`}> 21 | 22 | 23 |
24 |
25 | ); 26 | } 27 | -------------------------------------------------------------------------------- /client/src/components/Messages/Message/Message.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import './Message.css'; 4 | 5 | import ReactEmoji from 'react-emoji'; 6 | 7 | const Message = ({ message: { text, user }, name }) => { 8 | let isSentByCurrentUser = false; 9 | 10 | const trimmedName = name.trim().toLowerCase(); 11 | 12 | if(user === trimmedName) { 13 | isSentByCurrentUser = true; 14 | } 15 | 16 | return ( 17 | isSentByCurrentUser 18 | ? ( 19 |
20 |

{trimmedName}

21 |
22 |

{ReactEmoji.emojify(text)}

23 |
24 |
25 | ) 26 | : ( 27 |
28 |
29 |

{ReactEmoji.emojify(text)}

30 |
31 |

{user}

32 |
33 | ) 34 | ); 35 | } 36 | 37 | export default Message; -------------------------------------------------------------------------------- /client/src/components/TextContainer/TextContainer.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import onlineIcon from '../../icons/onlineIcon.png'; 4 | 5 | import './TextContainer.css'; 6 | 7 | const TextContainer = ({ users }) => ( 8 |
9 |
10 |

Realtime Chat Application 💬

11 |

Created with React, Express, Node and Socket.IO ❤️

12 |

Try it out right now! ⬅️

13 |
14 | { 15 | users 16 | ? ( 17 |
18 |

People currently chatting:

19 |
20 |

21 | {users.map(({name}) => ( 22 |
23 | {name} 24 | Online Icon 25 |
26 | ))} 27 |

28 |
29 |
30 | ) 31 | : null 32 | } 33 |
34 | ); 35 | 36 | export default TextContainer; -------------------------------------------------------------------------------- /client/src/components/Messages/Message/Message.css: -------------------------------------------------------------------------------- 1 | .messageBox { 2 | background: #F3F3F3; 3 | border-radius: 20px; 4 | padding: 5px 20px; 5 | color: white; 6 | display: inline-block; 7 | max-width: 80%; 8 | } 9 | 10 | .messageText { 11 | width: 100%; 12 | letter-spacing: 0; 13 | float: left; 14 | font-size: 1.1em; 15 | word-wrap: break-word; 16 | } 17 | 18 | .messageText img { 19 | vertical-align: middle; 20 | } 21 | 22 | .messageContainer { 23 | display: flex; 24 | justify-content: flex-end; 25 | padding: 0 5%; 26 | margin-top: 3px; 27 | } 28 | 29 | .sentText { 30 | display: flex; 31 | align-items: center; 32 | font-family: Helvetica; 33 | color: #828282; 34 | letter-spacing: 0.3px; 35 | } 36 | 37 | .pl-10 { 38 | padding-left: 10px; 39 | } 40 | 41 | .pr-10 { 42 | padding-right: 10px; 43 | } 44 | 45 | .justifyStart { 46 | justify-content: flex-start; 47 | } 48 | 49 | .justifyEnd { 50 | justify-content: flex-end; 51 | } 52 | 53 | .colorWhite { 54 | color: white; 55 | } 56 | 57 | .colorDark { 58 | color: #353535; 59 | } 60 | 61 | .backgroundBlue { 62 | background: #2979FF; 63 | } 64 | 65 | .backgroundLight { 66 | background: #F3F3F3; 67 | } 68 | -------------------------------------------------------------------------------- /client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "project_chat_application", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "query-string": "^6.8.2", 7 | "react": "^16.9.0", 8 | "react-dom": "^16.9.0", 9 | "react-emoji": "^0.5.0", 10 | "react-router-dom": "^5.0.1", 11 | "react-scripts": "3.1.1", 12 | "react-scroll-to-bottom": "^1.3.2", 13 | "socket.io-client": "^2.2.0" 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 | "devDependencies": { 37 | "eslint": "^6.2.2", 38 | "eslint-config-airbnb": "^18.0.1", 39 | "eslint-plugin-import": "^2.18.2", 40 | "eslint-plugin-jsx-a11y": "^6.2.3", 41 | "eslint-plugin-react": "^7.14.3", 42 | "eslint-plugin-react-hooks": "^1.7.0" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /client/src/components/Join/Join.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | font-family: 'Roboto', sans-serif; 3 | padding: 0; 4 | margin: 0; 5 | } 6 | 7 | #root { 8 | height: 100vh; 9 | } 10 | 11 | * { 12 | box-sizing: border-box; 13 | } 14 | 15 | .joinOuterContainer { 16 | display: flex; 17 | justify-content: center; 18 | text-align: center; 19 | height: 100vh; 20 | align-items: center; 21 | background-color: #1A1A1D; 22 | } 23 | 24 | .joinInnerContainer { 25 | width: 20%; 26 | } 27 | 28 | .joinInput { 29 | border-radius: 0; 30 | padding: 15px 20px; 31 | width: 100%; 32 | } 33 | 34 | .heading { 35 | color: white; 36 | font-size: 2.5em; 37 | padding-bottom: 10px; 38 | border-bottom: 2px solid white; 39 | } 40 | 41 | .button { 42 | color: #fff !important; 43 | text-transform: uppercase; 44 | text-decoration: none; 45 | background: #2979FF; 46 | padding: 20px; 47 | border-radius: 5px; 48 | display: inline-block; 49 | border: none; 50 | width: 100%; 51 | } 52 | 53 | .mt-20 { 54 | margin-top: 20px; 55 | } 56 | 57 | @media (min-width: 320px) and (max-width: 480px) { 58 | .joinOuterContainer { 59 | height: 100%; 60 | } 61 | 62 | .joinInnerContainer { 63 | width: 90%; 64 | } 65 | 66 | 67 | } 68 | 69 | button:focus { 70 | outline: 0; 71 | } -------------------------------------------------------------------------------- /client/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "extends": "airbnb", 3 | "parser": "babel-eslint", 4 | "env": { 5 | "browser": true, 6 | "node": true, 7 | "mocha": true 8 | }, 9 | "rules": { 10 | "import/prefer-default-export": 0, 11 | "max-len": [ 12 | 2, 13 | 250 14 | ], 15 | "no-multiple-empty-lines": [ 16 | "error", 17 | { 18 | "max": 1, 19 | "maxEOF": 1 20 | } 21 | ], 22 | "no-underscore-dangle": [ 23 | "error", 24 | { 25 | "allow": [ 26 | "_d", 27 | "_dh", 28 | "_h", 29 | "_id", 30 | "_m", 31 | "_n", 32 | "_t", 33 | "_text" 34 | ] 35 | } 36 | ], 37 | "object-curly-newline": 0, 38 | "react/jsx-filename-extension": 0, 39 | "react/jsx-one-expression-per-line": 0, 40 | "jsx-a11y/click-events-have-key-events": 0, 41 | "jsx-a11y/alt-text": 0, 42 | "jsx-a11y/no-autofocus": 0, 43 | "jsx-a11y/no-static-element-interactions": 0, 44 | "react/no-array-index-key": 0, 45 | "jsx-a11y/anchor-is-valid": [ 46 | "error", 47 | { 48 | "components": [ 49 | "Link" 50 | ], 51 | "specialLink": [ 52 | "to", 53 | "hrefLeft", 54 | "hrefRight" 55 | ], 56 | "aspects": [ 57 | "noHref", 58 | "invalidHref", 59 | "preferButton" 60 | ] 61 | } 62 | ] 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /server/index.js: -------------------------------------------------------------------------------- 1 | const http = require('http'); 2 | const express = require('express'); 3 | const socketio = require('socket.io'); 4 | const cors = require('cors'); 5 | 6 | const { addUser, removeUser, getUser, getUsersInRoom } = require('./users'); 7 | 8 | const router = require('./router'); 9 | 10 | const app = express(); 11 | const server = http.createServer(app); 12 | const io = socketio(server); 13 | 14 | app.use(cors()); 15 | app.use(router); 16 | 17 | io.on('connect', (socket) => { 18 | socket.on('join', ({ name, room }, callback) => { 19 | const { error, user } = addUser({ id: socket.id, name, room }); 20 | 21 | if(error) return callback(error); 22 | 23 | socket.join(user.room); 24 | 25 | socket.emit('message', { user: 'admin', text: `${user.name}, welcome to room ${user.room}.`}); 26 | socket.broadcast.to(user.room).emit('message', { user: 'admin', text: `${user.name} has joined!` }); 27 | 28 | io.to(user.room).emit('roomData', { room: user.room, users: getUsersInRoom(user.room) }); 29 | 30 | callback(); 31 | }); 32 | 33 | socket.on('sendMessage', (message, callback) => { 34 | const user = getUser(socket.id); 35 | 36 | io.to(user.room).emit('message', { user: user.name, text: message }); 37 | 38 | callback(); 39 | }); 40 | 41 | socket.on('disconnect', () => { 42 | const user = removeUser(socket.id); 43 | 44 | if(user) { 45 | io.to(user.room).emit('message', { user: 'Admin', text: `${user.name} has left.` }); 46 | io.to(user.room).emit('roomData', { room: user.room, users: getUsersInRoom(user.room)}); 47 | } 48 | }) 49 | }); 50 | 51 | server.listen(process.env.PORT || 5000, () => console.log(`Server has started.`)); -------------------------------------------------------------------------------- /client/src/components/Chat/Chat.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from "react"; 2 | import queryString from 'query-string'; 3 | import io from "socket.io-client"; 4 | 5 | import TextContainer from '../TextContainer/TextContainer'; 6 | import Messages from '../Messages/Messages'; 7 | import InfoBar from '../InfoBar/InfoBar'; 8 | import Input from '../Input/Input'; 9 | 10 | import './Chat.css'; 11 | 12 | const ENDPOINT = 'https://project-chat-application.herokuapp.com/'; 13 | 14 | let socket; 15 | 16 | const Chat = ({ location }) => { 17 | const [name, setName] = useState(''); 18 | const [room, setRoom] = useState(''); 19 | const [users, setUsers] = useState(''); 20 | const [message, setMessage] = useState(''); 21 | const [messages, setMessages] = useState([]); 22 | 23 | useEffect(() => { 24 | const { name, room } = queryString.parse(location.search); 25 | 26 | socket = io(ENDPOINT); 27 | 28 | setRoom(room); 29 | setName(name) 30 | 31 | socket.emit('join', { name, room }, (error) => { 32 | if(error) { 33 | alert(error); 34 | } 35 | }); 36 | }, [ENDPOINT, location.search]); 37 | 38 | useEffect(() => { 39 | socket.on('message', message => { 40 | setMessages(messages => [ ...messages, message ]); 41 | }); 42 | 43 | socket.on("roomData", ({ users }) => { 44 | setUsers(users); 45 | }); 46 | }, []); 47 | 48 | const sendMessage = (event) => { 49 | event.preventDefault(); 50 | 51 | if(message) { 52 | socket.emit('sendMessage', message, () => setMessage('')); 53 | } 54 | } 55 | 56 | return ( 57 |
58 |
59 | 60 | 61 | 62 |
63 | 64 |
65 | ); 66 | } 67 | 68 | export default Chat; 69 | -------------------------------------------------------------------------------- /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/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "accepts": { 8 | "version": "1.3.7", 9 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", 10 | "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", 11 | "requires": { 12 | "mime-types": "~2.1.24", 13 | "negotiator": "0.6.2" 14 | } 15 | }, 16 | "after": { 17 | "version": "0.8.2", 18 | "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz", 19 | "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=" 20 | }, 21 | "array-flatten": { 22 | "version": "1.1.1", 23 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 24 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" 25 | }, 26 | "arraybuffer.slice": { 27 | "version": "0.0.7", 28 | "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz", 29 | "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==" 30 | }, 31 | "async-limiter": { 32 | "version": "1.0.1", 33 | "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", 34 | "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" 35 | }, 36 | "backo2": { 37 | "version": "1.0.2", 38 | "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", 39 | "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=" 40 | }, 41 | "base64-arraybuffer": { 42 | "version": "0.1.5", 43 | "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz", 44 | "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=" 45 | }, 46 | "base64id": { 47 | "version": "1.0.0", 48 | "resolved": "https://registry.npmjs.org/base64id/-/base64id-1.0.0.tgz", 49 | "integrity": "sha1-R2iMuZu2gE8OBtPnY7HDLlfY5rY=" 50 | }, 51 | "better-assert": { 52 | "version": "1.0.2", 53 | "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz", 54 | "integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=", 55 | "requires": { 56 | "callsite": "1.0.0" 57 | } 58 | }, 59 | "blob": { 60 | "version": "0.0.5", 61 | "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz", 62 | "integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig==" 63 | }, 64 | "body-parser": { 65 | "version": "1.19.0", 66 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", 67 | "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", 68 | "requires": { 69 | "bytes": "3.1.0", 70 | "content-type": "~1.0.4", 71 | "debug": "2.6.9", 72 | "depd": "~1.1.2", 73 | "http-errors": "1.7.2", 74 | "iconv-lite": "0.4.24", 75 | "on-finished": "~2.3.0", 76 | "qs": "6.7.0", 77 | "raw-body": "2.4.0", 78 | "type-is": "~1.6.17" 79 | } 80 | }, 81 | "bytes": { 82 | "version": "3.1.0", 83 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", 84 | "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" 85 | }, 86 | "callsite": { 87 | "version": "1.0.0", 88 | "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", 89 | "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=" 90 | }, 91 | "component-bind": { 92 | "version": "1.0.0", 93 | "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", 94 | "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E=" 95 | }, 96 | "component-emitter": { 97 | "version": "1.2.1", 98 | "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", 99 | "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=" 100 | }, 101 | "component-inherit": { 102 | "version": "0.0.3", 103 | "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz", 104 | "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM=" 105 | }, 106 | "content-disposition": { 107 | "version": "0.5.3", 108 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", 109 | "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", 110 | "requires": { 111 | "safe-buffer": "5.1.2" 112 | } 113 | }, 114 | "content-type": { 115 | "version": "1.0.4", 116 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", 117 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" 118 | }, 119 | "cookie": { 120 | "version": "0.4.0", 121 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", 122 | "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" 123 | }, 124 | "cookie-signature": { 125 | "version": "1.0.6", 126 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 127 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" 128 | }, 129 | "cors": { 130 | "version": "2.8.5", 131 | "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", 132 | "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", 133 | "requires": { 134 | "object-assign": "^4", 135 | "vary": "^1" 136 | } 137 | }, 138 | "debug": { 139 | "version": "2.6.9", 140 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 141 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 142 | "requires": { 143 | "ms": "2.0.0" 144 | } 145 | }, 146 | "depd": { 147 | "version": "1.1.2", 148 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", 149 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" 150 | }, 151 | "destroy": { 152 | "version": "1.0.4", 153 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", 154 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" 155 | }, 156 | "ee-first": { 157 | "version": "1.1.1", 158 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 159 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" 160 | }, 161 | "encodeurl": { 162 | "version": "1.0.2", 163 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 164 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" 165 | }, 166 | "engine.io": { 167 | "version": "3.3.2", 168 | "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.3.2.tgz", 169 | "integrity": "sha512-AsaA9KG7cWPXWHp5FvHdDWY3AMWeZ8x+2pUVLcn71qE5AtAzgGbxuclOytygskw8XGmiQafTmnI9Bix3uihu2w==", 170 | "requires": { 171 | "accepts": "~1.3.4", 172 | "base64id": "1.0.0", 173 | "cookie": "0.3.1", 174 | "debug": "~3.1.0", 175 | "engine.io-parser": "~2.1.0", 176 | "ws": "~6.1.0" 177 | }, 178 | "dependencies": { 179 | "cookie": { 180 | "version": "0.3.1", 181 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", 182 | "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" 183 | }, 184 | "debug": { 185 | "version": "3.1.0", 186 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", 187 | "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", 188 | "requires": { 189 | "ms": "2.0.0" 190 | } 191 | } 192 | } 193 | }, 194 | "engine.io-client": { 195 | "version": "3.3.2", 196 | "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.3.2.tgz", 197 | "integrity": "sha512-y0CPINnhMvPuwtqXfsGuWE8BB66+B6wTtCofQDRecMQPYX3MYUZXFNKDhdrSe3EVjgOu4V3rxdeqN/Tr91IgbQ==", 198 | "requires": { 199 | "component-emitter": "1.2.1", 200 | "component-inherit": "0.0.3", 201 | "debug": "~3.1.0", 202 | "engine.io-parser": "~2.1.1", 203 | "has-cors": "1.1.0", 204 | "indexof": "0.0.1", 205 | "parseqs": "0.0.5", 206 | "parseuri": "0.0.5", 207 | "ws": "~6.1.0", 208 | "xmlhttprequest-ssl": "~1.5.4", 209 | "yeast": "0.1.2" 210 | }, 211 | "dependencies": { 212 | "debug": { 213 | "version": "3.1.0", 214 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", 215 | "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", 216 | "requires": { 217 | "ms": "2.0.0" 218 | } 219 | } 220 | } 221 | }, 222 | "engine.io-parser": { 223 | "version": "2.1.3", 224 | "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.1.3.tgz", 225 | "integrity": "sha512-6HXPre2O4Houl7c4g7Ic/XzPnHBvaEmN90vtRO9uLmwtRqQmTOw0QMevL1TOfL2Cpu1VzsaTmMotQgMdkzGkVA==", 226 | "requires": { 227 | "after": "0.8.2", 228 | "arraybuffer.slice": "~0.0.7", 229 | "base64-arraybuffer": "0.1.5", 230 | "blob": "0.0.5", 231 | "has-binary2": "~1.0.2" 232 | } 233 | }, 234 | "escape-html": { 235 | "version": "1.0.3", 236 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 237 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" 238 | }, 239 | "etag": { 240 | "version": "1.8.1", 241 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 242 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" 243 | }, 244 | "express": { 245 | "version": "4.17.1", 246 | "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", 247 | "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", 248 | "requires": { 249 | "accepts": "~1.3.7", 250 | "array-flatten": "1.1.1", 251 | "body-parser": "1.19.0", 252 | "content-disposition": "0.5.3", 253 | "content-type": "~1.0.4", 254 | "cookie": "0.4.0", 255 | "cookie-signature": "1.0.6", 256 | "debug": "2.6.9", 257 | "depd": "~1.1.2", 258 | "encodeurl": "~1.0.2", 259 | "escape-html": "~1.0.3", 260 | "etag": "~1.8.1", 261 | "finalhandler": "~1.1.2", 262 | "fresh": "0.5.2", 263 | "merge-descriptors": "1.0.1", 264 | "methods": "~1.1.2", 265 | "on-finished": "~2.3.0", 266 | "parseurl": "~1.3.3", 267 | "path-to-regexp": "0.1.7", 268 | "proxy-addr": "~2.0.5", 269 | "qs": "6.7.0", 270 | "range-parser": "~1.2.1", 271 | "safe-buffer": "5.1.2", 272 | "send": "0.17.1", 273 | "serve-static": "1.14.1", 274 | "setprototypeof": "1.1.1", 275 | "statuses": "~1.5.0", 276 | "type-is": "~1.6.18", 277 | "utils-merge": "1.0.1", 278 | "vary": "~1.1.2" 279 | } 280 | }, 281 | "finalhandler": { 282 | "version": "1.1.2", 283 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", 284 | "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", 285 | "requires": { 286 | "debug": "2.6.9", 287 | "encodeurl": "~1.0.2", 288 | "escape-html": "~1.0.3", 289 | "on-finished": "~2.3.0", 290 | "parseurl": "~1.3.3", 291 | "statuses": "~1.5.0", 292 | "unpipe": "~1.0.0" 293 | } 294 | }, 295 | "forwarded": { 296 | "version": "0.1.2", 297 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", 298 | "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" 299 | }, 300 | "fresh": { 301 | "version": "0.5.2", 302 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 303 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" 304 | }, 305 | "has-binary2": { 306 | "version": "1.0.3", 307 | "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz", 308 | "integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==", 309 | "requires": { 310 | "isarray": "2.0.1" 311 | } 312 | }, 313 | "has-cors": { 314 | "version": "1.1.0", 315 | "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz", 316 | "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=" 317 | }, 318 | "http-errors": { 319 | "version": "1.7.2", 320 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", 321 | "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", 322 | "requires": { 323 | "depd": "~1.1.2", 324 | "inherits": "2.0.3", 325 | "setprototypeof": "1.1.1", 326 | "statuses": ">= 1.5.0 < 2", 327 | "toidentifier": "1.0.0" 328 | } 329 | }, 330 | "iconv-lite": { 331 | "version": "0.4.24", 332 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 333 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 334 | "requires": { 335 | "safer-buffer": ">= 2.1.2 < 3" 336 | } 337 | }, 338 | "indexof": { 339 | "version": "0.0.1", 340 | "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", 341 | "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=" 342 | }, 343 | "inherits": { 344 | "version": "2.0.3", 345 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 346 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 347 | }, 348 | "ipaddr.js": { 349 | "version": "1.9.0", 350 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz", 351 | "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==" 352 | }, 353 | "isarray": { 354 | "version": "2.0.1", 355 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", 356 | "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=" 357 | }, 358 | "media-typer": { 359 | "version": "0.3.0", 360 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 361 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" 362 | }, 363 | "merge-descriptors": { 364 | "version": "1.0.1", 365 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 366 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" 367 | }, 368 | "methods": { 369 | "version": "1.1.2", 370 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 371 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" 372 | }, 373 | "mime": { 374 | "version": "1.6.0", 375 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", 376 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" 377 | }, 378 | "mime-db": { 379 | "version": "1.40.0", 380 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", 381 | "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==" 382 | }, 383 | "mime-types": { 384 | "version": "2.1.24", 385 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", 386 | "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", 387 | "requires": { 388 | "mime-db": "1.40.0" 389 | } 390 | }, 391 | "ms": { 392 | "version": "2.0.0", 393 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 394 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 395 | }, 396 | "negotiator": { 397 | "version": "0.6.2", 398 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", 399 | "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" 400 | }, 401 | "object-assign": { 402 | "version": "4.1.0", 403 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.0.tgz", 404 | "integrity": "sha1-ejs9DpgGPUP0wD8uiubNUahog6A=" 405 | }, 406 | "object-component": { 407 | "version": "0.0.3", 408 | "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz", 409 | "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE=" 410 | }, 411 | "on-finished": { 412 | "version": "2.3.0", 413 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", 414 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", 415 | "requires": { 416 | "ee-first": "1.1.1" 417 | } 418 | }, 419 | "parseqs": { 420 | "version": "0.0.5", 421 | "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz", 422 | "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=", 423 | "requires": { 424 | "better-assert": "~1.0.0" 425 | } 426 | }, 427 | "parseuri": { 428 | "version": "0.0.5", 429 | "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz", 430 | "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=", 431 | "requires": { 432 | "better-assert": "~1.0.0" 433 | } 434 | }, 435 | "parseurl": { 436 | "version": "1.3.3", 437 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", 438 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" 439 | }, 440 | "path-to-regexp": { 441 | "version": "0.1.7", 442 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 443 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" 444 | }, 445 | "proxy-addr": { 446 | "version": "2.0.5", 447 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz", 448 | "integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==", 449 | "requires": { 450 | "forwarded": "~0.1.2", 451 | "ipaddr.js": "1.9.0" 452 | } 453 | }, 454 | "qs": { 455 | "version": "6.7.0", 456 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", 457 | "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" 458 | }, 459 | "range-parser": { 460 | "version": "1.2.1", 461 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", 462 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" 463 | }, 464 | "raw-body": { 465 | "version": "2.4.0", 466 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", 467 | "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", 468 | "requires": { 469 | "bytes": "3.1.0", 470 | "http-errors": "1.7.2", 471 | "iconv-lite": "0.4.24", 472 | "unpipe": "1.0.0" 473 | } 474 | }, 475 | "safe-buffer": { 476 | "version": "5.1.2", 477 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 478 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" 479 | }, 480 | "safer-buffer": { 481 | "version": "2.1.2", 482 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 483 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 484 | }, 485 | "send": { 486 | "version": "0.17.1", 487 | "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", 488 | "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", 489 | "requires": { 490 | "debug": "2.6.9", 491 | "depd": "~1.1.2", 492 | "destroy": "~1.0.4", 493 | "encodeurl": "~1.0.2", 494 | "escape-html": "~1.0.3", 495 | "etag": "~1.8.1", 496 | "fresh": "0.5.2", 497 | "http-errors": "~1.7.2", 498 | "mime": "1.6.0", 499 | "ms": "2.1.1", 500 | "on-finished": "~2.3.0", 501 | "range-parser": "~1.2.1", 502 | "statuses": "~1.5.0" 503 | }, 504 | "dependencies": { 505 | "ms": { 506 | "version": "2.1.1", 507 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", 508 | "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" 509 | } 510 | } 511 | }, 512 | "serve-static": { 513 | "version": "1.14.1", 514 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", 515 | "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", 516 | "requires": { 517 | "encodeurl": "~1.0.2", 518 | "escape-html": "~1.0.3", 519 | "parseurl": "~1.3.3", 520 | "send": "0.17.1" 521 | } 522 | }, 523 | "setprototypeof": { 524 | "version": "1.1.1", 525 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", 526 | "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" 527 | }, 528 | "socket.io": { 529 | "version": "2.2.0", 530 | "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.2.0.tgz", 531 | "integrity": "sha512-wxXrIuZ8AILcn+f1B4ez4hJTPG24iNgxBBDaJfT6MsyOhVYiTXWexGoPkd87ktJG8kQEcL/NBvRi64+9k4Kc0w==", 532 | "requires": { 533 | "debug": "~4.1.0", 534 | "engine.io": "~3.3.1", 535 | "has-binary2": "~1.0.2", 536 | "socket.io-adapter": "~1.1.0", 537 | "socket.io-client": "2.2.0", 538 | "socket.io-parser": "~3.3.0" 539 | }, 540 | "dependencies": { 541 | "debug": { 542 | "version": "4.1.1", 543 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", 544 | "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", 545 | "requires": { 546 | "ms": "^2.1.1" 547 | } 548 | }, 549 | "ms": { 550 | "version": "2.1.2", 551 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 552 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 553 | } 554 | } 555 | }, 556 | "socket.io-adapter": { 557 | "version": "1.1.1", 558 | "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.1.tgz", 559 | "integrity": "sha1-KoBeihTWNyEk3ZFZrUUC+MsH8Gs=" 560 | }, 561 | "socket.io-client": { 562 | "version": "2.2.0", 563 | "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.2.0.tgz", 564 | "integrity": "sha512-56ZrkTDbdTLmBIyfFYesgOxsjcLnwAKoN4CiPyTVkMQj3zTUh0QAx3GbvIvLpFEOvQWu92yyWICxB0u7wkVbYA==", 565 | "requires": { 566 | "backo2": "1.0.2", 567 | "base64-arraybuffer": "0.1.5", 568 | "component-bind": "1.0.0", 569 | "component-emitter": "1.2.1", 570 | "debug": "~3.1.0", 571 | "engine.io-client": "~3.3.1", 572 | "has-binary2": "~1.0.2", 573 | "has-cors": "1.1.0", 574 | "indexof": "0.0.1", 575 | "object-component": "0.0.3", 576 | "parseqs": "0.0.5", 577 | "parseuri": "0.0.5", 578 | "socket.io-parser": "~3.3.0", 579 | "to-array": "0.1.4" 580 | }, 581 | "dependencies": { 582 | "debug": { 583 | "version": "3.1.0", 584 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", 585 | "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", 586 | "requires": { 587 | "ms": "2.0.0" 588 | } 589 | } 590 | } 591 | }, 592 | "socket.io-parser": { 593 | "version": "3.3.0", 594 | "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.0.tgz", 595 | "integrity": "sha512-hczmV6bDgdaEbVqhAeVMM/jfUfzuEZHsQg6eOmLgJht6G3mPKMxYm75w2+qhAQZ+4X+1+ATZ+QFKeOZD5riHng==", 596 | "requires": { 597 | "component-emitter": "1.2.1", 598 | "debug": "~3.1.0", 599 | "isarray": "2.0.1" 600 | }, 601 | "dependencies": { 602 | "debug": { 603 | "version": "3.1.0", 604 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", 605 | "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", 606 | "requires": { 607 | "ms": "2.0.0" 608 | } 609 | } 610 | } 611 | }, 612 | "statuses": { 613 | "version": "1.5.0", 614 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", 615 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" 616 | }, 617 | "to-array": { 618 | "version": "0.1.4", 619 | "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz", 620 | "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA=" 621 | }, 622 | "toidentifier": { 623 | "version": "1.0.0", 624 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", 625 | "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" 626 | }, 627 | "type-is": { 628 | "version": "1.6.18", 629 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", 630 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", 631 | "requires": { 632 | "media-typer": "0.3.0", 633 | "mime-types": "~2.1.24" 634 | } 635 | }, 636 | "unpipe": { 637 | "version": "1.0.0", 638 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 639 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" 640 | }, 641 | "utils-merge": { 642 | "version": "1.0.1", 643 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 644 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" 645 | }, 646 | "vary": { 647 | "version": "1.1.2", 648 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 649 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" 650 | }, 651 | "ws": { 652 | "version": "6.1.4", 653 | "resolved": "https://registry.npmjs.org/ws/-/ws-6.1.4.tgz", 654 | "integrity": "sha512-eqZfL+NE/YQc1/ZynhojeV8q+H050oR8AZ2uIev7RU10svA9ZnJUddHcOUZTJLinZ9yEfdA2kSATS2qZK5fhJA==", 655 | "requires": { 656 | "async-limiter": "~1.0.0" 657 | } 658 | }, 659 | "xmlhttprequest-ssl": { 660 | "version": "1.5.5", 661 | "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz", 662 | "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=" 663 | }, 664 | "yeast": { 665 | "version": "0.1.2", 666 | "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz", 667 | "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=" 668 | } 669 | } 670 | } 671 | --------------------------------------------------------------------------------