diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..cc05638e6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +# node mudules +node_modules +.env \ No newline at end of file diff --git a/app.js b/app.js new file mode 100644 index 000000000..f069ff849 --- /dev/null +++ b/app.js @@ -0,0 +1,30 @@ +import express from 'express'; +import httpModule from 'http'; +import { Server } from 'socket.io'; +import path from 'path'; + +import handleConnection from './events/connection.js'; +import handleDisconnect from './events/disconnect.js'; +import handleMessage from './events/message.js'; + +const app = express(); +const http = httpModule.createServer(app); +const io = new Server(http); +const port = process.env.PORT || 3000; + +app.use(express.static(path.resolve('public'))); +app.set('view engine', 'ejs'); + +app.get('/', (request, response) => { + response.render('index'); +}); + +io.on('connection', (socket) => { + handleConnection(socket); + handleDisconnect(socket); + handleMessage(socket, io); +}); + +http.listen(port, () => { + console.log(`listening on localhost:${port}`); +}); diff --git a/course/college 1 - live code voorbeeld.md b/course/college 1 - live code voorbeeld.md deleted file mode 100644 index 24b3bfb0b..000000000 --- a/course/college 1 - live code voorbeeld.md +++ /dev/null @@ -1,109 +0,0 @@ -# Live code college 1 -Gevoed door [https://socket.io/get-started/chat](https://socket.io/get-started/chat) en uitgewerkt in [https://github.com/ju5tu5/barebonechat](https://github.com/ju5tu5/barebonechat). - -## Stap 1 - Basic Node + Express (AKA Project Tech) -Minimale express app, het enige dat nodig is is een `npm init` en `npm install express`. Daarmee heb je vanuit node.js de mogelijkheid te luisteren naar een poortnummer en daar een response op te geven. In dit geval een heel simpele response na een call op `/`, de root van een website. - -``` -const express = require('express') -const app = express() -const http = require('http').createServer(app) - -app.get('/', (request, response) => { - response.send('

Hallo wereld!

