├── 1-node-js-basics
├── Data
│ └── products.json
├── Files
│ ├── append.txt
│ ├── input.txt
│ ├── output.txt
│ └── start.txt
├── Modules
│ ├── replaceHtml.js
│ └── user.js
├── Template
│ ├── index.html
│ ├── product-details.html
│ └── product-list.html
├── app.js
├── package-lock.json
├── package.json
└── test.txt
├── 2-node-js-with-express
├── Controllers
│ └── moviesController.js
├── Routes
│ └── moviesRoutes.js
├── app.js
├── config.env
├── data
│ └── movies.json
├── package-lock.json
├── package.json
├── public
│ ├── css
│ │ └── style.css
│ └── templates
│ │ └── demo.html
├── server.js
└── test.txt
├── 3-mongodb-and-mongoose
├── Controllers
│ └── moviesController.js
├── Log
│ └── log.txt
├── Models
│ └── movieModel.js
├── Routes
│ └── moviesRoutes.js
├── Utils
│ └── ApiFeatures.js
├── app.js
├── config.env
├── data
│ ├── import-dev-data.js
│ └── movies.json
├── package-lock.json
├── package.json
├── public
│ ├── css
│ │ └── style.css
│ └── templates
│ │ └── demo.html
├── server.js
└── test.txt
└── 4-error-handling
├── Controllers
├── authController.js
├── errorController.js
└── moviesController.js
├── Log
└── log.txt
├── Models
├── movieModel.js
└── userModel.js
├── Routes
├── authRouter.js
└── moviesRoutes.js
├── Utils
├── ApiFeatures.js
├── CustomError.js
└── asyncErrorHandler.js
├── app.js
├── config.env
├── data
├── import-dev-data.js
└── movies.json
├── package-lock.json
├── package.json
├── public
├── css
│ └── style.css
└── templates
│ └── demo.html
├── server.js
└── test.txt
/1-node-js-basics/Data/products.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": 0,
4 | "name": "APPLE iPhone SE",
5 | "color":"Black",
6 | "ROM": 128,
7 | "price": 990,
8 | "modeName": "iPhone SE",
9 | "modelNumber": "MHGT3HN/A",
10 | "size": "11.94 cm (4.7 inch) Retina HD Display",
11 | "camera": "12MP Rear Camera | 7MP Front Camera",
12 | "Description": "Widescreen HD LCD Retina Multi-touch IPS Display (1400:1 Contrast Ratio (Typical), True Tone Display, Wide Color Display (P3), Haptic Touch, 625 nits Max Brightness (Typical), Fingerprint-resistant Oleophobic Coating, Display Zoom, Reachability)",
13 | "productImage":"http://atlas-content-cdn.pixelsquid.com/stock-images/iphone-x-smartphone-xwVXQLD-600.jpg"
14 | },
15 | {
16 | "id": 1,
17 | "name": "APPLE iPhone XR",
18 | "color":"White",
19 | "ROM": 64,
20 | "price": 790,
21 | "modeName": "iPhone XR",
22 | "modelNumber": "MH6N3HN/A",
23 | "size": "15.49 cm (6.1 inch) Display",
24 | "camera": "12MP Rear Camera | 7MP Front Camera",
25 | "Description": "1400:1 Contrast Ratio (Typical), True Tone Display (Six-channel Light Sensor), Wide Colour Display (P3), 625 nits Maximum Brightness (Typical), Fingerprint-resistant Oleophobic Coating, Support for Display of Multiple Languages and Characters Simultaneously, Liquid Retina HD Display, Tap to Wake, Wide Colour Gamut",
26 | "productImage":"https://c8.alamy.com/zooms/6/e98284ded5444c08949d7fd9f2bae166/2cd68c5.jpg"
27 | },
28 | {
29 | "id": 2,
30 | "name": "APPLE iPhone 11",
31 | "color":"White",
32 | "ROM": 64,
33 | "price": 740,
34 | "modeName": "iPhone 11",
35 | "modelNumber": "MHDC3HN/A",
36 | "size": "15.49 cm (6.1 inch) Liquid Retina HD",
37 | "camera": "12MP + 12MP | 12MP Front Camera",
38 | "Description": "1400:1 Contrast Ratio (Typical), True Tone Display, Wide Colour Display (P3), Haptic Touch, 625 nits Max Brightness (Typical), Fingerprint Resistant Oleophobic Coating, Support for Display of Multiple Languages and Characters Simultaneously",
39 | "productImage":"https://png.pngtree.com/png-vector/20201230/ourlarge/pngtree-smart-phone-vector-3d-style-mobile-design-mockup-png-image_2679535.jpg"
40 | },
41 | {
42 | "id": 3,
43 | "name": "APPLE iPhone XR",
44 | "color":"Yellow",
45 | "ROM": 128,
46 | "price": 999,
47 | "modeName": "iPhone XR",
48 | "modelNumber": "MHDC4HN/A",
49 | "size": "15.49 cm (6.1 inch) Liquid Retina HD",
50 | "camera": "12MP + 12MP | 12MP Front Camera",
51 | "Description": "1400:1 Contrast Ratio (Typical), True Tone Display, Wide Colour Display (P3), Haptic Touch, 625 nits Max Brightness (Typical), Fingerprint Resistant Oleophobic Coating, Support for Display of Multiple Languages and Characters Simultaneously",
52 | "productImage":"https://png.pngtree.com/png-vector/20201230/ourlarge/pngtree-vector-3d-style-smart-phone-mockup-design-png-image_2679529.jpg"
53 | },
54 | {
55 | "id": 4,
56 | "name": "APPLE iPhone 12 Mini",
57 | "color":"Purple",
58 | "ROM": 64,
59 | "price": 720,
60 | "modeName": "iPhone 12",
61 | "modelNumber": "MGJE3HN/A",
62 | "size": "13.72 cm (5.4 inch) Super Retina XDR Display",
63 | "camera": "12MP + 12MP | 12MP Front Camera",
64 | "Description": "Dive into a world of crystal-clear visuals with the Super Retina XDR Display of the iPhone 12 Mini. This beast of a smartphone packs the A14 Bionic chip to make for blazing-fast performance speeds. On top of that, its Dual-camera System, along with Night Mode, helps you click amazing pictures and selfies even when the lighting isn’t as good as you’d want it to be.",
65 | "productImage":"https://cdn1.vectorstock.com/i/1000x1000/70/05/realistic-frameless-smartphone-mock-up-3d-vector-33107005.jpg"
66 | },
67 | {
68 | "id": 5,
69 | "name": "APPLE iPhone 11",
70 | "color":"Red",
71 | "ROM": 128,
72 | "price": 990,
73 | "modeName": "iPhone 11",
74 | "modelNumber": "MHDC3HN/A",
75 | "size": "15.49 cm (6.1 inch) Liquid Retina HD",
76 | "camera": "12MP + 12MP | 12MP Front Camera",
77 | "Description": "1400:1 Contrast Ratio (Typical), True Tone Display, Wide Colour Display (P3), Haptic Touch, 625 nits Max Brightness (Typical), Fingerprint Resistant Oleophobic Coating, Support for Display of Multiple Languages and Characters Simultaneously",
78 | "productImage":"https://previews.123rf.com/images/barmaleeva/barmaleeva2003/barmaleeva200300004/141800762-white-realistic-smartphone-mockup-3d-mobile-phone-with-blank-white-screen-modern-vector-cell-phone-t.jpg"
79 | },
80 | {
81 | "id": 6,
82 | "name": "APPLE iPhone SE",
83 | "color":"White",
84 | "ROM": 64,
85 | "price": 780,
86 | "modeName": "iPhone SE",
87 | "modelNumber": "MHGT3HN/A",
88 | "size": "11.94 cm (4.7 inch) Retina HD Display",
89 | "camera": "12MP Rear Camera | 7MP Front Camera",
90 | "Description": "Widescreen HD LCD Retina Multi-touch IPS Display (1400:1 Contrast Ratio (Typical), True Tone Display, Wide Color Display (P3), Haptic Touch, 625 nits Max Brightness (Typical), Fingerprint-resistant Oleophobic Coating, Display Zoom, Reachability)",
91 | "productImage":"http://atlas-content-cdn.pixelsquid.com/stock-images/iphone-x-smartphone-xwVXQLD-600.jpg"
92 | },
93 | {
94 | "id": 7,
95 | "name": "APPLE iPhone 12",
96 | "color":"Blue",
97 | "ROM": 64,
98 | "price": 780,
99 | "modeName": "iPhone 12",
100 | "modelNumber": "MHGT3HN/A",
101 | "size": "11.94 cm (4.7 inch) Retina HD Display",
102 | "camera": "12MP Rear Camera | 7MP Front Camera",
103 | "Description": "Widescreen HD LCD Retina Multi-touch IPS Display (1400:1 Contrast Ratio (Typical), True Tone Display, Wide Color Display (P3), Haptic Touch, 625 nits Max Brightness (Typical), Fingerprint-resistant Oleophobic Coating, Display Zoom, Reachability)",
104 | "productImage":"https://cdn1.vectorstock.com/i/1000x1000/70/05/realistic-frameless-smartphone-mock-up-3d-vector-33107005.jpg"
105 | }
106 | ]
107 |
--------------------------------------------------------------------------------
/1-node-js-basics/Files/append.txt:
--------------------------------------------------------------------------------
1 | The content of this file will be appended to the content of input.txt and will be written in output.txt file.
--------------------------------------------------------------------------------
/1-node-js-basics/Files/input.txt:
--------------------------------------------------------------------------------
1 | This is a sample text file which we are going to read using NODE JS.
--------------------------------------------------------------------------------
/1-node-js-basics/Files/output.txt:
--------------------------------------------------------------------------------
1 | This is a sample text file which we are going to read using NODE JS.
2 |
3 | The content of this file will be appended to the content of input.txt and will be written in output.txt file.
4 |
5 | Date created Sun Sep 04 2022 10:00:39 GMT+0530 (India Standard Time)
--------------------------------------------------------------------------------
/1-node-js-basics/Files/start.txt:
--------------------------------------------------------------------------------
1 | input
--------------------------------------------------------------------------------
/1-node-js-basics/Modules/replaceHtml.js:
--------------------------------------------------------------------------------
1 | module.exports = function(template, product){
2 | let output = template.replace('{{%IMAGE%}}', product.productImage);
3 | output = output.replace('{{%NAME%}}', product.name);
4 | output = output.replace('{{%MODELNAME%}}', product.modeName);
5 | output = output.replace('{{%MODELNO%}}', product.modelNumber);
6 | output = output.replace('{{%SIZE%}}', product.size);
7 | output = output.replace('{{%CAMERA%}}', product.camera);
8 | output = output.replace('{{%PRICE%}}', product.price);
9 | output = output.replace('{{%COLOR%}}', product.color);
10 | output = output.replace('{{%ID%}}', product.id);
11 | output = output.replace('{{%ROM%}}', product.ROM);
12 | output = output.replace('{{%DESC%}}', product.Description);
13 |
14 | return output;
15 | }
--------------------------------------------------------------------------------
/1-node-js-basics/Modules/user.js:
--------------------------------------------------------------------------------
1 | const events = require('events');
2 |
3 | module.exports = class extends events.EventEmitter{
4 | constructor(){
5 | super();
6 | }
7 | }
--------------------------------------------------------------------------------
/1-node-js-basics/Template/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Node App
5 |
120 |
121 |
122 |
123 |
129 |
130 | {{%CONTENT%}}
131 |
132 |
133 |
134 |
--------------------------------------------------------------------------------
/1-node-js-basics/Template/product-details.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |

6 |
7 |
8 |
{{%NAME%}}
9 |
${{%PRICE%}}
10 |
11 |
Product Details
12 |
{{%SIZE%}}
13 |
{{%CAMERA%}}
14 |
Model Number{{%MODELNO%}}
15 |
Model Name{{%MODELNAME%}}
16 |
ROM{{%ROM%}} GB
17 |
18 |
19 |
Product Description
20 |
21 | {{%DESC%}}
22 |
23 |
24 |
25 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/1-node-js-basics/Template/product-list.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |

5 |
6 |
7 |
{{%NAME%}}
8 |
Model Name:{{%MODELNAME%}}
9 |
Model Number:{{%MODELNO%}}
10 |
Size:{{%SIZE%}}
11 |
Camera:{{%CAMERA%}}
12 |
13 |
14 |
15 |
Price: ${{%PRICE%}}
16 |
Color: {{%COLOR%}}
17 |
20 |
21 |
--------------------------------------------------------------------------------
/1-node-js-basics/app.js:
--------------------------------------------------------------------------------
1 | //CORE MODULES
2 | const readline = require('readline');
3 | const fs = require('fs');
4 | const http = require('http');
5 | const url = require('url');
6 | const events = require('events');
7 |
8 |
9 | //USER DEFINED MODULES
10 | const replaceHtml = require('./Modules/replaceHtml');
11 | const user = require('./Modules/user');
12 | const { Socket } = require('dgram');
13 |
14 | //THIRD PARTY MODULES / LIBRARIES
15 |
16 | /*LECTURE 4: CODE EXAMPLE************
17 | READING INPUT & WRITING OUTPUT
18 | *************************************
19 | const rl = readline.createInterface({
20 | input: process.stdin,
21 | output: process.stdout
22 | });
23 |
24 | rl.question("Please enter your naame: ", (name) => {
25 | console.log("You entered: "+name);
26 | rl.close();
27 | })
28 |
29 | rl.on('close', () => {
30 | console.log("Interface closed");
31 | process.exit(0);
32 | })*/
33 |
34 | /*LECTURE 5: CODE EXAMPLE************
35 | READING & WRITING TO A FILE
36 | *************************************
37 | let textIn = fs.readFileSync('./Files/input.txt', 'utf-8'); //10min
38 | console.log(textIn)
39 |
40 | let content = `Data read from input.txt: ${textIn}. \nDate created ${new Date()}`
41 | fs.writeFileSync('./Files/output.txt', content);*/
42 |
43 |
44 | /*LECTURE 7: CODE EXAMPLE**************
45 | READING & WRITING TO FILE ASYNCHRONOUSLY
46 | ***************************************
47 | fs.readFile('./Files/start.txt', 'utf-8', (error1, data1) => {
48 | console.log(data1)
49 | fs.readFile(`./Files/${data1}.txt`, 'utf-8', (error2, data2) => {
50 | console.log(data2);
51 | fs.readFile('./Files/append.txt', 'utf-8', (error3, data3) => {
52 | console.log(data3);
53 | fs.writeFile('./Files/output.txt', `${data2}\n\n${data3}\n\nDate created ${new Date()}`, () => {
54 | console.log('File writen successfully');
55 | });
56 | })
57 | })
58 | })
59 |
60 |
61 | console.log('Reading file....');*/
62 |
63 |
64 | /*LECTURE 8: CODE EXAMPLE**************
65 | CREATING A SIMPLE WEB SERVER
66 | ***************************************/
67 | const html = fs.readFileSync('./Template/index.html', 'utf-8')
68 | let products = JSON.parse(fs.readFileSync('./Data/products.json', 'utf-8'))
69 | let productListHtml = fs.readFileSync('./Template/product-list.html', 'utf-8');
70 | let productDetailHtml = fs.readFileSync('./Template/product-details.html', 'utf-8');
71 |
72 | // function replaceHtml(template, product){
73 | // let output = template.replace('{{%IMAGE%}}', product.productImage);
74 | // output = output.replace('{{%NAME%}}', product.name);
75 | // output = output.replace('{{%MODELNAME%}}', product.modeName);
76 | // output = output.replace('{{%MODELNO%}}', product.modelNumber);
77 | // output = output.replace('{{%SIZE%}}', product.size);
78 | // output = output.replace('{{%CAMERA%}}', product.camera);
79 | // output = output.replace('{{%PRICE%}}', product.price);
80 | // output = output.replace('{{%COLOR%}}', product.color);
81 | // output = output.replace('{{%ID%}}', product.id);
82 | // output = output.replace('{{%ROM%}}', product.ROM);
83 | // output = output.replace('{{%DESC%}}', product.Description);
84 |
85 | // return output;
86 | // }
87 | //STEP 1: CREATE A SERVER
88 | // const server = http.createServer((request, response) => {
89 | // let {query, pathname: path} = url.parse(request.url, true)
90 | // //console.log(x);
91 | // //let path = request.url;
92 |
93 | // if(path === '/' || path.toLocaleLowerCase() ==='/home'){
94 | // response.writeHead(200, {
95 | // 'Content-Type' : 'text/html',
96 | // 'my-header': 'Hellow, world'
97 | // });
98 | // response.end(html.replace('{{%CONTENT%}}', 'You are in Home page'));
99 | // } else if(path.toLocaleLowerCase() === '/about'){
100 | // response.writeHead(200, {
101 | // 'Content-Type' : 'text/html',
102 | // 'my-header': 'Hellow, world'
103 | // });
104 | // response.end(html.replace('{{%CONTENT%}}', 'You are in About page'));
105 | // } else if(path.toLocaleLowerCase() === '/contact'){
106 | // response.writeHead(200, {
107 | // 'Content-Type' : 'text/html',
108 | // 'my-header': 'Hellow, world'
109 | // });
110 | // response.end(html.replace('{{%CONTENT%}}', 'You are in Contact page'));
111 | // } else if(path.toLocaleLowerCase() === '/products'){
112 | // if(!query.id){
113 | // let productHtmlArray = products.map((prod) => {
114 | // return replaceHtml(productListHtml, prod);
115 | // })
116 | // let productResponseHtml = html.replace('{{%CONTENT%}}', productHtmlArray.join(','));
117 | // response.writeHead(200, {'Content-Type': 'text/html' });
118 | // response.end(productResponseHtml);
119 | // } else {
120 | // let prod = products[query.id]
121 | // let productDetailResponseHtml = replaceHtml(productDetailHtml, prod);
122 | // response.end(html.replace('{{%CONTENT%}}', productDetailResponseHtml));
123 | // }
124 | // } else {
125 | // response.writeHead(404, {
126 | // 'Content-Type' : 'text/html',
127 | // 'my-header': 'Hellow, world'
128 | // });
129 | // response.end(html.replace('{{%CONTENT%}}', 'Error 404: Page not found!'));
130 | // }
131 | // });
132 |
133 | /*LECTURE 20: CODE EXAMPLE**************
134 | UNDERSTANDING EVENT DRIVEN ARCHITECTURE
135 | ***************************************/
136 | //SERVER INHERITS FROM EVENTEMITTER
137 | //const server = http.createServer();
138 |
139 | // server.on('request', (request, response) => {
140 | // let {query, pathname: path} = url.parse(request.url, true)
141 | // //console.log(x);
142 | // //let path = request.url;
143 |
144 | // if(path === '/' || path.toLocaleLowerCase() ==='/home'){
145 | // response.writeHead(200, {
146 | // 'Content-Type' : 'text/html',
147 | // 'my-header': 'Hellow, world'
148 | // });
149 | // response.end(html.replace('{{%CONTENT%}}', 'You are in Home page'));
150 | // } else if(path.toLocaleLowerCase() === '/about'){
151 | // response.writeHead(200, {
152 | // 'Content-Type' : 'text/html',
153 | // 'my-header': 'Hellow, world'
154 | // });
155 | // response.end(html.replace('{{%CONTENT%}}', 'You are in About page'));
156 | // } else if(path.toLocaleLowerCase() === '/contact'){
157 | // response.writeHead(200, {
158 | // 'Content-Type' : 'text/html',
159 | // 'my-header': 'Hellow, world'
160 | // });
161 | // response.end(html.replace('{{%CONTENT%}}', 'You are in Contact page'));
162 | // } else if(path.toLocaleLowerCase() === '/products'){
163 | // if(!query.id){
164 | // let productHtmlArray = products.map((prod) => {
165 | // return replaceHtml(productListHtml, prod);
166 | // })
167 | // let productResponseHtml = html.replace('{{%CONTENT%}}', productHtmlArray.join(','));
168 | // response.writeHead(200, {'Content-Type': 'text/html' });
169 | // response.end(productResponseHtml);
170 | // } else {
171 | // let prod = products[query.id]
172 | // let productDetailResponseHtml = replaceHtml(productDetailHtml, prod);
173 | // response.end(html.replace('{{%CONTENT%}}', productDetailResponseHtml));
174 | // }
175 | // } else {
176 | // response.writeHead(404, {
177 | // 'Content-Type' : 'text/html',
178 | // 'my-header': 'Hellow, world'
179 | // });
180 | // response.end(html.replace('{{%CONTENT%}}', 'Error 404: Page not found!'));
181 | // }
182 | // })
183 |
184 | //STEP 2: START THE SERVER
185 | // server.listen(8000, '127.0.0.1', () => {
186 | // console.log('Server has started!');
187 | // })
188 |
189 | /*LECTURE 21: CODE EXAMPLE**************
190 | EMITTING & HANDLING CUSTOM EVENTS
191 | ***************************************/
192 | // let myEmitter = new user();
193 |
194 | // myEmitter.on('userCreated', (id, name) => {
195 | // console.log(`A new user ${name} with ID ${id} is created!`)
196 | // })
197 |
198 | // myEmitter.on('userCreated', (id, name) => {
199 | // console.log(`A new user ${name} with ID ${id} is added to database!`)
200 | // })
201 |
202 | // myEmitter.emit('userCreated', 101, 'John');
203 |
204 |
205 | /*LECTURE 23: CODE EXAMPLE**************
206 | UNDERSTANDING STREAMS IN PRACTICE
207 | ***************************************/
208 | //SOLUTION 1: WITHOUT READABLE OR WRITABLE STREAM
209 | // server.on('request', (req, res) =>{
210 | // fs.readFile('./Files/large-file.txt', (err, data) =>{
211 | // if(err){
212 | // res.end('Something went wrong!');
213 | // return;
214 | // }
215 | // res.end(data);
216 | // })
217 | // })
218 |
219 | //SOLUTION 2: USING READABLE & WRITABLE STREAM
220 | // server.on('request', (req, res) =>{
221 | // let rs = fs.createReadStream('./Files/large-file.txt');
222 |
223 | // rs.on('data', (chunk) => {
224 | // res.write(chunk)
225 | // })
226 |
227 | // rs.on('end', () => {
228 | // res.end();
229 | // })
230 |
231 | // rs.on('error', (error) => {
232 | // res.end(error.message);
233 | // })
234 | // })
235 |
236 | /*LECTURE 24: CODE EXAMPLE**************
237 | UNDERSTANDING PIPE() METHOD
238 | ***************************************/
239 | //SOLUTION 3: USING PIPE METHOD
240 | // server.on('request', (req, res) => {
241 | // let rs = fs.createReadStream('./Files/large-file.txt');
242 | // rs.pipe(res);
243 | // //redableSource.pipe(writableDest)
244 | // })
245 |
246 | //console.log('Nodemon is working')
247 |
248 |
249 | /*LECTURE 29: CODE EXAMPLE**************
250 | EVENT LOOP IN PRACTICE
251 | ***************************************/
252 | console.log('Program has started')
253 |
254 | //STORED - 2ND PHASE
255 | fs.readFile('./Files/input.txt', () => {
256 | console.log('File read complete!');
257 |
258 | //STORED IN - 1ST PHASE
259 | setTimeout(() => {
260 | console.log('Timer callback executed')
261 | }, 0);
262 |
263 | //STORED IN - 3RD PHASE
264 | setImmediate(() => {console.log('SetImmediate callback executed')});
265 |
266 | process.nextTick(() => {console.log('Process.nextTick callback executed')})
267 | })
268 |
269 | console.log('Program has completed')
--------------------------------------------------------------------------------
/1-node-js-basics/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "node-js-basic",
3 | "version": "1.0.0",
4 | "lockfileVersion": 2,
5 | "requires": true,
6 | "packages": {
7 | "": {
8 | "name": "node-js-basic",
9 | "version": "1.0.0",
10 | "license": "ISC",
11 | "dependencies": {
12 | "express": "^4.18.1"
13 | },
14 | "devDependencies": {
15 | "nodemon": "^2.0.20"
16 | }
17 | },
18 | "node_modules/abbrev": {
19 | "version": "1.1.1",
20 | "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
21 | "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
22 | "dev": true
23 | },
24 | "node_modules/accepts": {
25 | "version": "1.3.8",
26 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
27 | "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
28 | "dependencies": {
29 | "mime-types": "~2.1.34",
30 | "negotiator": "0.6.3"
31 | },
32 | "engines": {
33 | "node": ">= 0.6"
34 | }
35 | },
36 | "node_modules/anymatch": {
37 | "version": "3.1.2",
38 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
39 | "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
40 | "dev": true,
41 | "dependencies": {
42 | "normalize-path": "^3.0.0",
43 | "picomatch": "^2.0.4"
44 | },
45 | "engines": {
46 | "node": ">= 8"
47 | }
48 | },
49 | "node_modules/array-flatten": {
50 | "version": "1.1.1",
51 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
52 | "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
53 | },
54 | "node_modules/balanced-match": {
55 | "version": "1.0.2",
56 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
57 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
58 | "dev": true
59 | },
60 | "node_modules/binary-extensions": {
61 | "version": "2.2.0",
62 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
63 | "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
64 | "dev": true,
65 | "engines": {
66 | "node": ">=8"
67 | }
68 | },
69 | "node_modules/body-parser": {
70 | "version": "1.20.0",
71 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz",
72 | "integrity": "sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==",
73 | "dependencies": {
74 | "bytes": "3.1.2",
75 | "content-type": "~1.0.4",
76 | "debug": "2.6.9",
77 | "depd": "2.0.0",
78 | "destroy": "1.2.0",
79 | "http-errors": "2.0.0",
80 | "iconv-lite": "0.4.24",
81 | "on-finished": "2.4.1",
82 | "qs": "6.10.3",
83 | "raw-body": "2.5.1",
84 | "type-is": "~1.6.18",
85 | "unpipe": "1.0.0"
86 | },
87 | "engines": {
88 | "node": ">= 0.8",
89 | "npm": "1.2.8000 || >= 1.4.16"
90 | }
91 | },
92 | "node_modules/brace-expansion": {
93 | "version": "1.1.11",
94 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
95 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
96 | "dev": true,
97 | "dependencies": {
98 | "balanced-match": "^1.0.0",
99 | "concat-map": "0.0.1"
100 | }
101 | },
102 | "node_modules/braces": {
103 | "version": "3.0.2",
104 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
105 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
106 | "dev": true,
107 | "dependencies": {
108 | "fill-range": "^7.0.1"
109 | },
110 | "engines": {
111 | "node": ">=8"
112 | }
113 | },
114 | "node_modules/bytes": {
115 | "version": "3.1.2",
116 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
117 | "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
118 | "engines": {
119 | "node": ">= 0.8"
120 | }
121 | },
122 | "node_modules/call-bind": {
123 | "version": "1.0.2",
124 | "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
125 | "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
126 | "dependencies": {
127 | "function-bind": "^1.1.1",
128 | "get-intrinsic": "^1.0.2"
129 | },
130 | "funding": {
131 | "url": "https://github.com/sponsors/ljharb"
132 | }
133 | },
134 | "node_modules/chokidar": {
135 | "version": "3.5.3",
136 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
137 | "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
138 | "dev": true,
139 | "funding": [
140 | {
141 | "type": "individual",
142 | "url": "https://paulmillr.com/funding/"
143 | }
144 | ],
145 | "dependencies": {
146 | "anymatch": "~3.1.2",
147 | "braces": "~3.0.2",
148 | "glob-parent": "~5.1.2",
149 | "is-binary-path": "~2.1.0",
150 | "is-glob": "~4.0.1",
151 | "normalize-path": "~3.0.0",
152 | "readdirp": "~3.6.0"
153 | },
154 | "engines": {
155 | "node": ">= 8.10.0"
156 | },
157 | "optionalDependencies": {
158 | "fsevents": "~2.3.2"
159 | }
160 | },
161 | "node_modules/concat-map": {
162 | "version": "0.0.1",
163 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
164 | "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
165 | "dev": true
166 | },
167 | "node_modules/content-disposition": {
168 | "version": "0.5.4",
169 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
170 | "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
171 | "dependencies": {
172 | "safe-buffer": "5.2.1"
173 | },
174 | "engines": {
175 | "node": ">= 0.6"
176 | }
177 | },
178 | "node_modules/content-type": {
179 | "version": "1.0.4",
180 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
181 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==",
182 | "engines": {
183 | "node": ">= 0.6"
184 | }
185 | },
186 | "node_modules/cookie": {
187 | "version": "0.5.0",
188 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
189 | "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==",
190 | "engines": {
191 | "node": ">= 0.6"
192 | }
193 | },
194 | "node_modules/cookie-signature": {
195 | "version": "1.0.6",
196 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
197 | "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
198 | },
199 | "node_modules/debug": {
200 | "version": "2.6.9",
201 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
202 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
203 | "dependencies": {
204 | "ms": "2.0.0"
205 | }
206 | },
207 | "node_modules/depd": {
208 | "version": "2.0.0",
209 | "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
210 | "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
211 | "engines": {
212 | "node": ">= 0.8"
213 | }
214 | },
215 | "node_modules/destroy": {
216 | "version": "1.2.0",
217 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
218 | "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
219 | "engines": {
220 | "node": ">= 0.8",
221 | "npm": "1.2.8000 || >= 1.4.16"
222 | }
223 | },
224 | "node_modules/ee-first": {
225 | "version": "1.1.1",
226 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
227 | "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
228 | },
229 | "node_modules/encodeurl": {
230 | "version": "1.0.2",
231 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
232 | "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
233 | "engines": {
234 | "node": ">= 0.8"
235 | }
236 | },
237 | "node_modules/escape-html": {
238 | "version": "1.0.3",
239 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
240 | "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
241 | },
242 | "node_modules/etag": {
243 | "version": "1.8.1",
244 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
245 | "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
246 | "engines": {
247 | "node": ">= 0.6"
248 | }
249 | },
250 | "node_modules/express": {
251 | "version": "4.18.1",
252 | "resolved": "https://registry.npmjs.org/express/-/express-4.18.1.tgz",
253 | "integrity": "sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q==",
254 | "dependencies": {
255 | "accepts": "~1.3.8",
256 | "array-flatten": "1.1.1",
257 | "body-parser": "1.20.0",
258 | "content-disposition": "0.5.4",
259 | "content-type": "~1.0.4",
260 | "cookie": "0.5.0",
261 | "cookie-signature": "1.0.6",
262 | "debug": "2.6.9",
263 | "depd": "2.0.0",
264 | "encodeurl": "~1.0.2",
265 | "escape-html": "~1.0.3",
266 | "etag": "~1.8.1",
267 | "finalhandler": "1.2.0",
268 | "fresh": "0.5.2",
269 | "http-errors": "2.0.0",
270 | "merge-descriptors": "1.0.1",
271 | "methods": "~1.1.2",
272 | "on-finished": "2.4.1",
273 | "parseurl": "~1.3.3",
274 | "path-to-regexp": "0.1.7",
275 | "proxy-addr": "~2.0.7",
276 | "qs": "6.10.3",
277 | "range-parser": "~1.2.1",
278 | "safe-buffer": "5.2.1",
279 | "send": "0.18.0",
280 | "serve-static": "1.15.0",
281 | "setprototypeof": "1.2.0",
282 | "statuses": "2.0.1",
283 | "type-is": "~1.6.18",
284 | "utils-merge": "1.0.1",
285 | "vary": "~1.1.2"
286 | },
287 | "engines": {
288 | "node": ">= 0.10.0"
289 | }
290 | },
291 | "node_modules/fill-range": {
292 | "version": "7.0.1",
293 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
294 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
295 | "dev": true,
296 | "dependencies": {
297 | "to-regex-range": "^5.0.1"
298 | },
299 | "engines": {
300 | "node": ">=8"
301 | }
302 | },
303 | "node_modules/finalhandler": {
304 | "version": "1.2.0",
305 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
306 | "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==",
307 | "dependencies": {
308 | "debug": "2.6.9",
309 | "encodeurl": "~1.0.2",
310 | "escape-html": "~1.0.3",
311 | "on-finished": "2.4.1",
312 | "parseurl": "~1.3.3",
313 | "statuses": "2.0.1",
314 | "unpipe": "~1.0.0"
315 | },
316 | "engines": {
317 | "node": ">= 0.8"
318 | }
319 | },
320 | "node_modules/forwarded": {
321 | "version": "0.2.0",
322 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
323 | "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
324 | "engines": {
325 | "node": ">= 0.6"
326 | }
327 | },
328 | "node_modules/fresh": {
329 | "version": "0.5.2",
330 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
331 | "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
332 | "engines": {
333 | "node": ">= 0.6"
334 | }
335 | },
336 | "node_modules/fsevents": {
337 | "version": "2.3.2",
338 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
339 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
340 | "dev": true,
341 | "hasInstallScript": true,
342 | "optional": true,
343 | "os": [
344 | "darwin"
345 | ],
346 | "engines": {
347 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
348 | }
349 | },
350 | "node_modules/function-bind": {
351 | "version": "1.1.1",
352 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
353 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
354 | },
355 | "node_modules/get-intrinsic": {
356 | "version": "1.1.3",
357 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz",
358 | "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==",
359 | "dependencies": {
360 | "function-bind": "^1.1.1",
361 | "has": "^1.0.3",
362 | "has-symbols": "^1.0.3"
363 | },
364 | "funding": {
365 | "url": "https://github.com/sponsors/ljharb"
366 | }
367 | },
368 | "node_modules/glob-parent": {
369 | "version": "5.1.2",
370 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
371 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
372 | "dev": true,
373 | "dependencies": {
374 | "is-glob": "^4.0.1"
375 | },
376 | "engines": {
377 | "node": ">= 6"
378 | }
379 | },
380 | "node_modules/has": {
381 | "version": "1.0.3",
382 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
383 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
384 | "dependencies": {
385 | "function-bind": "^1.1.1"
386 | },
387 | "engines": {
388 | "node": ">= 0.4.0"
389 | }
390 | },
391 | "node_modules/has-flag": {
392 | "version": "3.0.0",
393 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
394 | "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
395 | "dev": true,
396 | "engines": {
397 | "node": ">=4"
398 | }
399 | },
400 | "node_modules/has-symbols": {
401 | "version": "1.0.3",
402 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
403 | "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
404 | "engines": {
405 | "node": ">= 0.4"
406 | },
407 | "funding": {
408 | "url": "https://github.com/sponsors/ljharb"
409 | }
410 | },
411 | "node_modules/http-errors": {
412 | "version": "2.0.0",
413 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
414 | "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
415 | "dependencies": {
416 | "depd": "2.0.0",
417 | "inherits": "2.0.4",
418 | "setprototypeof": "1.2.0",
419 | "statuses": "2.0.1",
420 | "toidentifier": "1.0.1"
421 | },
422 | "engines": {
423 | "node": ">= 0.8"
424 | }
425 | },
426 | "node_modules/iconv-lite": {
427 | "version": "0.4.24",
428 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
429 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
430 | "dependencies": {
431 | "safer-buffer": ">= 2.1.2 < 3"
432 | },
433 | "engines": {
434 | "node": ">=0.10.0"
435 | }
436 | },
437 | "node_modules/ignore-by-default": {
438 | "version": "1.0.1",
439 | "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz",
440 | "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==",
441 | "dev": true
442 | },
443 | "node_modules/inherits": {
444 | "version": "2.0.4",
445 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
446 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
447 | },
448 | "node_modules/ipaddr.js": {
449 | "version": "1.9.1",
450 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
451 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
452 | "engines": {
453 | "node": ">= 0.10"
454 | }
455 | },
456 | "node_modules/is-binary-path": {
457 | "version": "2.1.0",
458 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
459 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
460 | "dev": true,
461 | "dependencies": {
462 | "binary-extensions": "^2.0.0"
463 | },
464 | "engines": {
465 | "node": ">=8"
466 | }
467 | },
468 | "node_modules/is-extglob": {
469 | "version": "2.1.1",
470 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
471 | "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
472 | "dev": true,
473 | "engines": {
474 | "node": ">=0.10.0"
475 | }
476 | },
477 | "node_modules/is-glob": {
478 | "version": "4.0.3",
479 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
480 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
481 | "dev": true,
482 | "dependencies": {
483 | "is-extglob": "^2.1.1"
484 | },
485 | "engines": {
486 | "node": ">=0.10.0"
487 | }
488 | },
489 | "node_modules/is-number": {
490 | "version": "7.0.0",
491 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
492 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
493 | "dev": true,
494 | "engines": {
495 | "node": ">=0.12.0"
496 | }
497 | },
498 | "node_modules/media-typer": {
499 | "version": "0.3.0",
500 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
501 | "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
502 | "engines": {
503 | "node": ">= 0.6"
504 | }
505 | },
506 | "node_modules/merge-descriptors": {
507 | "version": "1.0.1",
508 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
509 | "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w=="
510 | },
511 | "node_modules/methods": {
512 | "version": "1.1.2",
513 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
514 | "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
515 | "engines": {
516 | "node": ">= 0.6"
517 | }
518 | },
519 | "node_modules/mime": {
520 | "version": "1.6.0",
521 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
522 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
523 | "bin": {
524 | "mime": "cli.js"
525 | },
526 | "engines": {
527 | "node": ">=4"
528 | }
529 | },
530 | "node_modules/mime-db": {
531 | "version": "1.52.0",
532 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
533 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
534 | "engines": {
535 | "node": ">= 0.6"
536 | }
537 | },
538 | "node_modules/mime-types": {
539 | "version": "2.1.35",
540 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
541 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
542 | "dependencies": {
543 | "mime-db": "1.52.0"
544 | },
545 | "engines": {
546 | "node": ">= 0.6"
547 | }
548 | },
549 | "node_modules/minimatch": {
550 | "version": "3.1.2",
551 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
552 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
553 | "dev": true,
554 | "dependencies": {
555 | "brace-expansion": "^1.1.7"
556 | },
557 | "engines": {
558 | "node": "*"
559 | }
560 | },
561 | "node_modules/ms": {
562 | "version": "2.0.0",
563 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
564 | "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
565 | },
566 | "node_modules/negotiator": {
567 | "version": "0.6.3",
568 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
569 | "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
570 | "engines": {
571 | "node": ">= 0.6"
572 | }
573 | },
574 | "node_modules/nodemon": {
575 | "version": "2.0.20",
576 | "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.20.tgz",
577 | "integrity": "sha512-Km2mWHKKY5GzRg6i1j5OxOHQtuvVsgskLfigG25yTtbyfRGn/GNvIbRyOf1PSCKJ2aT/58TiuUsuOU5UToVViw==",
578 | "dev": true,
579 | "dependencies": {
580 | "chokidar": "^3.5.2",
581 | "debug": "^3.2.7",
582 | "ignore-by-default": "^1.0.1",
583 | "minimatch": "^3.1.2",
584 | "pstree.remy": "^1.1.8",
585 | "semver": "^5.7.1",
586 | "simple-update-notifier": "^1.0.7",
587 | "supports-color": "^5.5.0",
588 | "touch": "^3.1.0",
589 | "undefsafe": "^2.0.5"
590 | },
591 | "bin": {
592 | "nodemon": "bin/nodemon.js"
593 | },
594 | "engines": {
595 | "node": ">=8.10.0"
596 | },
597 | "funding": {
598 | "type": "opencollective",
599 | "url": "https://opencollective.com/nodemon"
600 | }
601 | },
602 | "node_modules/nodemon/node_modules/debug": {
603 | "version": "3.2.7",
604 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
605 | "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
606 | "dev": true,
607 | "dependencies": {
608 | "ms": "^2.1.1"
609 | }
610 | },
611 | "node_modules/nodemon/node_modules/ms": {
612 | "version": "2.1.3",
613 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
614 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
615 | "dev": true
616 | },
617 | "node_modules/nopt": {
618 | "version": "1.0.10",
619 | "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz",
620 | "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==",
621 | "dev": true,
622 | "dependencies": {
623 | "abbrev": "1"
624 | },
625 | "bin": {
626 | "nopt": "bin/nopt.js"
627 | },
628 | "engines": {
629 | "node": "*"
630 | }
631 | },
632 | "node_modules/normalize-path": {
633 | "version": "3.0.0",
634 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
635 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
636 | "dev": true,
637 | "engines": {
638 | "node": ">=0.10.0"
639 | }
640 | },
641 | "node_modules/object-inspect": {
642 | "version": "1.12.2",
643 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz",
644 | "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==",
645 | "funding": {
646 | "url": "https://github.com/sponsors/ljharb"
647 | }
648 | },
649 | "node_modules/on-finished": {
650 | "version": "2.4.1",
651 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
652 | "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
653 | "dependencies": {
654 | "ee-first": "1.1.1"
655 | },
656 | "engines": {
657 | "node": ">= 0.8"
658 | }
659 | },
660 | "node_modules/parseurl": {
661 | "version": "1.3.3",
662 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
663 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
664 | "engines": {
665 | "node": ">= 0.8"
666 | }
667 | },
668 | "node_modules/path-to-regexp": {
669 | "version": "0.1.7",
670 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
671 | "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
672 | },
673 | "node_modules/picomatch": {
674 | "version": "2.3.1",
675 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
676 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
677 | "dev": true,
678 | "engines": {
679 | "node": ">=8.6"
680 | },
681 | "funding": {
682 | "url": "https://github.com/sponsors/jonschlinkert"
683 | }
684 | },
685 | "node_modules/proxy-addr": {
686 | "version": "2.0.7",
687 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
688 | "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
689 | "dependencies": {
690 | "forwarded": "0.2.0",
691 | "ipaddr.js": "1.9.1"
692 | },
693 | "engines": {
694 | "node": ">= 0.10"
695 | }
696 | },
697 | "node_modules/pstree.remy": {
698 | "version": "1.1.8",
699 | "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz",
700 | "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==",
701 | "dev": true
702 | },
703 | "node_modules/qs": {
704 | "version": "6.10.3",
705 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz",
706 | "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==",
707 | "dependencies": {
708 | "side-channel": "^1.0.4"
709 | },
710 | "engines": {
711 | "node": ">=0.6"
712 | },
713 | "funding": {
714 | "url": "https://github.com/sponsors/ljharb"
715 | }
716 | },
717 | "node_modules/range-parser": {
718 | "version": "1.2.1",
719 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
720 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
721 | "engines": {
722 | "node": ">= 0.6"
723 | }
724 | },
725 | "node_modules/raw-body": {
726 | "version": "2.5.1",
727 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
728 | "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
729 | "dependencies": {
730 | "bytes": "3.1.2",
731 | "http-errors": "2.0.0",
732 | "iconv-lite": "0.4.24",
733 | "unpipe": "1.0.0"
734 | },
735 | "engines": {
736 | "node": ">= 0.8"
737 | }
738 | },
739 | "node_modules/readdirp": {
740 | "version": "3.6.0",
741 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
742 | "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
743 | "dev": true,
744 | "dependencies": {
745 | "picomatch": "^2.2.1"
746 | },
747 | "engines": {
748 | "node": ">=8.10.0"
749 | }
750 | },
751 | "node_modules/safe-buffer": {
752 | "version": "5.2.1",
753 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
754 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
755 | "funding": [
756 | {
757 | "type": "github",
758 | "url": "https://github.com/sponsors/feross"
759 | },
760 | {
761 | "type": "patreon",
762 | "url": "https://www.patreon.com/feross"
763 | },
764 | {
765 | "type": "consulting",
766 | "url": "https://feross.org/support"
767 | }
768 | ]
769 | },
770 | "node_modules/safer-buffer": {
771 | "version": "2.1.2",
772 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
773 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
774 | },
775 | "node_modules/semver": {
776 | "version": "5.7.1",
777 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
778 | "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
779 | "dev": true,
780 | "bin": {
781 | "semver": "bin/semver"
782 | }
783 | },
784 | "node_modules/send": {
785 | "version": "0.18.0",
786 | "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
787 | "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
788 | "dependencies": {
789 | "debug": "2.6.9",
790 | "depd": "2.0.0",
791 | "destroy": "1.2.0",
792 | "encodeurl": "~1.0.2",
793 | "escape-html": "~1.0.3",
794 | "etag": "~1.8.1",
795 | "fresh": "0.5.2",
796 | "http-errors": "2.0.0",
797 | "mime": "1.6.0",
798 | "ms": "2.1.3",
799 | "on-finished": "2.4.1",
800 | "range-parser": "~1.2.1",
801 | "statuses": "2.0.1"
802 | },
803 | "engines": {
804 | "node": ">= 0.8.0"
805 | }
806 | },
807 | "node_modules/send/node_modules/ms": {
808 | "version": "2.1.3",
809 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
810 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
811 | },
812 | "node_modules/serve-static": {
813 | "version": "1.15.0",
814 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz",
815 | "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==",
816 | "dependencies": {
817 | "encodeurl": "~1.0.2",
818 | "escape-html": "~1.0.3",
819 | "parseurl": "~1.3.3",
820 | "send": "0.18.0"
821 | },
822 | "engines": {
823 | "node": ">= 0.8.0"
824 | }
825 | },
826 | "node_modules/setprototypeof": {
827 | "version": "1.2.0",
828 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
829 | "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
830 | },
831 | "node_modules/side-channel": {
832 | "version": "1.0.4",
833 | "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
834 | "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
835 | "dependencies": {
836 | "call-bind": "^1.0.0",
837 | "get-intrinsic": "^1.0.2",
838 | "object-inspect": "^1.9.0"
839 | },
840 | "funding": {
841 | "url": "https://github.com/sponsors/ljharb"
842 | }
843 | },
844 | "node_modules/simple-update-notifier": {
845 | "version": "1.0.7",
846 | "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.0.7.tgz",
847 | "integrity": "sha512-BBKgR84BJQJm6WjWFMHgLVuo61FBDSj1z/xSFUIozqO6wO7ii0JxCqlIud7Enr/+LhlbNI0whErq96P2qHNWew==",
848 | "dev": true,
849 | "dependencies": {
850 | "semver": "~7.0.0"
851 | },
852 | "engines": {
853 | "node": ">=8.10.0"
854 | }
855 | },
856 | "node_modules/simple-update-notifier/node_modules/semver": {
857 | "version": "7.0.0",
858 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz",
859 | "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==",
860 | "dev": true,
861 | "bin": {
862 | "semver": "bin/semver.js"
863 | }
864 | },
865 | "node_modules/statuses": {
866 | "version": "2.0.1",
867 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
868 | "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
869 | "engines": {
870 | "node": ">= 0.8"
871 | }
872 | },
873 | "node_modules/supports-color": {
874 | "version": "5.5.0",
875 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
876 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
877 | "dev": true,
878 | "dependencies": {
879 | "has-flag": "^3.0.0"
880 | },
881 | "engines": {
882 | "node": ">=4"
883 | }
884 | },
885 | "node_modules/to-regex-range": {
886 | "version": "5.0.1",
887 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
888 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
889 | "dev": true,
890 | "dependencies": {
891 | "is-number": "^7.0.0"
892 | },
893 | "engines": {
894 | "node": ">=8.0"
895 | }
896 | },
897 | "node_modules/toidentifier": {
898 | "version": "1.0.1",
899 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
900 | "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
901 | "engines": {
902 | "node": ">=0.6"
903 | }
904 | },
905 | "node_modules/touch": {
906 | "version": "3.1.0",
907 | "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz",
908 | "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==",
909 | "dev": true,
910 | "dependencies": {
911 | "nopt": "~1.0.10"
912 | },
913 | "bin": {
914 | "nodetouch": "bin/nodetouch.js"
915 | }
916 | },
917 | "node_modules/type-is": {
918 | "version": "1.6.18",
919 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
920 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
921 | "dependencies": {
922 | "media-typer": "0.3.0",
923 | "mime-types": "~2.1.24"
924 | },
925 | "engines": {
926 | "node": ">= 0.6"
927 | }
928 | },
929 | "node_modules/undefsafe": {
930 | "version": "2.0.5",
931 | "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
932 | "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==",
933 | "dev": true
934 | },
935 | "node_modules/unpipe": {
936 | "version": "1.0.0",
937 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
938 | "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
939 | "engines": {
940 | "node": ">= 0.8"
941 | }
942 | },
943 | "node_modules/utils-merge": {
944 | "version": "1.0.1",
945 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
946 | "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
947 | "engines": {
948 | "node": ">= 0.4.0"
949 | }
950 | },
951 | "node_modules/vary": {
952 | "version": "1.1.2",
953 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
954 | "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
955 | "engines": {
956 | "node": ">= 0.8"
957 | }
958 | }
959 | },
960 | "dependencies": {
961 | "abbrev": {
962 | "version": "1.1.1",
963 | "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
964 | "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
965 | "dev": true
966 | },
967 | "accepts": {
968 | "version": "1.3.8",
969 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
970 | "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
971 | "requires": {
972 | "mime-types": "~2.1.34",
973 | "negotiator": "0.6.3"
974 | }
975 | },
976 | "anymatch": {
977 | "version": "3.1.2",
978 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
979 | "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
980 | "dev": true,
981 | "requires": {
982 | "normalize-path": "^3.0.0",
983 | "picomatch": "^2.0.4"
984 | }
985 | },
986 | "array-flatten": {
987 | "version": "1.1.1",
988 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
989 | "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
990 | },
991 | "balanced-match": {
992 | "version": "1.0.2",
993 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
994 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
995 | "dev": true
996 | },
997 | "binary-extensions": {
998 | "version": "2.2.0",
999 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
1000 | "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
1001 | "dev": true
1002 | },
1003 | "body-parser": {
1004 | "version": "1.20.0",
1005 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz",
1006 | "integrity": "sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==",
1007 | "requires": {
1008 | "bytes": "3.1.2",
1009 | "content-type": "~1.0.4",
1010 | "debug": "2.6.9",
1011 | "depd": "2.0.0",
1012 | "destroy": "1.2.0",
1013 | "http-errors": "2.0.0",
1014 | "iconv-lite": "0.4.24",
1015 | "on-finished": "2.4.1",
1016 | "qs": "6.10.3",
1017 | "raw-body": "2.5.1",
1018 | "type-is": "~1.6.18",
1019 | "unpipe": "1.0.0"
1020 | }
1021 | },
1022 | "brace-expansion": {
1023 | "version": "1.1.11",
1024 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
1025 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
1026 | "dev": true,
1027 | "requires": {
1028 | "balanced-match": "^1.0.0",
1029 | "concat-map": "0.0.1"
1030 | }
1031 | },
1032 | "braces": {
1033 | "version": "3.0.2",
1034 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
1035 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
1036 | "dev": true,
1037 | "requires": {
1038 | "fill-range": "^7.0.1"
1039 | }
1040 | },
1041 | "bytes": {
1042 | "version": "3.1.2",
1043 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
1044 | "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="
1045 | },
1046 | "call-bind": {
1047 | "version": "1.0.2",
1048 | "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
1049 | "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
1050 | "requires": {
1051 | "function-bind": "^1.1.1",
1052 | "get-intrinsic": "^1.0.2"
1053 | }
1054 | },
1055 | "chokidar": {
1056 | "version": "3.5.3",
1057 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
1058 | "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
1059 | "dev": true,
1060 | "requires": {
1061 | "anymatch": "~3.1.2",
1062 | "braces": "~3.0.2",
1063 | "fsevents": "~2.3.2",
1064 | "glob-parent": "~5.1.2",
1065 | "is-binary-path": "~2.1.0",
1066 | "is-glob": "~4.0.1",
1067 | "normalize-path": "~3.0.0",
1068 | "readdirp": "~3.6.0"
1069 | }
1070 | },
1071 | "concat-map": {
1072 | "version": "0.0.1",
1073 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
1074 | "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
1075 | "dev": true
1076 | },
1077 | "content-disposition": {
1078 | "version": "0.5.4",
1079 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
1080 | "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
1081 | "requires": {
1082 | "safe-buffer": "5.2.1"
1083 | }
1084 | },
1085 | "content-type": {
1086 | "version": "1.0.4",
1087 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
1088 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
1089 | },
1090 | "cookie": {
1091 | "version": "0.5.0",
1092 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
1093 | "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw=="
1094 | },
1095 | "cookie-signature": {
1096 | "version": "1.0.6",
1097 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
1098 | "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
1099 | },
1100 | "debug": {
1101 | "version": "2.6.9",
1102 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
1103 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
1104 | "requires": {
1105 | "ms": "2.0.0"
1106 | }
1107 | },
1108 | "depd": {
1109 | "version": "2.0.0",
1110 | "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
1111 | "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="
1112 | },
1113 | "destroy": {
1114 | "version": "1.2.0",
1115 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
1116 | "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg=="
1117 | },
1118 | "ee-first": {
1119 | "version": "1.1.1",
1120 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
1121 | "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
1122 | },
1123 | "encodeurl": {
1124 | "version": "1.0.2",
1125 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
1126 | "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w=="
1127 | },
1128 | "escape-html": {
1129 | "version": "1.0.3",
1130 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
1131 | "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
1132 | },
1133 | "etag": {
1134 | "version": "1.8.1",
1135 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
1136 | "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="
1137 | },
1138 | "express": {
1139 | "version": "4.18.1",
1140 | "resolved": "https://registry.npmjs.org/express/-/express-4.18.1.tgz",
1141 | "integrity": "sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q==",
1142 | "requires": {
1143 | "accepts": "~1.3.8",
1144 | "array-flatten": "1.1.1",
1145 | "body-parser": "1.20.0",
1146 | "content-disposition": "0.5.4",
1147 | "content-type": "~1.0.4",
1148 | "cookie": "0.5.0",
1149 | "cookie-signature": "1.0.6",
1150 | "debug": "2.6.9",
1151 | "depd": "2.0.0",
1152 | "encodeurl": "~1.0.2",
1153 | "escape-html": "~1.0.3",
1154 | "etag": "~1.8.1",
1155 | "finalhandler": "1.2.0",
1156 | "fresh": "0.5.2",
1157 | "http-errors": "2.0.0",
1158 | "merge-descriptors": "1.0.1",
1159 | "methods": "~1.1.2",
1160 | "on-finished": "2.4.1",
1161 | "parseurl": "~1.3.3",
1162 | "path-to-regexp": "0.1.7",
1163 | "proxy-addr": "~2.0.7",
1164 | "qs": "6.10.3",
1165 | "range-parser": "~1.2.1",
1166 | "safe-buffer": "5.2.1",
1167 | "send": "0.18.0",
1168 | "serve-static": "1.15.0",
1169 | "setprototypeof": "1.2.0",
1170 | "statuses": "2.0.1",
1171 | "type-is": "~1.6.18",
1172 | "utils-merge": "1.0.1",
1173 | "vary": "~1.1.2"
1174 | }
1175 | },
1176 | "fill-range": {
1177 | "version": "7.0.1",
1178 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
1179 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
1180 | "dev": true,
1181 | "requires": {
1182 | "to-regex-range": "^5.0.1"
1183 | }
1184 | },
1185 | "finalhandler": {
1186 | "version": "1.2.0",
1187 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
1188 | "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==",
1189 | "requires": {
1190 | "debug": "2.6.9",
1191 | "encodeurl": "~1.0.2",
1192 | "escape-html": "~1.0.3",
1193 | "on-finished": "2.4.1",
1194 | "parseurl": "~1.3.3",
1195 | "statuses": "2.0.1",
1196 | "unpipe": "~1.0.0"
1197 | }
1198 | },
1199 | "forwarded": {
1200 | "version": "0.2.0",
1201 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
1202 | "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="
1203 | },
1204 | "fresh": {
1205 | "version": "0.5.2",
1206 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
1207 | "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q=="
1208 | },
1209 | "fsevents": {
1210 | "version": "2.3.2",
1211 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
1212 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
1213 | "dev": true,
1214 | "optional": true
1215 | },
1216 | "function-bind": {
1217 | "version": "1.1.1",
1218 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
1219 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
1220 | },
1221 | "get-intrinsic": {
1222 | "version": "1.1.3",
1223 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz",
1224 | "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==",
1225 | "requires": {
1226 | "function-bind": "^1.1.1",
1227 | "has": "^1.0.3",
1228 | "has-symbols": "^1.0.3"
1229 | }
1230 | },
1231 | "glob-parent": {
1232 | "version": "5.1.2",
1233 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
1234 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
1235 | "dev": true,
1236 | "requires": {
1237 | "is-glob": "^4.0.1"
1238 | }
1239 | },
1240 | "has": {
1241 | "version": "1.0.3",
1242 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
1243 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
1244 | "requires": {
1245 | "function-bind": "^1.1.1"
1246 | }
1247 | },
1248 | "has-flag": {
1249 | "version": "3.0.0",
1250 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
1251 | "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
1252 | "dev": true
1253 | },
1254 | "has-symbols": {
1255 | "version": "1.0.3",
1256 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
1257 | "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A=="
1258 | },
1259 | "http-errors": {
1260 | "version": "2.0.0",
1261 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
1262 | "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
1263 | "requires": {
1264 | "depd": "2.0.0",
1265 | "inherits": "2.0.4",
1266 | "setprototypeof": "1.2.0",
1267 | "statuses": "2.0.1",
1268 | "toidentifier": "1.0.1"
1269 | }
1270 | },
1271 | "iconv-lite": {
1272 | "version": "0.4.24",
1273 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
1274 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
1275 | "requires": {
1276 | "safer-buffer": ">= 2.1.2 < 3"
1277 | }
1278 | },
1279 | "ignore-by-default": {
1280 | "version": "1.0.1",
1281 | "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz",
1282 | "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==",
1283 | "dev": true
1284 | },
1285 | "inherits": {
1286 | "version": "2.0.4",
1287 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
1288 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
1289 | },
1290 | "ipaddr.js": {
1291 | "version": "1.9.1",
1292 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
1293 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="
1294 | },
1295 | "is-binary-path": {
1296 | "version": "2.1.0",
1297 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
1298 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
1299 | "dev": true,
1300 | "requires": {
1301 | "binary-extensions": "^2.0.0"
1302 | }
1303 | },
1304 | "is-extglob": {
1305 | "version": "2.1.1",
1306 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
1307 | "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
1308 | "dev": true
1309 | },
1310 | "is-glob": {
1311 | "version": "4.0.3",
1312 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
1313 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
1314 | "dev": true,
1315 | "requires": {
1316 | "is-extglob": "^2.1.1"
1317 | }
1318 | },
1319 | "is-number": {
1320 | "version": "7.0.0",
1321 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
1322 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
1323 | "dev": true
1324 | },
1325 | "media-typer": {
1326 | "version": "0.3.0",
1327 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
1328 | "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ=="
1329 | },
1330 | "merge-descriptors": {
1331 | "version": "1.0.1",
1332 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
1333 | "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w=="
1334 | },
1335 | "methods": {
1336 | "version": "1.1.2",
1337 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
1338 | "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w=="
1339 | },
1340 | "mime": {
1341 | "version": "1.6.0",
1342 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
1343 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
1344 | },
1345 | "mime-db": {
1346 | "version": "1.52.0",
1347 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
1348 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="
1349 | },
1350 | "mime-types": {
1351 | "version": "2.1.35",
1352 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
1353 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
1354 | "requires": {
1355 | "mime-db": "1.52.0"
1356 | }
1357 | },
1358 | "minimatch": {
1359 | "version": "3.1.2",
1360 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
1361 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
1362 | "dev": true,
1363 | "requires": {
1364 | "brace-expansion": "^1.1.7"
1365 | }
1366 | },
1367 | "ms": {
1368 | "version": "2.0.0",
1369 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
1370 | "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
1371 | },
1372 | "negotiator": {
1373 | "version": "0.6.3",
1374 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
1375 | "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="
1376 | },
1377 | "nodemon": {
1378 | "version": "2.0.20",
1379 | "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.20.tgz",
1380 | "integrity": "sha512-Km2mWHKKY5GzRg6i1j5OxOHQtuvVsgskLfigG25yTtbyfRGn/GNvIbRyOf1PSCKJ2aT/58TiuUsuOU5UToVViw==",
1381 | "dev": true,
1382 | "requires": {
1383 | "chokidar": "^3.5.2",
1384 | "debug": "^3.2.7",
1385 | "ignore-by-default": "^1.0.1",
1386 | "minimatch": "^3.1.2",
1387 | "pstree.remy": "^1.1.8",
1388 | "semver": "^5.7.1",
1389 | "simple-update-notifier": "^1.0.7",
1390 | "supports-color": "^5.5.0",
1391 | "touch": "^3.1.0",
1392 | "undefsafe": "^2.0.5"
1393 | },
1394 | "dependencies": {
1395 | "debug": {
1396 | "version": "3.2.7",
1397 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
1398 | "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
1399 | "dev": true,
1400 | "requires": {
1401 | "ms": "^2.1.1"
1402 | }
1403 | },
1404 | "ms": {
1405 | "version": "2.1.3",
1406 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
1407 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
1408 | "dev": true
1409 | }
1410 | }
1411 | },
1412 | "nopt": {
1413 | "version": "1.0.10",
1414 | "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz",
1415 | "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==",
1416 | "dev": true,
1417 | "requires": {
1418 | "abbrev": "1"
1419 | }
1420 | },
1421 | "normalize-path": {
1422 | "version": "3.0.0",
1423 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
1424 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
1425 | "dev": true
1426 | },
1427 | "object-inspect": {
1428 | "version": "1.12.2",
1429 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz",
1430 | "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ=="
1431 | },
1432 | "on-finished": {
1433 | "version": "2.4.1",
1434 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
1435 | "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
1436 | "requires": {
1437 | "ee-first": "1.1.1"
1438 | }
1439 | },
1440 | "parseurl": {
1441 | "version": "1.3.3",
1442 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
1443 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
1444 | },
1445 | "path-to-regexp": {
1446 | "version": "0.1.7",
1447 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
1448 | "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
1449 | },
1450 | "picomatch": {
1451 | "version": "2.3.1",
1452 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
1453 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
1454 | "dev": true
1455 | },
1456 | "proxy-addr": {
1457 | "version": "2.0.7",
1458 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
1459 | "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
1460 | "requires": {
1461 | "forwarded": "0.2.0",
1462 | "ipaddr.js": "1.9.1"
1463 | }
1464 | },
1465 | "pstree.remy": {
1466 | "version": "1.1.8",
1467 | "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz",
1468 | "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==",
1469 | "dev": true
1470 | },
1471 | "qs": {
1472 | "version": "6.10.3",
1473 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz",
1474 | "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==",
1475 | "requires": {
1476 | "side-channel": "^1.0.4"
1477 | }
1478 | },
1479 | "range-parser": {
1480 | "version": "1.2.1",
1481 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
1482 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
1483 | },
1484 | "raw-body": {
1485 | "version": "2.5.1",
1486 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
1487 | "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
1488 | "requires": {
1489 | "bytes": "3.1.2",
1490 | "http-errors": "2.0.0",
1491 | "iconv-lite": "0.4.24",
1492 | "unpipe": "1.0.0"
1493 | }
1494 | },
1495 | "readdirp": {
1496 | "version": "3.6.0",
1497 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
1498 | "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
1499 | "dev": true,
1500 | "requires": {
1501 | "picomatch": "^2.2.1"
1502 | }
1503 | },
1504 | "safe-buffer": {
1505 | "version": "5.2.1",
1506 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
1507 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
1508 | },
1509 | "safer-buffer": {
1510 | "version": "2.1.2",
1511 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
1512 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
1513 | },
1514 | "semver": {
1515 | "version": "5.7.1",
1516 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
1517 | "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
1518 | "dev": true
1519 | },
1520 | "send": {
1521 | "version": "0.18.0",
1522 | "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
1523 | "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
1524 | "requires": {
1525 | "debug": "2.6.9",
1526 | "depd": "2.0.0",
1527 | "destroy": "1.2.0",
1528 | "encodeurl": "~1.0.2",
1529 | "escape-html": "~1.0.3",
1530 | "etag": "~1.8.1",
1531 | "fresh": "0.5.2",
1532 | "http-errors": "2.0.0",
1533 | "mime": "1.6.0",
1534 | "ms": "2.1.3",
1535 | "on-finished": "2.4.1",
1536 | "range-parser": "~1.2.1",
1537 | "statuses": "2.0.1"
1538 | },
1539 | "dependencies": {
1540 | "ms": {
1541 | "version": "2.1.3",
1542 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
1543 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
1544 | }
1545 | }
1546 | },
1547 | "serve-static": {
1548 | "version": "1.15.0",
1549 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz",
1550 | "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==",
1551 | "requires": {
1552 | "encodeurl": "~1.0.2",
1553 | "escape-html": "~1.0.3",
1554 | "parseurl": "~1.3.3",
1555 | "send": "0.18.0"
1556 | }
1557 | },
1558 | "setprototypeof": {
1559 | "version": "1.2.0",
1560 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
1561 | "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
1562 | },
1563 | "side-channel": {
1564 | "version": "1.0.4",
1565 | "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
1566 | "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
1567 | "requires": {
1568 | "call-bind": "^1.0.0",
1569 | "get-intrinsic": "^1.0.2",
1570 | "object-inspect": "^1.9.0"
1571 | }
1572 | },
1573 | "simple-update-notifier": {
1574 | "version": "1.0.7",
1575 | "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.0.7.tgz",
1576 | "integrity": "sha512-BBKgR84BJQJm6WjWFMHgLVuo61FBDSj1z/xSFUIozqO6wO7ii0JxCqlIud7Enr/+LhlbNI0whErq96P2qHNWew==",
1577 | "dev": true,
1578 | "requires": {
1579 | "semver": "~7.0.0"
1580 | },
1581 | "dependencies": {
1582 | "semver": {
1583 | "version": "7.0.0",
1584 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz",
1585 | "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==",
1586 | "dev": true
1587 | }
1588 | }
1589 | },
1590 | "statuses": {
1591 | "version": "2.0.1",
1592 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
1593 | "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="
1594 | },
1595 | "supports-color": {
1596 | "version": "5.5.0",
1597 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
1598 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
1599 | "dev": true,
1600 | "requires": {
1601 | "has-flag": "^3.0.0"
1602 | }
1603 | },
1604 | "to-regex-range": {
1605 | "version": "5.0.1",
1606 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
1607 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
1608 | "dev": true,
1609 | "requires": {
1610 | "is-number": "^7.0.0"
1611 | }
1612 | },
1613 | "toidentifier": {
1614 | "version": "1.0.1",
1615 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
1616 | "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="
1617 | },
1618 | "touch": {
1619 | "version": "3.1.0",
1620 | "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz",
1621 | "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==",
1622 | "dev": true,
1623 | "requires": {
1624 | "nopt": "~1.0.10"
1625 | }
1626 | },
1627 | "type-is": {
1628 | "version": "1.6.18",
1629 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
1630 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
1631 | "requires": {
1632 | "media-typer": "0.3.0",
1633 | "mime-types": "~2.1.24"
1634 | }
1635 | },
1636 | "undefsafe": {
1637 | "version": "2.0.5",
1638 | "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
1639 | "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==",
1640 | "dev": true
1641 | },
1642 | "unpipe": {
1643 | "version": "1.0.0",
1644 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
1645 | "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="
1646 | },
1647 | "utils-merge": {
1648 | "version": "1.0.1",
1649 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
1650 | "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA=="
1651 | },
1652 | "vary": {
1653 | "version": "1.1.2",
1654 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
1655 | "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="
1656 | }
1657 | }
1658 | }
1659 |
--------------------------------------------------------------------------------
/1-node-js-basics/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "node-js-basic",
3 | "version": "1.0.0",
4 | "description": "learn basics of node js",
5 | "main": "app.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "author": "Manoj Jha",
10 | "license": "ISC",
11 | "dependencies": {
12 | "express": "^4.18.1"
13 | },
14 | "devDependencies": {
15 | "nodemon": "^2.0.20"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/1-node-js-basics/test.txt:
--------------------------------------------------------------------------------
1 | Test
2 |
--------------------------------------------------------------------------------
/2-node-js-with-express/Controllers/moviesController.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 |
3 | let movies = JSON.parse(fs.readFileSync('./data/movies.json'));
4 |
5 | exports.checkId = (req, res, next, value) => {
6 | console.log('Movie ID is ' + value);
7 |
8 | //FIND MOVIE BASED ON ID PARAMETER
9 | let movie = movies.find(el => el.id === value * 1);
10 |
11 | if(!movie){
12 | return res.status(404).json({
13 | status: "fail",
14 | message: 'Movie with ID ' +value+ ' is not found'
15 | })
16 | }
17 | next();
18 | }
19 |
20 | exports.validateBody = (req, res, next) => {
21 | if(!req.body.name || !req.body.releaseYear){
22 | return res.status(400).json({
23 | status: 'fail',
24 | message: 'Not a valid movie data'
25 | });
26 | }
27 | next();
28 | }
29 |
30 | exports.getAllMovies = (req, res) => {
31 | res.status(200).json({
32 | status: "sucess",
33 | requestedAt: req.requestedAt,
34 | count: movies.length,
35 | data: {
36 | movies: movies
37 | }
38 | });
39 | }
40 |
41 | exports.getMovie = (req, res) => {
42 | //console.log(req.params);
43 | //CONVERT ID TO NUMBER TYPE
44 | const id = req.params.id * 1;
45 |
46 | //FIND MOVIE BASED ON ID PARAMETER
47 | let movie = movies.find(el => el.id === id);
48 |
49 | // if(!movie){
50 | // return res.status(404).json({
51 | // status: "fail",
52 | // message: 'Movie with ID ' +id+ ' is not found'
53 | // })
54 | // }
55 |
56 | //SEND MOVIE IN THE RESPONSE
57 | res.status(200).json({
58 | status: "success",
59 | data: {
60 | movie: movie
61 | }
62 | });
63 | }
64 |
65 | exports.createMovie = (req, res) => {
66 | //console.log(req.body);
67 | const newId = movies[movies.length - 1].id + 1;
68 | const newMovie = Object.assign({id: newId}, req.body)
69 | movies.push(newMovie);
70 | fs.writeFile('./data/movies.json', JSON.stringify(movies), (err) => {
71 | res.status(201).json({
72 | status: "success",
73 | data: {
74 | movie: newMovie
75 | }
76 | })
77 | })
78 | //res.send('Created');
79 | }
80 |
81 | exports.updateMovie = (req, res) => {
82 | let id = req.params.id * 1;
83 | let movieToUpdate = movies.find(el => el.id === id);
84 |
85 | // if(!movieToUpdate){
86 | // return res.status(404).json({
87 | // status:'fail',
88 | // message: 'No movie object with ID ' +id+ ' is found'
89 | // })
90 |
91 | // }
92 | let index = movies.indexOf(movieToUpdate);//e.g. - id = 4, index = 3
93 |
94 | Object.assign(movieToUpdate, req.body);
95 |
96 | movies[index] = movieToUpdate;
97 |
98 | fs.writeFile('./data/movies.json', JSON.stringify(movies), (err) => {
99 | res.status(200).json({
100 | status: "success",
101 | data: {
102 | movie: movieToUpdate
103 | }
104 | })
105 | })
106 | }
107 |
108 | exports.deleteMovie = (req, res) => {
109 | const id = req.params.id * 1;
110 | const movieToDelete = movies.find(el => el.id === id);
111 |
112 | // if(!movieToDelete){
113 | // return res.status(404).json({
114 | // status:'fail',
115 | // message: 'No movie object with ID ' +id+ ' is found to delete'
116 | // })
117 |
118 | // }
119 |
120 | const index = movies.indexOf(movieToDelete);
121 |
122 | movies.splice(index, 1);
123 |
124 | fs.writeFile('./data/movies.json', JSON.stringify(movies), (err) => {
125 | res.status(204).json({
126 | status: "success",
127 | data: {
128 | movie: null
129 | }
130 | })
131 | })
132 | }
--------------------------------------------------------------------------------
/2-node-js-with-express/Routes/moviesRoutes.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const moviesController = require('./../Controllers/moviesController');
3 |
4 | const router = express.Router();
5 |
6 | router.param('id', moviesController.checkId)
7 |
8 | router.route('/')
9 | .get(moviesController.getAllMovies)
10 | .post(moviesController.validateBody, moviesController.createMovie)
11 |
12 |
13 | router.route('/:id')
14 | .get(moviesController.getMovie)
15 | .patch(moviesController.updateMovie)
16 | .delete(moviesController.deleteMovie)
17 |
18 | module.exports = router;
--------------------------------------------------------------------------------
/2-node-js-with-express/app.js:
--------------------------------------------------------------------------------
1 | //IMPORT PACKAGE
2 | const { application } = require('express');
3 | const express = require('express');
4 | const morgan = require('morgan');
5 | const moviesRouter = require('./Routes/moviesRoutes');
6 |
7 | let app = express();
8 |
9 | const logger = function(req, res, next){
10 | console.log('Custom middleware called');
11 | next();
12 | }
13 |
14 | app.use(express.json());
15 |
16 | if(process.env.NODE_ENV === 'development'){
17 | app.use(morgan('dev'))
18 | }
19 |
20 | app.use(express.static('./public'))
21 | app.use(logger);
22 | app.use((req, res, next) => {
23 | req.requestedAt = new Date().toISOString();
24 | next();
25 | })
26 |
27 | //USING ROUTES
28 | app.use('/api/v1/movies', moviesRouter)
29 |
30 | module.exports = app;
31 |
32 |
--------------------------------------------------------------------------------
/2-node-js-with-express/config.env:
--------------------------------------------------------------------------------
1 | NODE_ENV=development
2 | PORT=3000
3 | DB_USER=admin
4 | DB_PASSWORD=n9Hb3bOrVoF4pGKw
--------------------------------------------------------------------------------
/2-node-js-with-express/data/movies.json:
--------------------------------------------------------------------------------
1 | [{"id":4,"name":"Test","releaseYear":2018,"duration":90},{"id":5,"name":"Test 6","releaseYear":2020,"duration":70},{"id":7},{"id":8,"name":"Test 4","releaseYear":2020,"duration":70},{"id":9},{"id":10,"name":"Test 6","releaseYear":2023,"duration":90}]
--------------------------------------------------------------------------------
/2-node-js-with-express/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "node-js-with-express",
3 | "version": "1.0.0",
4 | "description": "Learning express js with node",
5 | "main": "app.js",
6 | "scripts": {
7 | "start": "nodemon server.js"
8 | },
9 | "author": "Manoj Jha",
10 | "license": "ISC",
11 | "dependencies": {
12 | "dotenv": "^16.0.3",
13 | "express": "^4.18.2",
14 | "morgan": "^1.10.0",
15 | "nodemon": "^2.0.20"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/2-node-js-with-express/public/css/style.css:
--------------------------------------------------------------------------------
1 | body{
2 | font-family: 'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans Unicode', Geneva, Verdana, sans-serif;
3 | }
4 |
5 | h2{
6 | color: grey;
7 | }
8 |
9 | div.card {
10 | box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
11 | text-align: center;
12 | border-radius: 5px;
13 | margin: 50px 200px;
14 | padding: 20px 30px;
15 | }
--------------------------------------------------------------------------------
/2-node-js-with-express/public/templates/demo.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | serving static file
9 |
10 |
11 |
12 |
Welcome to Demo API
13 |
This is a demo mivies API built using Express JS and a JSON file as a data source.
14 |
This is a simple demo HTML page to demo serving static files.
15 |
16 |
17 |
--------------------------------------------------------------------------------
/2-node-js-with-express/server.js:
--------------------------------------------------------------------------------
1 | const dotenv = require('dotenv');
2 | dotenv.config({path: './config.env'});
3 |
4 | const app = require('./app');
5 |
6 | //console.log(app.get('env'));
7 | console.log(process.env);
8 |
9 | const port = process.env.PORT || 3000;
10 |
11 | app.listen(port, () => {
12 | console.log('server has started...');
13 | })
--------------------------------------------------------------------------------
/2-node-js-with-express/test.txt:
--------------------------------------------------------------------------------
1 | test
2 |
--------------------------------------------------------------------------------
/3-mongodb-and-mongoose/Controllers/moviesController.js:
--------------------------------------------------------------------------------
1 | const { param } = require('../Routes/moviesRoutes');
2 | const Movie = require('./../Models/movieModel');
3 | const ApiFeatures = require('./../Utils/ApiFeatures');
4 |
5 | exports.getHighestRated = (req, res, next) => {
6 | req.query.limit = '5';
7 | req.query.sort = '-ratings';
8 |
9 | next();
10 | }
11 |
12 | exports.getAllMovies = async (req, res) => {
13 | try{
14 | const features = new ApiFeatures(Movie.find(), req.query)
15 | .filter()
16 | .sort()
17 | .limitFields()
18 | .paginate();
19 | let movies = await features.query;
20 | //Mongoose 6.0 or less
21 | /**************Mongoose 6.0 or less**************
22 | const excludeFields = ['sort', 'page', 'limit', 'fields'];
23 | const queryObj = {...req.query};
24 | excludeFields.forEach((el) => {
25 | delete queryObj[el]
26 | })
27 | const movies = await Movie.find(queryObj);
28 | **************************************************/
29 |
30 | res.status(200).json({
31 | status: 'success',
32 | length: movies.length,
33 | data: {
34 | movies
35 | }
36 | });
37 | }catch(err){
38 | res.status(404).json({
39 | status: 'fail',
40 | message: err.message
41 | })
42 | }
43 |
44 | }
45 |
46 | exports.getMovie = async (req, res) => {
47 | try{
48 | //const movie = await Movie.findOne({_id: req.params.id});
49 | const movie = await Movie.findById(req.params.id);
50 |
51 | res.status(200).json({
52 | status: 'success',
53 | data: {
54 | movie
55 | }
56 | });
57 | }catch(err){
58 | res.status(404).json({
59 | status: 'fail',
60 | message: err.message
61 | })
62 | }
63 | }
64 |
65 | exports.createMovie = async (req, res) => {
66 | try{
67 | const movie = await Movie.create(req.body);
68 |
69 | res.status(201).json({
70 | status: 'success',
71 | data: {
72 | movie
73 | }
74 | })
75 | }catch(err){
76 | res.status(400).json({
77 | status: 'fail',
78 | message: err.message
79 | })
80 | }
81 | }
82 |
83 | exports.updateMovie = async (req, res) => {
84 | try{
85 | const updatedMovie = await Movie.findByIdAndUpdate(req.params.id, req.body, {new: true, runValidators: true});
86 |
87 | res.status(200).json({
88 | status: "success",
89 | data: {
90 | movie: updatedMovie
91 | }
92 | });
93 | }catch(err){
94 | res.status(404).json({
95 | status:"fail",
96 | message: err.message
97 | });
98 | }
99 | }
100 |
101 | exports.deleteMovie = async (req, res) => {
102 | try{
103 | await Movie.findByIdAndDelete(req.params.id);
104 |
105 | res.status(204).json({
106 | status: 'success',
107 | data: null
108 | });
109 | }catch(err){
110 | res.status(404).json({
111 | status:"fail",
112 | message: err.message
113 | });
114 | }
115 | }
116 |
117 | exports.getMovieStats = async (req, res) => {
118 | try{
119 | const stats = await Movie.aggregate([
120 | { $match: {ratings: {$gte: 4.5}}},
121 | { $group: {
122 | _id: '$releaseYear',
123 | avgRating: { $avg: '$ratings'},
124 | avgPrice: { $avg: '$price' },
125 | minPrice: { $min: '$price' },
126 | maxPrice: { $max: '$price' },
127 | priceTotal: { $sum: '$price'},
128 | movieCount: { $sum: 1}
129 | }},
130 | { $sort: { minPrice: 1}}
131 | //{ $match: {maxPrice: {$gte: 60}}}
132 | ]);
133 |
134 | res.status(200).json({
135 | status: 'success',
136 | count: stats.length,
137 | data: {
138 | stats
139 | }
140 | });
141 | }catch(err) {
142 | res.status(404).json({
143 | status:"fail",
144 | message: err.message
145 | });
146 | }
147 | }
148 |
149 | exports.getMovieByGenre = async (req, res) => {
150 | try{
151 | const genre = req.params.genre;
152 | const movies = await Movie.aggregate([
153 | {$unwind: '$genres'},
154 | {$group: {
155 | _id: '$genres',
156 | movieCount: { $sum: 1},
157 | movies: {$push: '$name'},
158 | }},
159 | {$addFields: {genre: "$_id"}},
160 | {$project: {_id: 0}},
161 | {$sort: {movieCount: -1}},
162 | //{$limit: 6}
163 | //{$match: {genre: genre}}
164 | ]);
165 |
166 | res.status(200).json({
167 | status: 'success',
168 | count: movies.length,
169 | data: {
170 | movies
171 | }
172 | });
173 | }catch(err) {
174 | res.status(404).json({
175 | status:"fail",
176 | message: err.message
177 | });
178 | }
179 | }
180 |
181 |
--------------------------------------------------------------------------------
/3-mongodb-and-mongoose/Log/log.txt:
--------------------------------------------------------------------------------
1 | A new movie document with name Test 1 has been created by MANOJJHA
2 | A new movie document with name Test 2 has been created by MANOJJHA
3 | A new movie document with name Sample Movie has been created by MANOJJHA
4 | Query took 249 milliseconds to fetch the documents.Query took 239 milliseconds to fetch the documents.Query took 236 milliseconds to fetch the documents.Query took 246 milliseconds to fetch the documents.Query took 4555 milliseconds to fetch the documents.Query took 236 milliseconds to fetch the documents.A new movie document with name Test Movie 1 has been created by MANOJJHA
5 |
--------------------------------------------------------------------------------
/3-mongodb-and-mongoose/Models/movieModel.js:
--------------------------------------------------------------------------------
1 | const mongoose = require('mongoose');
2 | const fs = require('fs');
3 | const validator = require('validator');
4 |
5 | const movieSchema = new mongoose.Schema({
6 | name: {
7 | type: String,
8 | required: [true, 'Name is required field!'],
9 | unique: true,
10 | maxlength: [100, "Movie name must not have more than 100 characters"],
11 | minlength: [4, "Movie name must have at least 4 charachters"],
12 | trim: true,
13 | validate: [validator.isAlpha, "Name should only contain alphabets."]
14 | },
15 | description: {
16 | type: String,
17 | required: [true, 'Description is required field!'],
18 | trim: true
19 | },
20 | duration: {
21 | type: Number,
22 | required: [true, 'Duration is required field!']
23 | },
24 | ratings: {
25 | type: Number,
26 | validate: {
27 | validator: function(value){
28 | return value >= 1 && value <= 10;
29 | },
30 | message: "Ratings ({VALUE}) should be above 1 and below 10"
31 | }
32 | },
33 | totalRating: {
34 | type: Number
35 | },
36 | releaseYear: {
37 | type: Number,
38 | required: [true, 'Release year is required field!']
39 | },
40 | releaseDate:{
41 | type: Date
42 | },
43 | createdAt: {
44 | type: Date,
45 | default: Date.now(),
46 | select: false
47 | },
48 | genres: {
49 | type: [String],
50 | required: [true, 'Genres is required field!'],
51 | // enum: {
52 | // values: ["Action", "Adventure", "Sci-Fi", "Thriller", "Crime", "Drama", "Comedy", "Romance", "Biography"],
53 | // message: "This genre does not exist"
54 | // }
55 | },
56 | directors: {
57 | type: [String],
58 | required: [true, 'Directors is required field!']
59 | },
60 | coverImage:{
61 | type: String,
62 | require: [true, 'Cover image is required field!']
63 | },
64 | actors: {
65 | type: [String],
66 | require: [true, 'actors is required field!']
67 | },
68 | price: {
69 | type: Number,
70 | require: [true, 'Price is required field!']
71 | },
72 | createdBy: String
73 | }, {
74 | toJSON: {virtuals: true},
75 | toObject: {virtuals: true}
76 | });
77 |
78 | movieSchema.virtual('durationInHours').get(function(){
79 | return this.duration / 60;
80 | })
81 |
82 | //EXECUTED BEFORE THE DOCUMENT IS SAVED IN DB
83 | //.save() or .create()
84 | //inserMany, findByIdAndUpdate will not work
85 | movieSchema.pre('save', function(next) {
86 | this.createdBy = 'MANOJJHA';
87 | next();
88 | })
89 |
90 | movieSchema.post('save', function(doc, next){
91 | const content = `A new movie document with name ${doc.name} has been created by ${doc.createdBy}\n`;
92 | fs.writeFileSync('./Log/log.txt', content, {flag: 'a'}, (err) => {
93 | console.log(err.message);
94 | });
95 | next();
96 | });
97 |
98 | movieSchema.pre(/^find/, function(next){
99 | this.find({releaseDate: {$lte: Date.now()}});
100 | this.startTime = Date.now()
101 | next();
102 | });
103 |
104 | movieSchema.post(/^find/, function(docs, next){
105 | this.find({releaseDate: {$lte: Date.now()}});
106 | this.endTime = Date.now();
107 |
108 | const content = `Query took ${this.endTime - this.startTime} milliseconds to fetch the documents.`
109 | fs.writeFileSync('./Log/log.txt', content, {flag: 'a'}, (err) => {
110 | console.log(err.message);
111 | });
112 |
113 | next();
114 | });
115 |
116 | movieSchema.pre('aggregate', function(next){
117 | console.log(this.pipeline().unshift({ $match: {releaseDate: {$lte: new Date()}}}));
118 | next();
119 | });
120 |
121 |
122 | const Movie = mongoose.model('Movie', movieSchema);
123 |
124 | module.exports = Movie;
--------------------------------------------------------------------------------
/3-mongodb-and-mongoose/Routes/moviesRoutes.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const moviesController = require('./../Controllers/moviesController');
3 |
4 | const router = express.Router();
5 |
6 | //router.param('id', moviesController.checkId)
7 |
8 | router.route('/highest-rated').get(moviesController.getHighestRated, moviesController.getAllMovies)
9 |
10 | router.route('/movie-stats').get(moviesController.getMovieStats);
11 |
12 | router.route('/movies-by-genre/:genre').get(moviesController.getMovieByGenre);
13 |
14 | router.route('/')
15 | .get(moviesController.getAllMovies)
16 | .post(moviesController.createMovie)
17 |
18 |
19 | router.route('/:id')
20 | .get(moviesController.getMovie)
21 | .patch(moviesController.updateMovie)
22 | .delete(moviesController.deleteMovie)
23 |
24 | module.exports = router;
--------------------------------------------------------------------------------
/3-mongodb-and-mongoose/Utils/ApiFeatures.js:
--------------------------------------------------------------------------------
1 | class Apifeatures{
2 | constructor(query, queryStr){
3 | this.query = query;
4 | this.queryStr = queryStr;
5 | }
6 |
7 | filter(){
8 | let queryString = JSON.stringify(this.queryStr);
9 | queryString = queryString.replace(/\b(gte|gt|lte|lt)\b/g, (match) => `$${match}`);
10 | const queryObj = JSON.parse(queryString);
11 |
12 | this.query = this.query.find(queryObj);
13 |
14 | return this;
15 | }
16 |
17 | sort(){
18 | if(this.queryStr.sort){
19 | const sortBy = this.queryStr.sort.split(',').join(' ');
20 | this.query = this.query.sort(sortBy);
21 | }else{
22 | this.query = this.query.sort('-createdAt');
23 | }
24 |
25 | return this;
26 | }
27 |
28 | limitFields(){
29 | if(this.queryStr.fields){
30 | const fields = this.queryStr.fields.split(',').join(' ');
31 | this.query = this.query.select(fields);
32 | }else{
33 | this.query = this.query.select('-__v');
34 | }
35 |
36 | return this;
37 | }
38 |
39 | paginate(){
40 | const page = this.queryStr.page*1 || 1;
41 | const limit = this.queryStr.limit*1 || 10;
42 | const skip = (page -1) * limit;
43 | this.query = this.query.skip(skip).limit(limit);
44 |
45 | // if(this.queryStr.page){
46 | // const moviesCount = await Movie.countDocuments();
47 | // if(skip >= moviesCount){
48 | // throw new Error("This page is not found!");
49 | // }
50 | // }
51 |
52 | return this;
53 | }
54 | }
55 |
56 | module.exports = Apifeatures;
--------------------------------------------------------------------------------
/3-mongodb-and-mongoose/app.js:
--------------------------------------------------------------------------------
1 | //IMPORT PACKAGE
2 | const express = require('express');
3 | const morgan = require('morgan');
4 | const moviesRouter = require('./Routes/moviesRoutes');
5 |
6 | let app = express();
7 |
8 | app.use(express.json());
9 |
10 | app.use(express.static('./public'))
11 |
12 | //USING ROUTES
13 | app.use('/api/v1/movies', moviesRouter)
14 |
15 | module.exports = app;
16 |
17 |
--------------------------------------------------------------------------------
/3-mongodb-and-mongoose/config.env:
--------------------------------------------------------------------------------
1 | NODE_ENV=development
2 | PORT=3000
3 | CONN_STR=mongodb+srv://admin:n9Hb3bOrVoF4pGKw@cluster0.wvot8r4.mongodb.net/cineflix?retryWrites=true&w=majority
4 | LOCAL_CONN_STR=mongodb://localhost:27017/cineflex
5 | DB_USER=admin
6 | DB_PASSWORD=n9Hb3bOrVoF4pGKw
--------------------------------------------------------------------------------
/3-mongodb-and-mongoose/data/import-dev-data.js:
--------------------------------------------------------------------------------
1 | const mongoose = require('mongoose');
2 | const dotenv = require('dotenv');
3 | const fs = require('fs');
4 | const Movie = require('./../Models/movieModel');
5 |
6 | dotenv.config({path: './config.env'});
7 |
8 | //CONNECT TO MONGODB
9 | mongoose.connect(process.env.CONN_STR, {
10 | useNewUrlParser: true
11 | }).then((conn) => {
12 | console.log('DB Connection Successful');
13 | }).catch((error) => {
14 | console.log('Some error has occured');
15 | });
16 |
17 | //READ MOVIES.JSON FILE
18 | const movies = JSON.parse(fs.readFileSync('./data/movies.json', 'utf-8'));
19 |
20 | //DELETE EXISTING MOVIE DOCUMENTS FROM COLLECTION
21 | const deleteMovies = async () => {
22 | try{
23 | await Movie.deleteMany();
24 | console.log('Data successfully deleted!');
25 | }catch(err){
26 | console.log(err.message);
27 | }
28 | process.exit();
29 | }
30 |
31 | //IMPORT MOVIES DATA TO MONGODB COLLECTION
32 | const importMovies = async () => {
33 | try{
34 | await Movie.create(movies);
35 | console.log('Data successfully imported!');
36 | }catch(err){
37 | console.log(err.message);
38 | }
39 | process.exit();
40 | }
41 |
42 | if(process.argv[2] === '--import'){
43 | importMovies();
44 | }
45 | if(process.argv[2] === '--delete'){
46 | deleteMovies();
47 | }
--------------------------------------------------------------------------------
/3-mongodb-and-mongoose/data/movies.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "name": "Riddick",
4 | "description": "Left for dead on a sun-scorched planet, Riddick finds himself up against an alien race of predators. Activating an emergency beacon alerts two ships: one carrying a new breed of mercenary, the other captained by a man from Riddick's past.",
5 | "duration": 119,
6 | "ratings": 6.8,
7 | "totalRating": 329,
8 | "releaseYear": 2013,
9 | "releaseDate": "2013-09-04T00:00:00Z",
10 | "genres": [
11 | "Action",
12 | "Sci-Fi",
13 | "Thriller"
14 | ],
15 | "directors": [ "David Twohy" ],
16 | "coverImage": "Riddick-2013.jpg",
17 | "actors": [
18 | "Vin Diesel",
19 | "Karl Urban",
20 | "Katee Sackhoff"
21 | ],
22 | "price": 50
23 | },
24 | {
25 | "releaseYear": 2013,
26 | "name": "We're the Millers",
27 | "directors": [ "Rawson Marshall Thurber" ],
28 | "releaseDate": "2013-08-03T00:00:00Z",
29 | "ratings": 7.2,
30 | "totalRating": 230,
31 | "genres": [
32 | "Comedy",
33 | "Crime"
34 | ],
35 | "coverImage": "we-are-the-millers-2013.jpg",
36 | "description": "A veteran pot dealer creates a fake family as part of his plan to move a huge shipment of weed into the U.S. from Mexico.",
37 | "duration": 110,
38 | "actors": [
39 | "Jason Sudeikis",
40 | "Jennifer Aniston",
41 | "Emma Roberts"
42 | ],
43 | "price": 47
44 | },
45 | {
46 | "releaseYear": 2014,
47 | "name": "Divergent",
48 | "directors": [ "Neil Burger" ],
49 | "releaseDate": "2014-03-20T00:00:00Z",
50 | "ratings": 6.4,
51 | "totalRating": 164,
52 | "genres": [
53 | "Action",
54 | "Adventure",
55 | "Romance",
56 | "Sci-Fi"
57 | ],
58 | "coverImage": "Divergent-2014.jpg",
59 | "description": "Beatrice Prior, a teenager with a special mind, finds her life threatened when an authoritarian leader seeks to exterminate her kind in her effort to seize control of their divided society.",
60 | "duration": 117,
61 | "actors": [
62 | "Shailene Woodley",
63 | "Kate Winslet",
64 | "Zoe Kravitz"
65 | ],
66 | "price": 52
67 | },
68 | {
69 | "releaseYear": 2013,
70 | "name": "Elysium",
71 | "directors": [ "Neill Blomkamp" ],
72 | "releaseDate": "2013-08-07T00:00:00Z",
73 | "ratings": 7,
74 | "totalRating": 436,
75 | "genres": [
76 | "Action",
77 | "Drama",
78 | "Sci-Fi",
79 | "Thriller"
80 | ],
81 | "coverImage": "Elysium-2013.jpg",
82 | "description": "Set in the year 2154, where the very wealthy live on a man-made space station while the rest of the population resides on a ruined Earth, a man takes on a mission that could bring equality to the polarized worlds.",
83 | "duration": 109,
84 | "actors": [
85 | "Matt Damon",
86 | "Jodie Foster",
87 | "Sharlto Copley"
88 | ],
89 | "price": 65
90 | },
91 | {
92 | "releaseYear": 2014,
93 | "name": "RoboCop",
94 | "directors": [ "Jose Padilha" ],
95 | "releaseDate": "2014-01-30T00:00:00Z",
96 | "ratings": 7.7,
97 | "totalRating": 310,
98 | "genres": [
99 | "Action",
100 | "Crime",
101 | "Sci-Fi",
102 | "Thriller"
103 | ],
104 | "coverImage": "RoboCop-2014.jpg",
105 | "description": "In 2028 Detroit, when Alex Murphy (Joel Kinnaman) - a loving husband, father and good cop - is critically injured in the line of duty, the multinational conglomerate OmniCorp sees their chance for a part-man, part-robot police officer.",
106 | "duration": 110,
107 | "actors": [
108 | "Joel Kinnaman",
109 | "Douglas Urbanski",
110 | "Abbie Cornish"
111 | ],
112 | "price": 40
113 | },
114 | {
115 | "releaseYear": 2014,
116 | "name": "Captain America: The Winter Soldier",
117 | "directors": [
118 | "Anthony Russo",
119 | "Joe Russo"
120 | ],
121 | "releaseDate": "2014-04-02T00:00:00Z",
122 | "ratings": 7.9,
123 | "totalRating": 239,
124 | "genres": [
125 | "Action",
126 | "Adventure",
127 | "Sci-Fi"
128 | ],
129 | "coverImage": "captain-america-the-winter-soldier-2014.jpg",
130 | "description": "Steve Rogers struggles to embrace his role in the modern world and teams up with Natasha Romanoff, aka Black Widow, to battle a powerful yet shadowy enemy in present-day Washington, D.C.",
131 | "duration": 135,
132 | "actors": [
133 | "Chris Evans",
134 | "Frank Grillo",
135 | "Sebastian Stan"
136 | ],
137 | "price": 57
138 | },
139 | {
140 | "releaseYear": 2013,
141 | "name": "Behind the Candelabra",
142 | "directors": [ "Steven Soderbergh" ],
143 | "releaseDate": "2013-05-21T00:00:00Z",
144 | "ratings": 7,
145 | "totalRating": 325,
146 | "genres": [
147 | "Biography",
148 | "Drama",
149 | "Romance"
150 | ],
151 | "coverImage": "behind-the-candelabra-2013.jpg",
152 | "description": "Based on the autobiographical novel, the tempestuous 6-year relationship between Liberace and his (much younger) lover, Scott Thorson, is recounted.",
153 | "duration": 118,
154 | "actors": [
155 | "Matt Damon",
156 | "Scott Bakula",
157 | "Eric Zuckerman"
158 | ],
159 | "price": 59
160 | },
161 | {
162 | "releaseYear": 2012,
163 | "name": "The Dark Knight Rises",
164 | "directors": [ "Christopher Nolan" ],
165 | "releaseDate": "2012-07-16T00:00:00Z",
166 | "ratings": 8.6,
167 | "totalRating": 530,
168 | "genres": [
169 | "Action",
170 | "Crime",
171 | "Thriller"
172 | ],
173 | "coverImage": "the-dark-knight-rises-2012.jpg",
174 | "description": "Eight years on, a new evil rises from where the Batman and Commissioner Gordon tried to bury it, causing the Batman to resurface and fight to protect Gotham City... the very city which brands him an enemy.",
175 | "duration": 165,
176 | "actors": [
177 | "Christian Bale",
178 | "Tom Hardy",
179 | "Anne Hathaway"
180 | ],
181 | "price": 57
182 | }
183 | ]
--------------------------------------------------------------------------------
/3-mongodb-and-mongoose/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "node-js-with-express",
3 | "version": "1.0.0",
4 | "description": "Learning express js with node",
5 | "main": "app.js",
6 | "scripts": {
7 | "start": "nodemon server.js"
8 | },
9 | "author": "Manoj Jha",
10 | "license": "ISC",
11 | "dependencies": {
12 | "dotenv": "^16.0.3",
13 | "express": "^4.18.2",
14 | "mongoose": "^6.9.2",
15 | "morgan": "^1.10.0",
16 | "nodemon": "^2.0.20",
17 | "validator": "^13.9.0"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/3-mongodb-and-mongoose/public/css/style.css:
--------------------------------------------------------------------------------
1 | body{
2 | font-family: 'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans Unicode', Geneva, Verdana, sans-serif;
3 | }
4 |
5 | h2{
6 | color: grey;
7 | }
8 |
9 | div.card {
10 | box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
11 | text-align: center;
12 | border-radius: 5px;
13 | margin: 50px 200px;
14 | padding: 20px 30px;
15 | }
--------------------------------------------------------------------------------
/3-mongodb-and-mongoose/public/templates/demo.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | serving static file
9 |
10 |
11 |
12 |
Welcome to Demo API
13 |
This is a demo mivies API built using Express JS and a JSON file as a data source.
14 |
This is a simple demo HTML page to demo serving static files.
15 |
16 |
17 |
--------------------------------------------------------------------------------
/3-mongodb-and-mongoose/server.js:
--------------------------------------------------------------------------------
1 | const dotenv = require('dotenv');
2 | dotenv.config({path: './config.env'});
3 | const mongoose = require('mongoose');
4 | const app = require('./app');
5 |
6 | //console.log(app.get('env'));
7 | console.log(process.env);
8 |
9 | mongoose.connect(process.env.CONN_STR, {
10 | useNewUrlParser: true
11 | }).then((conn) => {
12 | //console.log(conn);
13 | console.log('DB Connection Successful');
14 | }).catch((error) => {
15 | console.log('Some error has occured');
16 | });
17 |
18 | const port = process.env.PORT || 3000;
19 |
20 | app.listen(port, () => {
21 | console.log('server has started...');
22 | })
23 |
--------------------------------------------------------------------------------
/3-mongodb-and-mongoose/test.txt:
--------------------------------------------------------------------------------
1 | test
2 |
--------------------------------------------------------------------------------
/4-error-handling/Controllers/authController.js:
--------------------------------------------------------------------------------
1 | const User = require('./../Models/userModel');
2 | const asyncErrorHandler = require('./../Utils/asyncErrorHandler');
3 |
4 | exports.signup = asyncErrorHandler(async (req, res, next) => {
5 | const newUser = await User.create(req.body);
6 |
7 | res.status(201).json({
8 | status: 'success',
9 | data: {
10 | user: newUser
11 | }
12 | });
13 | });
--------------------------------------------------------------------------------
/4-error-handling/Controllers/errorController.js:
--------------------------------------------------------------------------------
1 | const CustomError = require('./../Utils/CustomError');
2 |
3 | const devErrors = (res, error) => {
4 | res.status(error.statusCode).json({
5 | status: error.statusCode,
6 | message: error.message,
7 | stackTrace: error.stack,
8 | error: error
9 | });
10 | }
11 |
12 | const castErrorHandler = (err) => {
13 | const msg = `Invalid value for ${err.path}: ${err.value}!`
14 | return new CustomError(msg, 400);
15 | }
16 |
17 | const duplicateKeyErrorHandler = (err) => {
18 | const name = err.keyValue.name;
19 | const msg = `There is already a movie with name ${name}. Please use another name!`;
20 |
21 | return new CustomError(msg, 400);
22 | }
23 |
24 | const validationErrorHandler = (err) => {
25 | const errors = Object.values(err.errors).map(val => val.message);
26 | const errorMessages = errors.join('. ');
27 | const msg = `Invalid input data: ${errorMessages}`;
28 |
29 | return new CustomError(msg, 400);
30 | }
31 |
32 | const prodErrors = (res, error) => {
33 | if(error.isOperational){
34 | res.status(error.statusCode).json({
35 | status: error.statusCode,
36 | message: error.message
37 | });
38 | }else {
39 | res.status(500).json({
40 | status: 'error',
41 | message: 'Something went wrong! Please try again later.'
42 | })
43 | }
44 | }
45 |
46 | module.exports = (error, req, res, next) => {
47 | error.statusCode = error.statusCode || 500;
48 | error.status = error.status || 'error';
49 |
50 | if(process.env.NODE_ENV === 'development'){
51 | devErrors(res, error);
52 | } else if(process.env.NODE_ENV === 'production'){
53 | if(error.name === 'CastError') error = castErrorHandler(error);
54 | if(error.code === 11000) error = duplicateKeyErrorHandler(error);
55 | if(error.name === 'ValidationError') error = validationErrorHandler(error);
56 |
57 | prodErrors(res, error);
58 | }
59 | }
--------------------------------------------------------------------------------
/4-error-handling/Controllers/moviesController.js:
--------------------------------------------------------------------------------
1 | const { param } = require('../Routes/moviesRoutes');
2 | const Movie = require('./../Models/movieModel');
3 | const ApiFeatures = require('./../Utils/ApiFeatures');
4 | const asyncErrorHandler = require('./../Utils/asyncErrorHandler');
5 | const CustomError = require('./../Utils/CustomError');
6 |
7 | exports.getHighestRated = (req, res, next) => {
8 | req.query.limit = '5';
9 | req.query.sort = '-ratings';
10 |
11 | next();
12 | }
13 |
14 | exports.getAllMovies = asyncErrorHandler(async (req, res, next) => {
15 |
16 | const features = new ApiFeatures(Movie.find(), req.query)
17 | .filter()
18 | .sort()
19 | .limitFields()
20 | .paginate();
21 | let movies = await features.query;
22 |
23 | res.status(200).json({
24 | status: 'success',
25 | length: movies.length,
26 | data: {
27 | movies
28 | }
29 | });
30 |
31 | })
32 |
33 | exports.getMovie = asyncErrorHandler(async (req, res, next) => {
34 |
35 | //const movie = await Movie.findOne({_id: req.params.id});
36 | const movie = await Movie.findById(req.params.id);
37 |
38 |
39 | if(!movie){
40 | const error = new CustomError('Movie with that ID is not found!', 404);
41 | return next(error);
42 | }
43 |
44 | res.status(200).json({
45 | status: 'success',
46 | data: {
47 | movie
48 | }
49 | });
50 | })
51 |
52 |
53 |
54 | exports.createMovie =asyncErrorHandler(async (req, res, next) => {
55 |
56 | const movie = await Movie.create(req.body);
57 |
58 | res.status(201).json({
59 | status: 'success',
60 | data: {
61 | movie
62 | }
63 | })
64 | });
65 |
66 | exports.updateMovie = async (req, res, next) => {
67 | try{
68 | const updatedMovie = await Movie.findByIdAndUpdate(req.params.id, req.body, {new: true, runValidators: true});
69 |
70 | if(!updatedMovie){
71 | const error = new CustomError('Movie with that ID is not found!', 404);
72 | return next(error);
73 | }
74 |
75 | res.status(200).json({
76 | status: "success",
77 | data: {
78 | movie: updatedMovie
79 | }
80 | });
81 | }catch(err){
82 | res.status(404).json({
83 | status:"fail",
84 | message: err.message
85 | });
86 | }
87 | }
88 |
89 | exports.deleteMovie = asyncErrorHandler(async (req, res, next) => {
90 |
91 | const deletedMovie = await Movie.findByIdAndDelete(req.params.id);
92 |
93 | if(!deletedMovie){
94 | const error = new CustomError('Movie with that ID is not found!', 404);
95 | return next(error);
96 | }
97 |
98 | res.status(204).json({
99 | status: 'success',
100 | data: null
101 | });
102 | })
103 |
104 | exports.getMovieStats = asyncErrorHandler(async (req, res, next) => {
105 | const stats = await Movie.aggregate([
106 | { $match: {ratings: {$gte: 4.5}}},
107 | { $group: {
108 | _id: '$releaseYear',
109 | avgRating: { $avg: '$ratings'},
110 | avgPrice: { $avg: '$price' },
111 | minPrice: { $min: '$price' },
112 | maxPrice: { $max: '$price' },
113 | priceTotal: { $sum: '$price'},
114 | movieCount: { $sum: 1}
115 | }},
116 | { $sort: { minPrice: 1}}
117 | //{ $match: {maxPrice: {$gte: 60}}}
118 | ]);
119 |
120 | res.status(200).json({
121 | status: 'success',
122 | count: stats.length,
123 | data: {
124 | stats
125 | }
126 | });
127 | })
128 |
129 | exports.getMovieByGenre = asyncErrorHandler(async (req, res, next) => {
130 | const genre = req.params.genre;
131 | const movies = await Movie.aggregate([
132 | {$unwind: '$genres'},
133 | {$group: {
134 | _id: '$genres',
135 | movieCount: { $sum: 1},
136 | movies: {$push: '$name'},
137 | }},
138 | {$addFields: {genre: "$_id"}},
139 | {$project: {_id: 0}},
140 | {$sort: {movieCount: -1}},
141 | //{$limit: 6}
142 | //{$match: {genre: genre}}
143 | ]);
144 |
145 | res.status(200).json({
146 | status: 'success',
147 | count: movies.length,
148 | data: {
149 | movies
150 | }
151 | });
152 | })
153 |
154 |
--------------------------------------------------------------------------------
/4-error-handling/Log/log.txt:
--------------------------------------------------------------------------------
1 | A new movie document with name Test 1 has been created by MANOJJHA
2 | A new movie document with name Test 2 has been created by MANOJJHA
3 | A new movie document with name Sample Movie has been created by MANOJJHA
4 | Query took 249 milliseconds to fetch the documents.Query took 239 milliseconds to fetch the documents.Query took 236 milliseconds to fetch the documents.Query took 246 milliseconds to fetch the documents.Query took 4555 milliseconds to fetch the documents.Query took 236 milliseconds to fetch the documents.A new movie document with name Test Movie 1 has been created by MANOJJHA
5 | Query took 362 milliseconds to fetch the documents.Query took 268 milliseconds to fetch the documents.Query took 235 milliseconds to fetch the documents.Query took 231 milliseconds to fetch the documents.Query took 297 milliseconds to fetch the documents.Query took 261 milliseconds to fetch the documents.Query took 247 milliseconds to fetch the documents.Query took 258 milliseconds to fetch the documents.A new movie document with name We Are the Millers has been created by MANOJJHA
6 | Query took 227 milliseconds to fetch the documents.Query took 241 milliseconds to fetch the documents.Query took 266 milliseconds to fetch the documents.Query took 230 milliseconds to fetch the documents.Query took 274 milliseconds to fetch the documents.Query took 245 milliseconds to fetch the documents.Query took 241 milliseconds to fetch the documents.Query took 234 milliseconds to fetch the documents.Query took 234 milliseconds to fetch the documents.Query took 249 milliseconds to fetch the documents.Query took 244 milliseconds to fetch the documents.Query took 252 milliseconds to fetch the documents.Query took 329 milliseconds to fetch the documents.Query took 235 milliseconds to fetch the documents.Query took 254 milliseconds to fetch the documents.Query took 265 milliseconds to fetch the documents.Query took 1016 milliseconds to fetch the documents.Query took 1524 milliseconds to fetch the documents.Query took 286 milliseconds to fetch the documents.Query took 954 milliseconds to fetch the documents.
--------------------------------------------------------------------------------
/4-error-handling/Models/movieModel.js:
--------------------------------------------------------------------------------
1 | const mongoose = require('mongoose');
2 | const fs = require('fs');
3 | const validator = require('validator');
4 |
5 | const movieSchema = new mongoose.Schema({
6 | name: {
7 | type: String,
8 | required: [true, 'Name is required field!'],
9 | unique: true,
10 | maxlength: [100, "Movie name must not have more than 100 characters"],
11 | minlength: [4, "Movie name must have at least 4 charachters"],
12 | trim: true,
13 | //validate: [validator.isAlpha, "Name should only contain alphabets."]
14 | },
15 | description: {
16 | type: String,
17 | required: [true, 'Description is required field!'],
18 | trim: true
19 | },
20 | duration: {
21 | type: Number,
22 | required: [true, 'Duration is required field!']
23 | },
24 | ratings: {
25 | type: Number,
26 | validate: {
27 | validator: function(value){
28 | return value >= 1 && value <= 10;
29 | },
30 | message: "Ratings ({VALUE}) should be above 1 and below 10"
31 | }
32 | },
33 | totalRating: {
34 | type: Number
35 | },
36 | releaseYear: {
37 | type: Number,
38 | required: [true, 'Release year is required field!']
39 | },
40 | releaseDate:{
41 | type: Date
42 | },
43 | createdAt: {
44 | type: Date,
45 | default: Date.now(),
46 | select: false
47 | },
48 | genres: {
49 | type: [String],
50 | required: [true, 'Genres is required field!'],
51 | // enum: {
52 | // values: ["Action", "Adventure", "Sci-Fi", "Thriller", "Crime", "Drama", "Comedy", "Romance", "Biography"],
53 | // message: "This genre does not exist"
54 | // }
55 | },
56 | directors: {
57 | type: [String],
58 | required: [true, 'Directors is required field!']
59 | },
60 | coverImage:{
61 | type: String,
62 | require: [true, 'Cover image is required field!']
63 | },
64 | actors: {
65 | type: [String],
66 | require: [true, 'actors is required field!']
67 | },
68 | price: {
69 | type: Number,
70 | require: [true, 'Price is required field!']
71 | },
72 | createdBy: String
73 | }, {
74 | toJSON: {virtuals: true},
75 | toObject: {virtuals: true}
76 | });
77 |
78 | movieSchema.virtual('durationInHours').get(function(){
79 | return this.duration / 60;
80 | })
81 |
82 | //EXECUTED BEFORE THE DOCUMENT IS SAVED IN DB
83 | //.save() or .create()
84 | //inserMany, findByIdAndUpdate will not work
85 | movieSchema.pre('save', function(next) {
86 | this.createdBy = 'MANOJJHA';
87 | next();
88 | })
89 |
90 | movieSchema.post('save', function(doc, next){
91 | const content = `A new movie document with name ${doc.name} has been created by ${doc.createdBy}\n`;
92 | fs.writeFileSync('./Log/log.txt', content, {flag: 'a'}, (err) => {
93 | console.log(err.message);
94 | });
95 | next();
96 | });
97 |
98 | movieSchema.pre(/^find/, function(next){
99 | this.find({releaseDate: {$lte: Date.now()}});
100 | this.startTime = Date.now()
101 | next();
102 | });
103 |
104 | movieSchema.post(/^find/, function(docs, next){
105 | this.find({releaseDate: {$lte: Date.now()}});
106 | this.endTime = Date.now();
107 |
108 | const content = `Query took ${this.endTime - this.startTime} milliseconds to fetch the documents.`
109 | fs.writeFileSync('./Log/log.txt', content, {flag: 'a'}, (err) => {
110 | console.log(err.message);
111 | });
112 |
113 | next();
114 | });
115 |
116 | movieSchema.pre('aggregate', function(next){
117 | console.log(this.pipeline().unshift({ $match: {releaseDate: {$lte: new Date()}}}));
118 | next();
119 | });
120 |
121 |
122 | const Movie = mongoose.model('Movie', movieSchema);
123 |
124 | module.exports = Movie;
--------------------------------------------------------------------------------
/4-error-handling/Models/userModel.js:
--------------------------------------------------------------------------------
1 | const mongoose = require('mongoose');
2 | const validator = require('validator');
3 |
4 | //name, email, password, confirmPassword, photo
5 | const userSchema = new mongoose.Schema({
6 | name: {
7 | type: String,
8 | required: [true, 'Please enter your name.']
9 | },
10 | email: {
11 | type: String,
12 | required: [true, 'Please enter an email.'],
13 | unique: true,
14 | lowercase: true,
15 | validate: [validator.isEmail, 'Please enter a valid email.']
16 | },
17 | photo: String,
18 | password: {
19 | type: String,
20 | required: [true, 'Please enter a password.'],
21 | minlength: 8
22 | },
23 | confirmPassword: {
24 | type: String,
25 | required: [true, 'Please confirm your password.']
26 | }
27 | })
28 |
29 | const User = mongoose.model('User', userSchema);
30 |
31 | module.exports = User;
--------------------------------------------------------------------------------
/4-error-handling/Routes/authRouter.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const authController = require('./../Controllers/authController')
3 |
4 | const router = express.Router();
5 |
6 | router.route('/signup').post(authController.signup);
7 |
8 | module.exports = router;
--------------------------------------------------------------------------------
/4-error-handling/Routes/moviesRoutes.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 | const moviesController = require('./../Controllers/moviesController');
3 |
4 | const router = express.Router();
5 |
6 | //router.param('id', moviesController.checkId)
7 |
8 | router.route('/highest-rated').get(moviesController.getHighestRated, moviesController.getAllMovies)
9 |
10 | router.route('/movie-stats').get(moviesController.getMovieStats);
11 |
12 | router.route('/movies-by-genre/:genre').get(moviesController.getMovieByGenre);
13 |
14 | router.route('/')
15 | .get((moviesController.getAllMovies))
16 | .post(moviesController.createMovie)
17 |
18 |
19 | router.route('/:id')
20 | .get(moviesController.getMovie)
21 | .patch(moviesController.updateMovie)
22 | .delete(moviesController.deleteMovie)
23 |
24 | module.exports = router;
--------------------------------------------------------------------------------
/4-error-handling/Utils/ApiFeatures.js:
--------------------------------------------------------------------------------
1 | class Apifeatures{
2 | constructor(query, queryStr){
3 | this.query = query;
4 | this.queryStr = queryStr;
5 | }
6 |
7 | filter(){
8 | let queryString = JSON.stringify(this.queryStr);
9 | queryString = queryString.replace(/\b(gte|gt|lte|lt)\b/g, (match) => `$${match}`);
10 | const queryObj = JSON.parse(queryString);
11 |
12 | this.query = this.query.find(queryObj);
13 |
14 | return this;
15 | }
16 |
17 | sort(){
18 | if(this.queryStr.sort){
19 | const sortBy = this.queryStr.sort.split(',').join(' ');
20 | this.query = this.query.sort(sortBy);
21 | }else{
22 | this.query = this.query.sort('-createdAt');
23 | }
24 |
25 | return this;
26 | }
27 |
28 | limitFields(){
29 | if(this.queryStr.fields){
30 | const fields = this.queryStr.fields.split(',').join(' ');
31 | this.query = this.query.select(fields);
32 | }else{
33 | this.query = this.query.select('-__v');
34 | }
35 |
36 | return this;
37 | }
38 |
39 | paginate(){
40 | const page = this.queryStr.page*1 || 1;
41 | const limit = this.queryStr.limit*1 || 10;
42 | const skip = (page -1) * limit;
43 | this.query = this.query.skip(skip).limit(limit);
44 |
45 | // if(this.queryStr.page){
46 | // const moviesCount = await Movie.countDocuments();
47 | // if(skip >= moviesCount){
48 | // throw new Error("This page is not found!");
49 | // }
50 | // }
51 |
52 | return this;
53 | }
54 | }
55 |
56 | module.exports = Apifeatures;
--------------------------------------------------------------------------------
/4-error-handling/Utils/CustomError.js:
--------------------------------------------------------------------------------
1 | class CustomError extends Error{
2 | constructor(message, statusCode){
3 | super(message);
4 | this.statusCode = statusCode;
5 | this.status = statusCode >= 400 && statusCode < 500 ? 'fail' : 'error';
6 |
7 | this.isOperational = true;
8 |
9 | Error.captureStackTrace(this, this.constructor);
10 | }
11 | }
12 |
13 | module.exports = CustomError;
14 |
15 | //const error = new CustomError('some error message', 404)
--------------------------------------------------------------------------------
/4-error-handling/Utils/asyncErrorHandler.js:
--------------------------------------------------------------------------------
1 | module.exports = (func) => {
2 | return (req, res, next) => {
3 | func(req, res, next).catch(err => next(err));
4 | }
5 | }
--------------------------------------------------------------------------------
/4-error-handling/app.js:
--------------------------------------------------------------------------------
1 | //IMPORT PACKAGE
2 | const express = require('express');
3 | const morgan = require('morgan');
4 | const moviesRouter = require('./Routes/moviesRoutes');
5 | const authRouter = require('./Routes/authRouter')
6 | const CustomError = require('./Utils/CustomError');
7 | const globalErrorHandler = require('./Controllers/errorController')
8 |
9 | let app = express();
10 |
11 | app.use(express.json());
12 |
13 | app.use(express.static('./public'))
14 |
15 | //USING ROUTES
16 |
17 |
18 | app.use('/api/v1/movies', moviesRouter);
19 | app.use('/api/v1/users', authRouter);
20 | app.all('*', (req, res, next) => {
21 | // res.status(404).json({
22 | // status: 'fail',
23 | // message: `Can't find ${req.originalUrl} on the server!`
24 | // });
25 | // const err = new Error(`Can't find ${req.originalUrl} on the server!`);
26 | // err.status = 'fail';
27 | // err.statusCode = 404;
28 | const err = new CustomError(`Can't find ${req.originalUrl} on the server!`, 404);
29 | next(err);
30 | });
31 |
32 | app.use(globalErrorHandler);
33 |
34 | module.exports = app;
35 |
36 |
--------------------------------------------------------------------------------
/4-error-handling/config.env:
--------------------------------------------------------------------------------
1 | NODE_ENV=development
2 | PORT=3000
3 | CONN_STR=mongodb+srv://admin:n9Hb3bOrVoF4pGKw@cluster0.wvot8r4.mongodb.net/cineflix?retryWrites=true&w=majority
4 | LOCAL_CONN_STR=mongodb://localhost:27017/cineflex
5 | DB_USER=admin
6 | DB_PASSWORD=n9Hb3bOrVoF4pGKw
--------------------------------------------------------------------------------
/4-error-handling/data/import-dev-data.js:
--------------------------------------------------------------------------------
1 | const mongoose = require('mongoose');
2 | const dotenv = require('dotenv');
3 | const fs = require('fs');
4 | const Movie = require('./../Models/movieModel');
5 |
6 | dotenv.config({path: './config.env'});
7 |
8 | //CONNECT TO MONGODB
9 | mongoose.connect(process.env.CONN_STR, {
10 | useNewUrlParser: true
11 | }).then((conn) => {
12 | console.log('DB Connection Successful');
13 | }).catch((error) => {
14 | console.log('Some error has occured');
15 | });
16 |
17 | //READ MOVIES.JSON FILE
18 | const movies = JSON.parse(fs.readFileSync('./data/movies.json', 'utf-8'));
19 |
20 | //DELETE EXISTING MOVIE DOCUMENTS FROM COLLECTION
21 | const deleteMovies = async () => {
22 | try{
23 | await Movie.deleteMany();
24 | console.log('Data successfully deleted!');
25 | }catch(err){
26 | console.log(err.message);
27 | }
28 | process.exit();
29 | }
30 |
31 | //IMPORT MOVIES DATA TO MONGODB COLLECTION
32 | const importMovies = async () => {
33 | try{
34 | await Movie.create(movies);
35 | console.log('Data successfully imported!');
36 | }catch(err){
37 | console.log(err.message);
38 | }
39 | process.exit();
40 | }
41 |
42 | if(process.argv[2] === '--import'){
43 | importMovies();
44 | }
45 | if(process.argv[2] === '--delete'){
46 | deleteMovies();
47 | }
--------------------------------------------------------------------------------
/4-error-handling/data/movies.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "name": "Riddick",
4 | "description": "Left for dead on a sun-scorched planet, Riddick finds himself up against an alien race of predators. Activating an emergency beacon alerts two ships: one carrying a new breed of mercenary, the other captained by a man from Riddick's past.",
5 | "duration": 119,
6 | "ratings": 6.8,
7 | "totalRating": 329,
8 | "releaseYear": 2013,
9 | "releaseDate": "2013-09-04T00:00:00Z",
10 | "genres": [
11 | "Action",
12 | "Sci-Fi",
13 | "Thriller"
14 | ],
15 | "directors": [ "David Twohy" ],
16 | "coverImage": "Riddick-2013.jpg",
17 | "actors": [
18 | "Vin Diesel",
19 | "Karl Urban",
20 | "Katee Sackhoff"
21 | ],
22 | "price": 50
23 | },
24 | {
25 | "releaseYear": 2013,
26 | "name": "We're the Millers",
27 | "directors": [ "Rawson Marshall Thurber" ],
28 | "releaseDate": "2013-08-03T00:00:00Z",
29 | "ratings": 7.2,
30 | "totalRating": 230,
31 | "genres": [
32 | "Comedy",
33 | "Crime"
34 | ],
35 | "coverImage": "we-are-the-millers-2013.jpg",
36 | "description": "A veteran pot dealer creates a fake family as part of his plan to move a huge shipment of weed into the U.S. from Mexico.",
37 | "duration": 110,
38 | "actors": [
39 | "Jason Sudeikis",
40 | "Jennifer Aniston",
41 | "Emma Roberts"
42 | ],
43 | "price": 47
44 | },
45 | {
46 | "releaseYear": 2014,
47 | "name": "Divergent",
48 | "directors": [ "Neil Burger" ],
49 | "releaseDate": "2014-03-20T00:00:00Z",
50 | "ratings": 6.4,
51 | "totalRating": 164,
52 | "genres": [
53 | "Action",
54 | "Adventure",
55 | "Romance",
56 | "Sci-Fi"
57 | ],
58 | "coverImage": "Divergent-2014.jpg",
59 | "description": "Beatrice Prior, a teenager with a special mind, finds her life threatened when an authoritarian leader seeks to exterminate her kind in her effort to seize control of their divided society.",
60 | "duration": 117,
61 | "actors": [
62 | "Shailene Woodley",
63 | "Kate Winslet",
64 | "Zoe Kravitz"
65 | ],
66 | "price": 52
67 | },
68 | {
69 | "releaseYear": 2013,
70 | "name": "Elysium",
71 | "directors": [ "Neill Blomkamp" ],
72 | "releaseDate": "2013-08-07T00:00:00Z",
73 | "ratings": 7,
74 | "totalRating": 436,
75 | "genres": [
76 | "Action",
77 | "Drama",
78 | "Sci-Fi",
79 | "Thriller"
80 | ],
81 | "coverImage": "Elysium-2013.jpg",
82 | "description": "Set in the year 2154, where the very wealthy live on a man-made space station while the rest of the population resides on a ruined Earth, a man takes on a mission that could bring equality to the polarized worlds.",
83 | "duration": 109,
84 | "actors": [
85 | "Matt Damon",
86 | "Jodie Foster",
87 | "Sharlto Copley"
88 | ],
89 | "price": 65
90 | },
91 | {
92 | "releaseYear": 2014,
93 | "name": "RoboCop",
94 | "directors": [ "Jose Padilha" ],
95 | "releaseDate": "2014-01-30T00:00:00Z",
96 | "ratings": 7.7,
97 | "totalRating": 310,
98 | "genres": [
99 | "Action",
100 | "Crime",
101 | "Sci-Fi",
102 | "Thriller"
103 | ],
104 | "coverImage": "RoboCop-2014.jpg",
105 | "description": "In 2028 Detroit, when Alex Murphy (Joel Kinnaman) - a loving husband, father and good cop - is critically injured in the line of duty, the multinational conglomerate OmniCorp sees their chance for a part-man, part-robot police officer.",
106 | "duration": 110,
107 | "actors": [
108 | "Joel Kinnaman",
109 | "Douglas Urbanski",
110 | "Abbie Cornish"
111 | ],
112 | "price": 40
113 | },
114 | {
115 | "releaseYear": 2014,
116 | "name": "Captain America: The Winter Soldier",
117 | "directors": [
118 | "Anthony Russo",
119 | "Joe Russo"
120 | ],
121 | "releaseDate": "2014-04-02T00:00:00Z",
122 | "ratings": 7.9,
123 | "totalRating": 239,
124 | "genres": [
125 | "Action",
126 | "Adventure",
127 | "Sci-Fi"
128 | ],
129 | "coverImage": "captain-america-the-winter-soldier-2014.jpg",
130 | "description": "Steve Rogers struggles to embrace his role in the modern world and teams up with Natasha Romanoff, aka Black Widow, to battle a powerful yet shadowy enemy in present-day Washington, D.C.",
131 | "duration": 135,
132 | "actors": [
133 | "Chris Evans",
134 | "Frank Grillo",
135 | "Sebastian Stan"
136 | ],
137 | "price": 57
138 | },
139 | {
140 | "releaseYear": 2013,
141 | "name": "Behind the Candelabra",
142 | "directors": [ "Steven Soderbergh" ],
143 | "releaseDate": "2013-05-21T00:00:00Z",
144 | "ratings": 7,
145 | "totalRating": 325,
146 | "genres": [
147 | "Biography",
148 | "Drama",
149 | "Romance"
150 | ],
151 | "coverImage": "behind-the-candelabra-2013.jpg",
152 | "description": "Based on the autobiographical novel, the tempestuous 6-year relationship between Liberace and his (much younger) lover, Scott Thorson, is recounted.",
153 | "duration": 118,
154 | "actors": [
155 | "Matt Damon",
156 | "Scott Bakula",
157 | "Eric Zuckerman"
158 | ],
159 | "price": 59
160 | },
161 | {
162 | "releaseYear": 2012,
163 | "name": "The Dark Knight Rises",
164 | "directors": [ "Christopher Nolan" ],
165 | "releaseDate": "2012-07-16T00:00:00Z",
166 | "ratings": 8.6,
167 | "totalRating": 530,
168 | "genres": [
169 | "Action",
170 | "Crime",
171 | "Thriller"
172 | ],
173 | "coverImage": "the-dark-knight-rises-2012.jpg",
174 | "description": "Eight years on, a new evil rises from where the Batman and Commissioner Gordon tried to bury it, causing the Batman to resurface and fight to protect Gotham City... the very city which brands him an enemy.",
175 | "duration": 165,
176 | "actors": [
177 | "Christian Bale",
178 | "Tom Hardy",
179 | "Anne Hathaway"
180 | ],
181 | "price": 57
182 | }
183 | ]
--------------------------------------------------------------------------------
/4-error-handling/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "node-js-with-express",
3 | "version": "1.0.0",
4 | "description": "Learning express js with node",
5 | "main": "app.js",
6 | "scripts": {
7 | "start": "SET NODE_ENV=development& nodemon server.js",
8 | "start_prod": "SET NODE_ENV=production& nodemon server.js"
9 | },
10 | "author": "Manoj Jha",
11 | "license": "ISC",
12 | "dependencies": {
13 | "dotenv": "^16.0.3",
14 | "express": "^4.18.2",
15 | "mongoose": "^6.9.2",
16 | "morgan": "^1.10.0",
17 | "nodemon": "^2.0.20",
18 | "validator": "^13.9.0"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/4-error-handling/public/css/style.css:
--------------------------------------------------------------------------------
1 | body{
2 | font-family: 'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans Unicode', Geneva, Verdana, sans-serif;
3 | }
4 |
5 | h2{
6 | color: grey;
7 | }
8 |
9 | div.card {
10 | box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
11 | text-align: center;
12 | border-radius: 5px;
13 | margin: 50px 200px;
14 | padding: 20px 30px;
15 | }
--------------------------------------------------------------------------------
/4-error-handling/public/templates/demo.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | serving static file
9 |
10 |
11 |
12 |
Welcome to Demo API
13 |
This is a demo mivies API built using Express JS and a JSON file as a data source.
14 |
This is a simple demo HTML page to demo serving static files.
15 |
16 |
17 |
--------------------------------------------------------------------------------
/4-error-handling/server.js:
--------------------------------------------------------------------------------
1 | const dotenv = require('dotenv');
2 | dotenv.config({path: './config.env'});
3 | const mongoose = require('mongoose');
4 |
5 | process.on('uncaughtException', (err) => {
6 | console.log(err.name, err.message);
7 | console.log('Uncaught Exception occured! Shutting down...');
8 | process.exit(1);
9 | })
10 |
11 | const app = require('./app');
12 |
13 | //console.log(app.get('env'));
14 | console.log(process.env);
15 |
16 | mongoose.connect(process.env.CONN_STR, {
17 | useNewUrlParser: true
18 | }).then((conn) => {
19 | //console.log(conn);
20 | console.log('DB Connection Successful');
21 | })
22 |
23 | const port = process.env.PORT || 3000;
24 |
25 | const server = app.listen(port, () => {
26 | console.log('server has started...');
27 | })
28 |
29 | process.on('unhandledRejection', (err) => {
30 | console.log(err.name, err.message);
31 | console.log('Unhandled rejection occured! Shutting down...');
32 |
33 | server.close(() => {
34 | process.exit(1);
35 | })
36 | })
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/4-error-handling/test.txt:
--------------------------------------------------------------------------------
1 | Test
2 |
--------------------------------------------------------------------------------