') -}) - -http.listen(4242, () => { - console.log('listening on 4242') -}) -``` - -## Stap 2 - Serving HTML -Het zou naar zijn om alle HTML te *embedden* in javascript. Vroeger in PHP was dit de normale manier van werken maar wij vinden scheiden van inhoud/ vormgeving/ functionaliteit belangrijk. Dus.. we stoppen wat HTML en CSS in een bestand en serveren dat. - -``` -const path = require('path') - -app.use(express.static(path.resolve('public'))) -``` - -## Stap 3 - Socket.io -We runnen het commando `npm install socket.io` om dit pakketje binnen te trekken. Het wordt daarmee automagisch als dependency opgenomen in package.json (nice!). - -Is iedereen bekend met de event loop in (node).js? Het is wel een ingewikkeld ding maar ik probeer die heel gesimplificeerd uit te leggen zodat je een idee hebt. Voor de nerds kunnen we daar later dieper op in gaan.. animo? (zie sheet!) - -`Socket.io` propageert zelf events, daar kunnen we dus een event listener aan koppelen en iets doen. We passen het server script aan om een console bericht te loggen zodra er een gebruiker verbinding maakt met via socket.io, dat zie je aan het `connection` event. - -``` -const io = require('socket.io')(http) -const port = process.env.PORT || 4242 - -io.on('connection', (socket) => { - console.log('a user connected'); -}); -``` - -Om de boel werken te krijgen moet er wel een gebruiker connecten. Daarom passen we de static file `/public/index.html` aan. `Socket.io` zorgt zelf voor de afhandeling van alle aanvragen op `http://example.com/socket.io/`, daar hoeven we dus verder niets voor te doen (w00t!). - -``` - - -``` - -Als een gebruiker connectie maakt zie je de log message die we ingesteld hebben, misschien willen we ook zien wanneer een gebruiker disconnect. - -``` -... - socket.on('disconnect', () => { - console.log('user disconnected') - }) -... -``` - -Uitproberen in de console met `nodemon start`, nodemon is heel handig tijdens developen want die update de draaiende code zodra er in de source iets veranderd. - -## Stap 4 - event propagation -Het grote idee achter socket.io is het afvuren en versturen (engels propagation) van events. Laten we *client-side* een event op de socket plaatsen als de gebruiker via het formulier een event verstuurt. - -``` -... -var messages = document.querySelector('section ul') -var input = document.querySelector('input') - -document.querySelector('form').addEventListener('submit', (event) => { - event.preventDefault() - if (input.value) { - socket.emit('message', input.value) - input.value = '' - } -}) - -``` - -In het kort i) luisteren (!) we naar het ‘submit’ event van het formulier, ii) stoppen de standaardactie (versturen en verversen van de huidige pagina), iii) checken of er inderdaad iets in het invoerveld staat, iv) versturen datgene in het invoerveld dmv. `emit` (uitzenden/uitstoten) en v) maken het invoerveld leeg. Nb alléén de server ontvangt dit event. - -Natuurlijk moet er ook op de server *geluisterd* worden naar dit type event op de socket. We voegen de volgende afhandeling toe: - -``` -socket.on('message', (message) => { - console.log('message: ' + message) -}) -``` - -Check het resultaat in de console! Okee we zijn nu op de server, elke client die connect kan berichten sturen die `console.log`ed worden. Nu moeten alle clients nog weten dat er ook berichten zijn. We sturen opnieuw via `emit` een event op de socket, nu vanaf de kant van de server. Nb. álle clients krijgen dit event. - -We breiden het script op de client uit om iets met dit event te doen, dat doen we door het toevoegen van een.. je raad het al.. event-listener. Voeg de volgende code toe. - -``` -... -socket.on('message', function(message) { - var element = document.createElement('li') - element.textContent = message - messages.appendChild(element) - messages.scrollTop = messages.scrollHeight -}) -``` - -En klaar is onze barebone chat app.. chat mee op [https://barebonechat.herokuapp.com/](https://barebonechat.herokuapp.com/) \ No newline at end of file diff --git a/course/peer-review.md b/course/peer-review.md deleted file mode 100644 index e48005d17..000000000 --- a/course/peer-review.md +++ /dev/null @@ -1,45 +0,0 @@ -# Peer review - -## Goals -- Get feedback on your work -- Check if your documentation is understandable -- Find out where you stand in relation to learning goals for this course - -## Preparation (please do this before attending) -- Push the latest changes to your readme, code, and/or wiki -- Make sure you’ve read and understand [the rubric](https://github.com/cmda-minor-web/real-time-web-2021#grading) for this course -- Read up on [how issues fit into the Github workflow](https://guides.github.com/features/issues/) and make sure you know how to [write useful issues](https://upthemes.com/blog/2014/02/writing-useful-github-issues/) -- Start a call with your sub-team (eg. Squid B.1 or R2D2 C.2) in MS Teams - -## Peer Review -Write feedback in issues on the repository of your peers, Use a separate issue for each point you’re making. Remember, positive feedback is also welcome and can be put into an issue! Follow the steps below for each of your sub-team members. - -### Steps -- Scan your buddy's repository - + Does the repo have an apt description? - + Is there a link to the live demo at the top? - + Are any files in the repo that shouldn't be there like an .env file or node_modules? - + Is there a section about the concept, install notes, data life cycle, external API, and real-time events? -- Try to understand the concept - + Read about the concept in the readme, do you understand what the app does? - + Open up the live link (if it's already alive) and play around with the app - + Review the app's functions in an issue, feel free to add cool new ideas in an issue - + Test the app in multiple tabs, does multi-user functionality work as expected? -- Understand the data life cycle - + Are all the app's components present in a visual diagram? - + Is it clear what data is stored for each component? - + Is it clear how components communicate with each other? (socket, polling, OAuth) - + Can you follow where real-time data is created? -- Check the real-time events - + Are all custom events described? - + Can you imagine how the client-server communication works now that you know which events are used? -- Review the API info - + Is it clear what data the external API offers? - + Are instructions present on how to get a key from the API and how to connect to it? -### Switch! -Follow the steps again for each of your sub-team members, add issues for feedback not already mentioned, add your own insights to issues created by other sub-team members. - -## Wrapping up -- Review the issues that were created for your own repo. Ask your reviewers about them if you don't understand. -- Respond to the issues with a message describing which action you take. For instance: valid point, will fix. Or: I won't fix this because of ... -- Close issues when they are solved. \ No newline at end of file diff --git a/course/week-1.md b/course/week-1.md deleted file mode 100644 index eeb9111d2..000000000 --- a/course/week-1.md +++ /dev/null @@ -1,102 +0,0 @@ -# Assignments for week 1 - -## Intention -After all you learned so far, you now will learn how to build real-time applications, and rule the world! - -## Assignments -1. [Make it so! (team)](#assignment-1-make-it-so) -2. [Make it so! (individually)](#assignment-2-make-it-so) - -## Assignment 1: Make it so! -This is a team exercise which means you work together on one repository. (Of course you will all fork it if the work is done so you’ve got your own copy in your own portfolio ;)). During this course we will use *readme driven development* (RDD) which is a way of organising your work. Use this - relatively simple - assignment to get a grip on the way you set up a project in this course. - -### Readme -Start out by forking [this repository](https://github.com/cmda-minor-web/real-time-web-2021) to one of your github accounts. Add all your team members as collaborator so you can work together. - -Read this [article about RDD](http://tom.preston-werner.com/2010/08/23/readme-driven-development.html) if you haven’t already and look at these [notes about RDD](https://deterministic.space/readme-driven-development.html) and [some of the examples in this gist](https://gist.github.com/stefanbirkner/835b7d0c498b4026f65a). Discuss findings, insights and/or allergies you encounter in with team. As you will be using markdown a lot when writing readme files you might want to freshen your knowledge by scanning [mastering markdown](https://guides.github.com/features/mastering-markdown/). - -Write your [README.md](../README.md) and get started on your project. As a team you will discuss details, make sketches, make decisions, divide tasks etc. Write all of this down so everyone has an idea of what to work on. - -*Note: keep your documentation up-to-date while working on your project!* - -*Note: Keep a teams meeting open in your squad channel while you work so we can find you and invite us into your meeting when you need help.* - -### Coding Style -Maybe you already have a preferred code-style, maybe you want to try something else. Discuss the style you will use during this project and write up a few basic rules in a section in your readme. You can look at some of the style guides below but keep in mind that these are very extensive, for this project (and your own sanity), please keep it simple. - -Set up at least an [npm script](https://docs.npmjs.com/cli/run-script). You will probably want some integrations with your editor. Use Nodemon or similar to allow instant reloading while developing. - -#### Resources -*Styleguides:* [w3schools.com .js conventions](https://www.w3schools.com/js/js_conventions.asp), [Google JavaScript Style Guide](https://google.github.io/styleguide/jsguide.html), [Airbnb JavaScript Style Guide](https://github.com/airbnb/javascript) -*Linters/auto-formatting:* [xo](https://github.com/xojs/xo), [standard](https://github.com/standard/standard), [prettier](https://github.com/prettier/prettier), [eslint](https://github.com/eslint/eslint), [vscode marketplace](https://marketplace.visualstudio.com/search?term=ES) - -### Serving static files -Set-up [Express](https://expressjs.com/en/4x/api.html) for static file serving or templating (optional). Implement your team page so that it will be served through express. - -### Implement socket.io -Set-up [socket.io](https://socket.io/). Create a basic real-time app using sockets. Bare minimum is a successful message from client to server and from server to all connected clients (“round trip”). You can use the [live coded example](https://github.com/ju5tu5/barebonechat) or the [chat demo from socket.io](https://socket.io/get-started/chat/) to get started. - -### Deploy on Cyclic.sh -Get your app running on [Cyclic](https://www.cyclic.sh/)! Add a link to your live demo to your project and a screenshot showing of your team page including the new chat functionality at the top of your readme. - -**Note:** Don’t 😱 if this doesn’t work out: there’s plenty time to deploy in week 2 or week 3. But it may be advantageous to deploy now so you can make sure all team members understand how to do this. - -### Extra challenges -Try to add an extra feature from the list below to your basic chat app. Do this one step at a time, each time discussing with your team what feature would be nice and how you would go about implementing it. Ask for help if you need it! **Note**: Update your readme vigourously ;) - -- Add support for nicknames. -- Broadcast a message to connected users when someone connects or disconnects. -- Don’t send the same message to the user that sent it. Instead, append the message directly as soon as he/she presses enter. -- Add “{user} is typing” functionality. -- Show who’s online. -- Add private messaging. - -### Great job! Now wrap it up... -Review the project as a team, create issues for everything that needs extra attention if you would have more time. Update [README.md](../README.md) to reflect the latest changes, take new screenshots, link to all team members etc. Finally, have all team members fork this repo so you each have a copy in your own Github account - it’s your technical portfolio. - -## Assignment 2: Make it so! -This individual exercise is not alltogether different from [assignment 1](#assignment-1-make-it-so) - the one you did as a team.. remember? It even bears the same name! For assignment 2 (this one) you will collect all of your experience and go full-out on a unique realtime app. Again you will use *readme driven development* (RDD). Use this assignment to start your adventure towards comprehension of the realtime universe. - -### Readme -You have probably forked your team project at the end of last assignment. Create a new branch called ‘team-page’ and switch back to the main branch. By branching out we’ve created a snapshot of this repository at this point, you can always find the code by switching to the ‘team-page’ branch. You will be working on this project by yourself so there is no need to add collaborators to Github. - -Think up unique features that will make your chat much more interesting. What is unique? Not unique are storing nicknames, showing who’s online or any of the other feature requests listed at the bottom of the [socket.io tutorial](https://socket.io/get-started/chat/). Unique is using D3 to structure text into SVG art, using words to fight each other in a text based game, additional features you can earn if you stay in chat long enough. It doesn’t even have to be a chat app, just use the socket.io framework. - -Think of something that matches your skill level. If you have trouble understanding the basics of socket.io, focus on the basics (and talk to us if you need help). - -Write your [README.md](../README.md), use the resources below and *make it so*! - -#### Resources -You’ve probably read all these before in [assignment 1](#assignment-1-make-it-so): -- [article about RDD](http://tom.preston-werner.com/2010/08/23/readme-driven-development.html) -- [notes about RDD](https://deterministic.space/readme-driven-development.html) -- [some RDD examples](https://gist.github.com/stefanbirkner/835b7d0c498b4026f65a) -- [mastering markdown](https://guides.github.com/features/mastering-markdown/). -- [npm script](https://docs.npmjs.com/cli/run-script) -- [w3schools.com .js conventions](https://www.w3schools.com/js/js_conventions.asp) -- [Google JavaScript Style Guide](https://google.github.io/styleguide/jsguide.html) -- [Airbnb JavaScript Style Guide](https://github.com/airbnb/javascript) -- [xo](https://github.com/xojs/xo) -- [standard](https://github.com/standard/standard) -- [prettier](https://github.com/prettier/prettier) -- [eslint](https://github.com/eslint/eslint) -- [vscode marketplace](https://marketplace.visualstudio.com/search?term=ES) -- [Express](https://expressjs.com/en/4x/api.html) -- [Socket.io](https://socket.io/) -- [Live coded example](https://github.com/ju5tu5/barebonechat) -- [Chat demo from socket.io](https://socket.io/get-started/chat/) -- [Cyclic](https://www.cyclic.sh/) - -### Extra challenges -Again, try to add extra features from the list below to your basic chat app. Do this one step at a time. Ask for help if you need it! **Note**:Again, update your readme zealously :P - -- Add support for nicknames. -- Broadcast a message to connected users when someone connects or disconnects. -- Don’t send the same message to the user that sent it. Instead, append the message directly as soon as he/she presses enter. -- Add “{user} is typing” functionality. -- Show who’s online. -- Add private messaging. - -*Extra challenge: Start working on your final app. [Look at the exercises from week 2](./week-2.md), pick an API and think of a strong concept. Start working on that instead of a barebone app*. - -*Extra challenge: Instead of socket.io, use the native [server-sent-events](https://www.voorhoede.nl/en/blog/real-time-communication-with-server-sent-events/)! It works very well although sending messages from client to server is trickier.* \ No newline at end of file diff --git a/course/week-2.md b/course/week-2.md deleted file mode 100644 index 7d290711e..000000000 --- a/course/week-2.md +++ /dev/null @@ -1,90 +0,0 @@ -# Assignments for week 2 - -## Intention -Last week you built a basic socket web app. The purpose was to learn about real-time communication using websockets. This week you’re going to take it to the next level and build a **meaningful** webapp that consumes an external source. -This week we will focus on client-server communication and data management. - -## Assignments -1. [(Proof of) Concept](#assignment-1-proof-of-concept) -2. [Proof of Concept](#assignment-2-proof-of-concept) - -## Assignment 1: (Proof of) Concept -Let’s create a concept! Why..? Because we always create a concept.. You can start either by writing the readme for a **meaningful** realtime application and then finding a matching API; or by looking at existing realtime APIs and finding meaningful realtime uses for them. Choose your approach and read the corresponding paragraph below. - -### Readme -We’re practicing RDD remember? Start by creating a vague outline for three concepts in your [README.md](../README.md). Make at least three variations so you can pick the best one later on. Make (pencil/paper) sketches to define basic usage, maybe you want to draw a happy little wireframe to demonstrate what you mean. Snap a photo with your phone and add your sketches to the [README.md](../README.md) file. Share your ideas with your teammates, try to find out which of your ideas has the most potential and choose wisely. - -Now.. try and describe your finished realtime app as close as possible without touching any code (we know this is hard but please try). Keep in mind: concepts are NOT final products. They are rough drafts that give an idea of how your **meaningful** realtime app could take shape. Here are some hints: -- What do you need to write to show how to use this? -- What features do you need to show so that other people understand what you are aiming for? -- How do you want your realtime app to be used? - -Make some more sketches, add comments, highlight core functionality etc. to make your concept clear as daylight. Again, snap a photo with your phone and add your sketches to the [README.md](../README.md) file. - -### Pick an API -Time to find an API that either carries your concept or that you can build a concept on (@cmd: remember project web last year?!) Here's a [list of some realtime APIs](https://docs.google.com/spreadsheets/d/1YKMTvdWVbzJ-CXDCHBEH2n3KofcQTN7EerTOEXy9MHI/edit?usp=sharing) we’ve collected for you. Let us know if you want to add any and I'll make you a collaborator. - -You could, for instance, use an [API that tracks the number of crypto currency transactions globally](https://www.coinapi.io/) and estimate their CO2 impact (per currency or per transaction). - -Or, you might use Amsterdam's [real-time open trash API](https://api.data.amsterdam.nl/afval/) to figure out which neighbourhoods produce the most (plastic) trash. - -You could even track a trend on twitter to show the status of an important development like the recent [#trashtag event](https://twitter.com/search?q=%23trashtag&src=typd) - -**Your external data source should be real-time (like a twitter feed).** If you want to build an app that uses a data source that can't be consumed in real-time (or by polling external data that changes regularly) there is an alternative. Create an app where you use a non real-time external source but where your users can manipulate the data model on your server in real time. Like this [drawing app made by Fenna de Wilde](https://live-draw.herokuapp.com/) or this [game made by Mees Rutten](https://github.com/meesrutten/real-time-web). If you don't use a real-time external data source, please check with a teacher if your concept is sufficient to pass the course. - -Pick a data source and define what you want to do. You can use a real-time source you find by yourself (be weary of OAuth, poorly documented, strict rate limiting etc.) or you can pick one from [our list](https://docs.google.com/spreadsheets/d/1YKMTvdWVbzJ-CXDCHBEH2n3KofcQTN7EerTOEXy9MHI/edit?usp=sharing). If you find outdated information in this list, please update it 🙏🏼. - -### Data modelling -Describe the API you intend to use, including it’s properties (rate-limiting, authorization method, API methods, etc.). Use [data modelling techniques](https://www.ibm.com/cloud/learn/data-modeling) to map the data in your API to get a grip on the structure of the data you will use. If you’ve picked a really extensive API you only need to map those parts you will actually use in your **meaningful** realtime app. Please start out by using a pencil and paper to model your data. Begin with a conceptual datamodel, adding more and more detail to move through a logical towards a physical data model. Don’t spend too much time on getting the details on relations... Snap a photo with your phone and add your sketches to the [README.md](../README.md) file. - -**Hint:** if you really want to go berserk on the data-model you can use your favourite drawing program or [https://asciiflow.com/](https://asciiflow.com/) if you want to let your inner nerd out (example below). - -``` -┌─────────────┐ ┌──────────┐ -│uniqueID:int ├──────────┤thisID:int│ -│whatever:str │ │what:str │ -│data:blob │ └──────────┘ -│resides:str │ -│in_your:date │ -│API:str │ -└─────────────┘ -``` - -### User testing?! -Test your concept against team- and/or squad-members. Ask what they think about your concept and if they have any additions of their own that can help you along. Write down everything they say and refine your concept.. Do this at least three times.. - -## Assignment 2: Proof of Concept -So you’ve got a concept? And a grip on your API’s data? Nice..! Your next step is testing if you can actually build a reliable frontend based on the chosen API. Start out by building a spike solution testing the core idea. Once you’ve made sure you can do this you get a grip on the data-flow in your **meaningful** realtime app by creating a data lifecycle diagram. Finally you can set out to create a nice, semantic and user friendly representation of the API data in your frontend. - -### Build a spike solution -A [spike solution](http://wiki.c2.com/?SpikeSolution) is a [well documented](http://wiki.c2.com/?SpikeDescribed) way of testing out a core concept in code. It’s usually a throw-out piece of code to make sure the idea you’ve got will work out. Let’s go! - -Reflect some of the data from the external API in a frontend view. The first step is to have your server consume data from the external source. Then you'll want to send that data to user. Finally, the frontend should deal with the data and show some HTML content. - -#### Resources -- [npm script](https://docs.npmjs.com/cli/run-script) -- [Express](https://expressjs.com/en/4x/api.html) -- [Socket.io](https://socket.io/) - -### Data lifecycle diagram -What data lives in your app universe? What are the entry points of data in your app? Where can the data be manipulated from? If you want to answer these questions, making a data lifecycle diagram can help. Check out [the slides]() if you need a recap and follow the steps described below. Here's an [overview of the symbols](https://www.gliffy.com/blog/how-to-flowchart-basic-symbols-part-1-of-3) you can use and their meaning. - -1. Draw each of the major components of your app (usual suspects: client, your server, external resource server) -2. Write under each component what’s inside its data model -3. Add lines for different pieces of data shared between components -4. Add information to your diagram showing the methods for getting and exposing data (e.g. pub-sub, request over socket, OAuth authorization, input validation) - -Add the model to your [README.md](../README.md). **We will check this during your assessment!** You may of course replace the sketch with a proper visualisation. What’s that? Of course you can use [https://asciiflow.com/](https://asciiflow.com/) you nerd... - -### Building a ‘nice’ frontend -Now that you have a *spike* that implements the core idea and grip on the *data lifecycle* for your **meaningful** realtime app, you can use everything you have learned in this and other courses to create an accessible, user-friendly, WCAG proof frontend for a realtime API. If you’ve got the frontend set-up, work on a way for your user to change the data on the server using sockets. - -### Extra challenges: -If you're certain you can meet the baseline for this course as per [the rubric](https://github.com/cmda-minor-web/real-time-web-2021#grading), these are good challenges to add: - -- Security (validate user requests server side) -- Mix different communication methods (SSE, XHR/Fetch, Sockets, Long Polling) -- Offline Support (what happens when the source is unavailable, what happens when the client temporarily loses their connection to your server?) -- Add publication subscription model too elegantly keep clients up to date -- Don't use socket.io for client server communication but try the native implementation instead. - diff --git a/course/week-3.md b/course/week-3.md deleted file mode 100644 index f01f0bd81..000000000 --- a/course/week-3.md +++ /dev/null @@ -1,31 +0,0 @@ -# Assignments for week 3 - -## Intention -Last week you started work on a **meaningful** webapp that consumes an external source. This week you will learn how to store and serve data from an external source to your own clients. We'll also provide extra challenges, it's up to you if you want to add fancy features or make the app more secure. Just be sure to check your features against [the rubric](https://github.com/cmda-minor-web/real-time-web-2021#grading) and to always properly test with multiple users. - -## Assignments -1. [Data management](#assignment-1-data-management) -2. [User testing](#assignment-2-user-testing) -3. [(Optional) Not ignoring the UI stack](#optional-assignment-3-not-ignoring-the-ui-stack) - -## Assignment 1: Data management -You probably want to persist data in a database (tunnel event, initial load, etc.) so set up some of way of storing the data. If you want to start out simple, store the data in-memory first (like an array of data items) and move it to a database later. - -Describe the chosen database system in your [README.md](../README.md). Make sure you only store the data you **actually need** for your application. This almost always involves cleaning and restructuring the data. For instance, if the external API you chose returns a complex object with confusing property names, use the functional programming trinity: [map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map), [filter](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter) and [reduce](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce) to change the data to your own format. - -## Resources -Databases: [levelup](https://github.com/level/levelup), [mongo](https://www.npmjs.com/package/mongodb), [redis](https://github.com/NodeRedis/node_redis), [postgres](https://github.com/brianc/node-postgres) - -Cleaning data: [array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array), [string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace), [object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object) - -## Assignment 2: User testing -Make sure that by Friday, your app works with at least three people connected (preferably more) at the same time. They will probably need different parts of your database so you will need to set up some server-side functionality that serves a specific part of your database depending on the type or request a clients sends. These types of requests like “getLatestData” or “sendMessage” form the basis of the API of *your* server. think about which methods/ events your server will have/ allow and describe them in your [README.md](../README.md). It’s okay if not all methods work yet but try to plan ahead, after all, we’re practicing RDD right?! - -## (Optional) Assignment 3: Not ignoring the UI stack -You’ve probably heard something about building robust interfaces [without ignoring the UI stack](https://www.scotthurff.com/posts/why-your-user-interface-is-awkward-youre-ignoring-the-ui-stack/). Try to address all states (blank, loading, partial, error, ideal) in your **meaningful** realtime webapp but take them on one state at a time. Use the scenario’s below to test if your app informs and helps the user to stay in control, even if his internet connection fails.. - -Build adequate user feedback for the following scenario's: -- The client can't reach the server. What does the user see? Can they still generate content which is served to the server when the connection resumes? Think about the tunnel event and how socket.io handles this out of the box. Should that behaviour change for your app? Think about queueing (real world) messages bot client and server side. -- The server can't reach the client. Should it do anything with its data model? Should other users be notified? -- The server can't reach the external source. Can the app still work? What should the end users see? - diff --git a/docs/css/generic.css b/docs/css/generic.css deleted file mode 100644 index 79f01b67f..000000000 --- a/docs/css/generic.css +++ /dev/null @@ -1,153 +0,0 @@ -:root { - --cmd-primary: #fff; - --cmd-secondary: #000; - --minor-one: #34a8e0; - --minor-two: #e9590e; - --minor-three: #3aaa34; - - --dark: #000; - --course: #01cd85; - --light: #fff; - - --groen: #01cd85; - --blauw: #01b4eb; - --geel: #f1e40a; -} - -* { - box-sizing: border-box; - scroll-behavior: smooth; -} - -html { - background-color: var(--groen); - background: linear-gradient(12deg, var(--groen) 46%, rgba(0, 0, 0, 0) 34%), - linear-gradient(21deg, var(--geel) 56%, var(--blauw) 46%); - background-size: cover; - background-attachment: fixed; - font-family: 'Open sans', Helvetica, Sans-Serif; - font-size: 1.3em; - line-height: 1.5em; - color: var(--cmd-primary); - padding: 0; - margin: 0; -} -body { - overflow-x: hidden; - padding: 0; - margin: 0; - transition: all 0.25s; -} -h1, -h2 { - font-size: 1.6em; - position: relative; -} -a { - color: var(--cmd-primary); -} -a:hover, -a:focus, -summary:hover, -summary:focus { - background: var(--cmd-primary); - color: var(--cmd-secondary); -} -pre { - overflow-x: hidden; -} -q { - display: block; - margin: 1em 0; - font-size: 1.1em; - font-style: italic; -} -p { - hyphens: auto; - hyphenate-limit-lines: 2; - -webkit-hyphenate-limit-before: 3; - -webkit-hyphenate-limit-after: 2; - hyphenate-limit-chars: 6 3 2; - hyphenate-limit-zone: 2em; - hyphenate-limit-last: always; -} -abbr { - hyphens: none; -} -i { - font-style: normal; -} -ul { - list-style: none; - padding-left: 0; -} - -/* -Header met het logo en de titel -*/ -body p { - max-width: 40em; -} -body > header { - padding: 1rem; -} -body > header h1 { - padding-top: 1rem; - line-height: 2; -} -svg { - margin-top: 0.5rem; -} -/* main */ -main { - transition: transform 0.25s ease-in; -} -main > header { - max-width: 30em; -} -section { - position: relative; - padding: 1rem; - display: flex; - flex-direction: column; - align-items: flex-start; -} - -/* footer */ -footer { - padding: 1em; - border-radius: 0.2em; - overflow-x: hidden; - margin-top: 4em; - color: var(--cmd-primary); -} -footer h2 { - padding: 0.2em 0.3em; - background-color: var(--cmd-primary); - color: #000; - display: inline-block; -} -footer h3 { - margin-left: 2em; -} -footer p { - max-width: 30em; -} -footer a { - color: var(--cmd-primary); -} -footer img { - background: white; - padding: 0.6em; - margin: 0; -} -footer img[title='wd'] { - width: 10.1em; - filter: brightness(1); -} -footer img[title='cmd'] { - width: 6.19em; -} -footer img[title='hva'] { - width: 12em; -} diff --git a/docs/css/styles.css b/docs/css/styles.css deleted file mode 100644 index e6b5972c2..000000000 --- a/docs/css/styles.css +++ /dev/null @@ -1,83 +0,0 @@ -html { - height: 100%; - background-color: #ff70c5; - background: repeating-conic-gradient( - from 0deg at 50% 120%, - #ff70c5 0deg 0.4deg, - #ffffff 0.4deg 4deg - ); - background-size: cover; - background-attachment: fixed; -} -body { - color: black; - font-weight: 400; -} -a { - color: black; -} -h1 { - font-size: 10vw; - margin: 0; - margin-top: 60vh; -} -h1 em { - font-size: 2rem; - display: block; -} -h2 { - font-size: 2em; - margin-top: 1.5em; -} -section > ul { - display: flex; - justify-content: space-between; - list-style-type: none; - padding: 0; - margin: 0 -1rem; -} - -section > ul li { - display: flex; - flex-direction: column; - align-items: center; - margin: 1rem; -} - -ul img { - border: 2px solid var(--light); - margin-top: 2em; - margin-bottom: 2em; -} -img { - max-width: 100%; -} -section#lecturers ul { - /* flex-wrap: wrap; */ -} -section#lecturers li { - width: 45%; - text-align: center; -} - -section#lecturers img { - border-radius: 100%; - margin-right: 1em; - width: 20vw; -} -section#lecturers a { - white-space: nowrap; -} -article { - margin: 2em -2rem; - background-color: rgba(255, 255, 255, 0.1); - padding: 2rem; -} - -.examples img { - max-width: 25vh; -} -footer { - font-style: italic; - color: #000000; -} diff --git a/docs/img/placeholder.png b/docs/img/placeholder.png deleted file mode 100644 index 41d321d9a..000000000 Binary files a/docs/img/placeholder.png and /dev/null differ diff --git a/docs/index.html b/docs/index.html deleted file mode 100644 index a51e955ba..000000000 --- a/docs/index.html +++ /dev/null @@ -1,65 +0,0 @@ - - - - - - - - - - Realtime Web - Minor Web Development - CMD Amsterdam - - - -
-

Realtime Web

-
-
-
-

Description

-

During this course you will learn how to build a real-time application. You will learn techniques to setup an open connection between the client and the server. This will enable you to send data in real-time both ways, at the same time.

-

Please read more about this course on -

Student work

- -
- --> - -
-

Lecturers

- -
- -
-

Program

-

This course is part of the minor Web Development, a semester program at Communication and Multimedia Design, a design bachelor focused on interactive digital products and services. CMD is part of the Faculty of Digital Media and Creative Industries at the Amsterdam University of Applied Sciences.

-
- -
-

Conduct

-

This course has a Code of Conduct. Anyone interacting with this repository, organisation, or community is bound by it. Staff and students of the Amsterdam University of Applied Sciences (Hogeschool van Amsterdam) are additionally bound by the Regulation Undesirable Conduct (Regeling Ongewenst Gedrag).

-
- - -
- - diff --git a/events/connection.js b/events/connection.js new file mode 100644 index 000000000..df99d48e1 --- /dev/null +++ b/events/connection.js @@ -0,0 +1,6 @@ +const handleConnection = (socket) => { + console.log('a user connected'); + }; + + export default handleConnection; + \ No newline at end of file diff --git a/events/disconnect.js b/events/disconnect.js new file mode 100644 index 000000000..ad285dafe --- /dev/null +++ b/events/disconnect.js @@ -0,0 +1,8 @@ +const handleDisconnect = (socket) => { + socket.on('disconnect', () => { + console.log('user disconnected :D'); + }); + }; + + export default handleDisconnect; + \ No newline at end of file diff --git a/events/message.js b/events/message.js new file mode 100644 index 000000000..802468689 --- /dev/null +++ b/events/message.js @@ -0,0 +1,9 @@ +const handleMessage = (socket, io) => { + socket.on('message', (message) => { + console.log('message:', message); + io.emit('message', message); + }); + }; + + export default handleMessage; + \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 000000000..0defa9c1b --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1281 @@ +{ + "name": "real-time-web-2223", + "version": "0.0.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "real-time-web-2223", + "version": "0.0.1", + "license": "MIT", + "dependencies": { + "dotenv": "^16.0.3", + "ejs": "^3.1.9", + "express": "^4.18.2", + "io": "^1.3.2", + "nodemon": "^2.0.22", + "socket.io": "^4.6.1" + } + }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", + "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==" + }, + "node_modules/@types/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==" + }, + "node_modules/@types/cors": { + "version": "2.8.13", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.13.tgz", + "integrity": "sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/node": { + "version": "18.15.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.11.tgz", + "integrity": "sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==" + }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "node_modules/async": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "engines": { + "node": "^4.5.0 || >= 5.9" + } + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/body-parser": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/dotenv": { + "version": "16.0.3", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", + "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "node_modules/ejs": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.9.tgz", + "integrity": "sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==", + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/engine.io": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.4.1.tgz", + "integrity": "sha512-JFYQurD/nbsA5BSPmbaOSLa3tSVj8L6o4srSwXXY3NqE+gGUNmmPTbhn8tjzcCtSqhFgIeqef81ngny8JM25hw==", + "dependencies": { + "@types/cookie": "^0.4.1", + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.4.1", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.0.3", + "ws": "~8.11.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/engine.io-parser": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.6.tgz", + "integrity": "sha512-tjuoZDMAdEhVnSFleYPCtdL2GXwVTGtNjoeJd9IhIG3C1xs9uwxqRNEu5WpnDZCaozwVlK/nuQhpodhXSIMaxw==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.1", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/express/node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "node_modules/get-intrinsic": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", + "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==" + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/io": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/io/-/io-1.3.2.tgz", + "integrity": "sha512-LPdyMRSOTEUOjs/HZtxxDM0S+tPa4Epfy/vXVvwTUXswd6w4q+ZoHPOF1D2d/4ZliRwUG1UoWdTcS1BG1m6nHQ==", + "dependencies": { + "mime": "^3.0.0" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/jake": { + "version": "10.8.5", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.5.tgz", + "integrity": "sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==", + "dependencies": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.1", + "minimatch": "^3.0.4" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", + "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/nodemon": { + "version": "2.0.22", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.22.tgz", + "integrity": "sha512-B8YqaKMmyuCO7BowF1Z1/mkPqLk6cs/l63Ojtd6otKjMx47Dq1utxfRxcavH1I7VSaL8n5BUaoutadnsX3AAVQ==", + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^3.2.7", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^5.7.1", + "simple-update-notifier": "^1.0.7", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=8.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/nodemon/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/nopt": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", + "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==", + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "*" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==" + }, + "node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/send/node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/simple-update-notifier": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz", + "integrity": "sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg==", + "dependencies": { + "semver": "~7.0.0" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/simple-update-notifier/node_modules/semver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/socket.io": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.6.1.tgz", + "integrity": "sha512-KMcaAi4l/8+xEjkRICl6ak8ySoxsYG+gG6/XfRCPJPQ/haCRIJBTL4wIl8YCsmtaBovcAXGLOShyVWQ/FG8GZA==", + "dependencies": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "debug": "~4.3.2", + "engine.io": "~6.4.1", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-adapter": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz", + "integrity": "sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA==", + "dependencies": { + "ws": "~8.11.0" + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.2.tgz", + "integrity": "sha512-DJtziuKypFkMMHCm2uIshOYC7QaylbtzQwiMYDuCKy3OPkjLzu4B2vAhTlqipRHHzrI0NJeBAizTK7X+6m1jVw==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/touch": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", + "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", + "dependencies": { + "nopt": "~1.0.10" + }, + "bin": { + "nodetouch": "bin/nodetouch.js" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/ws": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 000000000..e326c0c53 --- /dev/null +++ b/package.json @@ -0,0 +1,24 @@ +{ + "name": "real-time-web-2223", + "version": "0.0.1", + "description": "- [Synopsis](#synopsis)\r - [Description](#description)\r - [Communication](#communication)\r - [Goals](#goals)\r - [Grading](#grading)\r - [Programme](#programme)", + "main": "app.js", + "directories": { + "doc": "docs" + }, + "type": "module", + "scripts": { + "start": "node index.js", + "dev": "nodemon index.js" + }, + "author": "Jeffrey Ullers", + "license": "MIT", + "dependencies": { + "dotenv": "^16.0.3", + "ejs": "^3.1.9", + "express": "^4.18.2", + "io": "^1.3.2", + "nodemon": "^2.0.22", + "socket.io": "^4.6.1" + } +} diff --git a/public/js/app.js b/public/js/app.js new file mode 100644 index 000000000..da82f45f4 --- /dev/null +++ b/public/js/app.js @@ -0,0 +1,18 @@ +const socket = io(); +const messages = document.querySelector('section ul'); +const input = document.querySelector('input'); + +document.querySelector('form').addEventListener('submit', (event) => { + event.preventDefault(); + if (input.value) { + socket.emit('message', input.value); + input.value = ''; + } +}); + +socket.on('message', (message) => { + const element = document.createElement('li'); + element.textContent = message; + messages.appendChild(element); + messages.scrollTop = messages.scrollHeight; +}); \ No newline at end of file diff --git a/views/index.ejs b/views/index.ejs new file mode 100644 index 000000000..be3c1a24b --- /dev/null +++ b/views/index.ejs @@ -0,0 +1,12 @@ +<%- include('partials/header') %> +
+ +
+ +
+
+ + +
+ +<%- include('partials/footer') %> \ No newline at end of file diff --git a/views/partials/footer.ejs b/views/partials/footer.ejs new file mode 100644 index 000000000..586630da0 --- /dev/null +++ b/views/partials/footer.ejs @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/views/partials/header.ejs b/views/partials/header.ejs new file mode 100644 index 000000000..9e642b4df --- /dev/null +++ b/views/partials/header.ejs @@ -0,0 +1,11 @@ + + + + + + Modern ES6 Node.js Chat + + + + + \ No newline at end of file