Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

NodeJS

NodeJS basic

What is NodeJS ?

  • Node is a runtime environment for executing JavaScript code.
  • Non-blocking (asynchronous) architecture.
  • It has an event loop with event queue.
  • It is good for network-intensive apps, but not good for CPU intensive applications.
  • Running on top of the V8 Virtual Machine written by Google, which is also the JavaScript engine in Chrome.

NodeJS Installation

NodeJS Installation Linux

By the time you are reading this, there might have a newer LTS version already.

export NODEJS=v14.15.1
wget https://nodejs.org/dist/$NODEJS/node-$NODEJS-linux-x64.tar.xz
tar xf node-$NODEJS-linux-x64.tar.xz
sudo mv node-$NODEJS-linux-x64/ /opt
sudo chown -R root.root /opt/node-$NODEJS-linux-x64/
sudo ln -s /opt/node-$NODEJS-linux-x64 /opt/node

Add it to the PATH:

echo "export PATH=\$PATH:/opt/node/bin" >> ~/.bashrc

NodeJS version

  • node
$ which node
/opt/node/bin/node

$ node -v
v14.15.1

Hello World

  • console
  • log
console.log("Hello World");
$ node examples/basic/hello_world.js
Hello World

Comments

  • //
console.log("Start here")

// this is a comment

// console.log("This line is not in use")

console.log("Still working")  // But comment here

/*
Multi-line comment

console.log("Maybe with code")

*/

console.log("End here")
Start here
Still working
End here

Exercise: Hello World

  • The primary goal of this exercise is to make sure you have your environment ready to write more code.
  • Create a file called hello.js and make it print "Hello World".
  • Run the code from the command line.
  • Run the code from your IDE, if you use one.
  • Add some comments to your code and check if you can still run it.

Hello World with sh

Unix-specific issue:

#!/usr/bin/env node

console.log("Hello World");

Make the file executable:

chmod +x examples/basic/hello_world_sh.js

Run it directly

./examples/basic/hello_world_sh.js

Literal Values and types

  • typeof
  • number
  • string
  • boolean

console.log(42);          // 42
console.log(3.14);        // 3.14
console.log("some text"); // some text
console.log('more text'); // more text
console.log("42");        // 42
console.log(true);        // true
console.log("true");      // true

console.log('')
console.log(typeof 42);     // number
console.log(typeof 3.14);   // number
console.log(typeof "text"); // string
console.log(typeof 'text'); // string
console.log(typeof "42");   // string
console.log(typeof true);   // boolean
console.log(typeof "true"); // string

Declare variables with let

  • let
let x = 19
let y = 23
let res = x + y
console.log(res)  // 42

Hello World in function

  • function
function sayHello() {
    console.log('Hello World')
}

sayHello()
Hello World

Parameter passing to function

function sayHello(name) {
    console.log('Hello ' + name)
}

sayHello('Kate')


Hello Kate

for loop

  • for
for (let ix = 0; ix < 5; ix++) {
    console.log(ix);
}

0
1
2
3
4

Array

var solar = ["Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptunus"]
console.log(solar)

console.log(solar[0])

Command line arguments (argv)

  • process
  • argv

Throught the process object we can access the command line of our program. The first value is node itself. The second value is our code, then come the values from that the user supplied.

console.log(process.argv.length);
console.log(process.argv);
console.log('')

console.log(process.argv[0]);
console.log(process.argv[1]);
console.log(process.argv[2]);
console.log(process.argv[3]);
console.log(process.argv[4]);
$ node argv.js "hello world" Foo
4
[
  '/opt/node-v12.14.1-linux-x64/bin/node',
  '/home/gabor/work/slides/nodejs/examples/basic/argv.js',
  'hello world',
  'Foo'
]

/opt/node-v12.14.1-linux-x64/bin/node
/home/gabor/work/slides/nodejs/examples/basic/argv.js
hello world
Foo
undefined

Command line arguments - forEach (argv)

  • forEach

argv

Try: node argv_foreach.js Hello my world

process.argv.forEach((val, index) => {
    console.log(`${index}: ${val}`);
});
0: /opt/node-v12.14.1-linux-x64/bin/node
1: /home/gabor/work/slides/nodejs/examples/basic/argv_foreach.js

Define function with arrow notation

const sum = (a, b) => a + b;

console.log( sum(2, 3) )
5

setTimeout - delayed execution

  • setTimeout

Delayed execution, callback function.

  • First we use setTimeout to schedule an anonymous function to be executed 1000 ms later.
  • Then we print "hello" to the console.
  • Then, after a second, the functions starts running and prints "world"
setTimeout(function() {
    console.log("world");
}, 1000);
console.log("hello");
$ node examples/basic/with_timeout.js
hello
world

setInterval - scheduled execution

  • setInterval

  • setInterval allows for repeated calls. We can stop this infinite calling using Ctrl-C.

setInterval(function() {
    console.log("world");
}, 1000);
console.log("hello");

$ node examples/basic/with_interval.js
hello
world
world
world
^C

clearInterval

  • clearInterval

  • setInterval

  • setTimeout

  • setInterval returns an identifier that can later be used to stop the scheduled process.

  • clearInterval will stop a scheduled process.

var intervalId = setInterval(function() {
    console.log("world");
}, 1000);

setTimeout(function() {
    console.log("Calling clearInterval");
    clearInterval(intervalId);
}, 3030);

console.log("hello");
$ node examples/basic/with_clearinterval.js
hello
world
world
world
Calling clearInterval

clearTimeout

  • clearTimeout
var t3000 = setTimeout(function() {
    console.log("Hello 3000");
}, 3000)

var t2000 = setTimeout(function() {
    console.log("Hello 2000");
}, 2000)

var t1000 = setTimeout(function() {
    console.log("Hello 1000");
    clearTimeout(t2000);
    console.log("cleared 2000");
}, 1000)

console.log("start")
start
Hello 1000
cleared 2000
Hello 3000

Template literals (template strings) - variable interpolation


let name = 'Kate'
console.log(`Hello ${name}`)
$ node examples/basic/template_literals.js
Hello Kate

Scope of variables and constants

  • let
  • var
  • const

There are several ways to start declaring and using variables and constants. In general the best is to use the let keyword.

x = "Joe"
var y = "Jane"
let z = "George"
const w = "nothing"

console.log(x) // Joe
console.log(y) // Jane
console.log(z) // George
console.log(w) // nothing




constant (const)

  • const

const pi = 3.14;

console.log(pi);

pi = 3.15;
$ node examples/basic/const.js
3.14
/home/gabor/work/slides/nodejs/examples/basic/const.js:6
pi = 3.15;
   ^

TypeError: Assignment to constant variable.
    at Object.<anonymous> (/home/gabor/work/slides/nodejs/examples/basic/const.js:6:4)
    at Module._compile (internal/modules/cjs/loader.js:776:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:787:10)
    at Module.load (internal/modules/cjs/loader.js:653:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:593:12)
    at Function.Module._load (internal/modules/cjs/loader.js:585:3)
    at Function.Module.runMain (internal/modules/cjs/loader.js:829:12)
    at startup (internal/bootstrap/node.js:283:19)
    at bootstrapNodeJSCore (internal/bootstrap/node.js:622:3)

var

  • var
var x = 1;

console.log(x);      // 1

if (true) {
    console.log(x);  // 1
    x = 2;
    console.log(x);  // 2
}
console.log(x);      // 2
var x = 1;

console.log(x);      // 1

if (true) {
    console.log(x);  // 1
    var x = 2;
    console.log(x);  // 2
}
console.log(x);      // 2
x = 1;

console.log(x);      // 1

if (true) {
    console.log(x);  // 1
    x = 2;
    console.log(x);  // 2
}
console.log(x);      // 2

let

  • let
let x = 1;

console.log(x);      // 1

if (true) {
    console.log(x);  // 1
    x = 2;
    console.log(x);  // 2
}
console.log(x);      // 2
let x = 1;

console.log(x);      // 1

if (true) {
    //console.log(x);  // ReferenceError: x is not defined
    let x = 2;
    console.log(x);  // 2
}
console.log(x);      // 1

var let

var x = 1;

console.log(x);      // 1

if (true) {
    // console.log(x);  // ReferenceError: x is not defined
    let x = 2;
    console.log(x);  // 2
}
console.log(x);      // 1

let var

let x = 1;

console.log(x);      //

if (true) {
    console.log(x);  //
    var x = 2;       // SyntaxError: Identifier 'x' has already been declared
    console.log(x);  //
}
console.log(x);      //

Create library

  • require
function say_hi(name) {
    console.log('Hello ' + name)
}

console.log('Loading module')

module.exports.say_hi = say_hi;
const lib = require('./lib')

console.log('Hello World')

lib.say_hi('Foo')
$ node examples/basic/app.js

Loading module
Hello World
Hello Foo

It is better to define as constant so we won't change it by mistake

Function as a Library

From a module we can export a module or a single function like this: // module.exports = hi;

function say_hi(name) {
    console.log('Hello ' + name)
}

console.log('Loading module')

module.exports = say_hi;
const say_hi = require('./lib2')

console.log('Hello World')

say_hi('Foo')
$ node examples/basic/app2.js

Loading module
Hello World
Hello Foo

Loading a library twice

  • Loads it only once but allows access to it from several names.
const say_hi = require('./lib2');

function other() {
    const say_hello = require('./lib2');
    say_hello('Bar');
}


console.log('Hello World');
say_hi('Foo');

other();


$ node examples/basic/app22.js

Loading module
Hello World
Hello Foo
Hello Bar

What is in a module?

  • module

  • The module object represents the current module

  • See more about modules


console.log(module)

OS module

  • os

  • totalmem

  • freemem

  • uptime

  • loadavg

  • platform

  • hostname

  • type

  • tmpdir

  • networkInterfaces

  • A bunch of OS-related methods

  • os

const os = require('os')

console.log(os.totalmem())
console.log(os.freemem())
console.log(os.uptime())
console.log(os.loadavg())


console.log(os.platform())
console.log(os.hostname())
console.log(os.type())
console.log(os.tmpdir())

console.log(os.networkInterfaces())

The path related tools

Path to the current file

  • __filename
  • __dirname
console.log(__filename);     // path to this file
console.log(__dirname);      // path to the directory of this file

Relative path inside a project

  • path
  • dirname
  • join
project_dir/
   /bin/
       code.js
   /templates
       main.html
const path = require('path');

console.log(__filename);
console.log(__dirname);

const root = path.dirname(__dirname)
console.log(root);

const templates = path.join(root, 'templates');
console.log(templates);

const rel_path_to_file = path.join(root, 'templates', 'main.html');
console.log(rel_path_to_file);

Exception

  • throw
  • Error
throw new Error("a problem, we have");

REPL

node
>
...
Ctrl-d

Debugging

Add "debugger;" to the code. That will set a breakpoint if running under the debugger. node debug code.js debug> help

debug> quit

Callback

function call_back(cb) {
    console.log("Start call_back");
    cb();
    //console.log("End call_back");
}

function helper() {
    console.log("In helper function");
}

call_back(function() {
    console.log("Inside anonymous function");
})

call_back(helper);

Closure


function createCounter(n) {
    function counter(v) {
        return v + n;
    }
    return counter;
}

var counter_3 = createCounter(3);

console.log(counter_3(7));

var counter_4 = createCounter(4);

console.log(counter_3(7));
console.log(counter_4(7));

File-system related operations (fs)

File-system related operations (fs)

  • fs

fs

  • readFile
  • writeFile
  • appendFile
  • unlink (delete file)
  • createReadStream
  • createWriteStream
  • mkdir
  • rmdir
  • readdirSync
  • readdir
  • rename

Read file

  • readFile
const fs = require('fs');

fs.readFile('data.txt', 'utf8', (err, content) => {
    if (err) {
        console.log(err);
    } else {
        console.log(content);
    }
});

Write file

  • writeFile
const fs = require('fs');

fs.writeFile('data.txt', 'first line', (err) => {
    if (err) {
        console.log(err);
    } else {
        console.log('Success');
    }
});

Append to file

  • appendFile
const fs = require('fs');

fs.appendFile('filename', 'content', (err) => {
    if (err) {
        console.log(err);
    } else {
        console.log('Success');
    }
});

Delete file (unlink file)

  • unlink
const fs = require('fs');

fs.unlink(filename, (err) => {
    if (err) {
        console.log(err);
    } else {
        console.log('Success');
    }
});

Read (and write) file by chunks

  • createReadStream
  • createWriteStream
  • on
  • write
const fs = require('fs');

const readStream = fs.createReadStream('README', 'utf8'); // inherits from events
const writeStream = fs.createWriteStream('README.txt');
readStream.on('data', (chunk) => {
    if (chunk) {
        writeStream.write(chunk);
    }
});

Read (and write) file by chunks using pipe

  • pipe
const fs = require('fs');

const readStream = fs.createReadStream('README', 'utf8'); // inherits from events
const writeStream = fs.createWriteStream('README.txt');
readStream.pipe(writeStream);

Create a directory (folder) (mkdir)

  • mkdir
const fs = require('fs');

fs.mkdir(dirname, (err) => {

    if (err) {
        console.log(err);
    } else {
        console.log('Success');
    }
});

Remove a directory (folder) (rmdir)

  • rmdir
const fs = require('fs');

fs.rmdir(dirname, (err) => {
    if (err) {
        console.log(err);
    } else {
        console.log('Success');
    }
});

Read directory sync (readdirSync)

  • readdirSync
const fs = require('fs')

let files = fs.readdirSync('.')
console.log(files)

Read directory async (readdir)

  • readdir
const fs = require('fs')

fs.readdir('.', function (err, things) {
    console.log(things)
});

Read directory async error handline

  • readdir
const fs = require('fs')

fs.readdir('xyz', function (err, files) {
    if (err) {
        console.log('Error', err);
    } else {
        console.log(files);
    }
})

Rename a file (rename)

  • rename
fs.rename(from, to, (err) => {})
const fs = require('fs');

fs.rename('old.txt', 'new.txt', (err) => {
    if (err) {
        console.log(err);
    } else {
        console.log('Success');
    }

});

OOP

Class

  • new
  • class
  • constructor
class MyClass {
    constructor() {
        console.log('object created');
    }
}

function f() {
    const n = new MyClass();
    console.log(n);
}

console.log('before');
f();
console.log('after');

HTTP

HTTP Hello World

  • http
  • createServer
  • listen
const http = require('http');     // A subclass of the emitter

const server = http.createServer((req, res) => {
    if (req.url === '/') {
        res.write('Hello World');
        res.end();
    }
});
const port = 3000;

server.listen(port);

console.log(`Listening on port ${port}`);

HTTP Return JSON

  • writeHead
  • Content-type
const http = require('http');

const server = http.createServer((req, res) => {
    if (req.url === '/') {
        res.writeHead(200, { 'Content-Type': 'text/html' });
        res.write('Hello World <a href="/api/users">users</a>');
        res.end();
    }
    if (req.url === '/api/users') {
        res.writeHead(200, { 'Content-Type': 'application/json' });
        res.write(JSON.stringify([1, 2, 3]));
        res.end();
    }
});
const port = 3000;

server.listen(port);

console.log(`Listening on port ${port}`);

HTTP Server

var http = require('http');   // Load the http library

var s = http.createServer(function(req, res) {
    res.writeHeader(200, { 'Content-Type' : 'text/plain' });
    res.end('Hello World\n');

});

s.listen(8000);

$ curl http://localhost:8000
Hello World

$ curl -i http://localhost:8000
HTTP/1.1 200 OK
content-type: text/plain
Date: Thu, 30 Jan 2014 21:03:36 GMT
Connection: keep-alive
Transfer-Encoding: chunked

Hello World

Point out the Connection: keep-alive - in modern web you can send several requsts on the same connection Transfer-Encoding: chunked - streaming

Transfer-Encoding: chunked

var http = require('http');   // Load the http library

var s = http.createServer(function(req, res) {
    res.writeHeader(200, { 'Content-Type' : 'text/plain' });
    res.write('Hello\n');
    setTimeout(function() {
        res.end('World\n');
    }, 2000);

});

s.listen(8000);


In this example, if we fetch it with curl, we'll see the Hello arrives first and then after 2 seconds world arrives. This is what has been provided by the chunked parameter. This script might fetch some data from a database or from some other source. Instead of building up the whole response in the server and then sening it out only when it is ready, we send it out in chunks as parts of it got ready.

If there was a Content-Length the client could know when the data ends, but with chunked budy it has to use the http://en.wikipedia.org/wiki/Chunked_transfer_encoding which basically means, when a chunk of size 0 arrives that's the end of the current document.

Apache bench: ab -n 100 -c 100 http://127.0.0.1:8000/

Mashup

While one process is still "running" and printing "world" every 5 seconds, we add a new "process" fetching a website every 2 seconds. It just works.

setInterval(function() {
    console.log("world");
}, 5000);

var http = require('http');
setInterval(function() {
    console.log("fetching google.com");
    http.get({ host: 'google.com' }, function(res) {
        console.log(res.headers);
    });
}, 2000);

console.log("hello");

Net

The net module

TCP Server

const net = require('net');

const server = net.createServer(function(socket) {
    socket.write('hello\n');
    socket.end('world\n');
});

server.listen(8000);

node examples/net/tcp-server.js

$ telnet localhost 8000
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
hello
world
Connection closed by foreign host.

TCP Echo Server

const net = require('net');

const server = net.createServer(function(socket) {
    socket.write('hello\n');
    socket.write('world\n');
    socket.on('data', function(data) {
       socket.write('You said: ' + data);
    })
});

server.listen(8000);

Chat server

var net = require('net');

var sockets = [];

var s = net.Server(function(socket) {
    sockets.push(socket);
    socket.on('data', function(txt) {
        for (var i=0; i < sockets.length; i++) {
            sockets[i].write(txt);
        }
    });
});

s.listen(8000);

This works, but when someone disconnects, the dead socket remains in the array and when we try to write to it it will blow up.

Remove dead sockets

    socket.on('end', function() {
        var i = sockets.indexOf(socket);
        sockets.splice(i, 1)
    });
var net = require('net');

var sockets = [];

var s = net.Server(function(socket) {
    sockets.push(socket);
    socket.on('data', function(txt) {
        for (var i=0; i < sockets.length; i++) {
            sockets[i].write(txt);
        }
    });
    socket.on('end', function() {
        var i = sockets.indexOf(socket);
        sockets.splice(i, 1) 
    });
});

s.listen(8000);

Stop echoing

        if (sockets[i] == socket) continue;
const net = require('net');

var sockets = [];

const sock = net.Server(function(socket) {
    sockets.push(socket);
    socket.on('data', function(txt) {
        for (var i = 0; i < sockets.length; i++) {
            if (sockets[i] === socket) continue;
            sockets[i].write(txt);
        }
    });
    socket.on('end', function() {
        var i = sockets.indexOf(socket);
        sockets.splice(i, 1);
    });
});

sock.listen(8000);

npm

npmjs

  • npm
  • Private npm repository in the cloud: https://gemfury.com/

npm init

  • Create a directory for the project (eg. mynode) and cd into the directory
npm init

This will aske a few questions and create the package.json file.

The questions, and the default answers, you can just accept the defaults.

package name: mynode
version: 1.0.0
description:
entry point: index.js
test command:
git repository:
keywords:
author:
license (ISC):
npm init --yes
  • Would populate the file with the defaults without asking emberassing questions.

package json

{% embed include file="src/examples/manual/package.json)

npm install PACKAGE

npm install module
npm uninstall module
  • This will install in the node_modules folder in the directory where the package.json can be found
  • And it will add the name of the package to packege.json as well declaring it as a dependency
  • It will also create a file called package-lock.json.
  • Both json files should be added to git and the node_modules/ directory should be added to gitignore

Development dependencies

npm install mocha --save-dev
  • It is added to the devDependencies section in the package.json file

Semanic Versioning

^1.2.3   means 1.x.x
~1.2.3  means  1.2.x
1.2.3   exact version number

npm install

  • Based on pacakge.json and package-lock.json this will install all the depdendencies.
  • Useful to recreate the environment.
npm install

npx

Linter - ESLint

npm install eslint --save-dev
npx eslint --init

Creates:

.eslintrc.json

{% embed include file="src/examples/basic/.eslintrc.json)

Run it:

npx eslint *.js

In package.json set:

  "scripts": {
    "test": "npx eslint *.js"
  },

Then run as

$ npm run test

Express

Install Express

npm install express

{% embed include file="src/examples/web-express/hello_world.js)

Testing for NodeJS projects

Testing - Application Under Test

function add(x, y) {
    return x*y
}

function multiply(x, y) {
    return x+y
}

module.exports ={
        add, multiply
}

Using the JavaScript library

const {parseArgs} = require('node:util');
const calc = require("./mycalc.js");


const args = process.argv;
if (args.length != 4) {
    console.log(`Usage ${args[1]} X Y`);
    process.exit(1);
}

console.log(calc.add(args[2], args[3]));
node add.js

Testing Demo with Mocha

npm init -y npm install mocha npm install mocha-cli

{
  "name": "testing-demo",
  "version": "1.0.0",
  "description": "",
  "main": "add.js",
  "scripts": {
    "test": "mocha test"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "mocha": "^10.2.0",
    "mocha-cli": "^1.0.1"
  }
}
{
  "name": "testing-demo",
  "version": "1.0.0",
  "lockfileVersion": 1,
  "requires": true,
  "dependencies": {
    "ansi-colors": {
      "version": "4.1.1",
      "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
      "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA=="
    },
    "ansi-regex": {
      "version": "5.0.1",
      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
      "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
    },
    "ansi-styles": {
      "version": "4.3.0",
      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
      "requires": {
        "color-convert": "^2.0.1"
      }
    },
    "anymatch": {
      "version": "3.1.3",
      "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
      "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
      "requires": {
        "normalize-path": "^3.0.0",
        "picomatch": "^2.0.4"
      }
    },
    "argparse": {
      "version": "2.0.1",
      "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
      "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
    },
    "balanced-match": {
      "version": "1.0.2",
      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
    },
    "binary-extensions": {
      "version": "2.2.0",
      "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
      "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA=="
    },
    "brace-expansion": {
      "version": "1.1.11",
      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
      "requires": {
        "balanced-match": "^1.0.0",
        "concat-map": "0.0.1"
      }
    },
    "braces": {
      "version": "3.0.2",
      "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
      "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
      "requires": {
        "fill-range": "^7.0.1"
      }
    },
    "browser-stdout": {
      "version": "1.3.1",
      "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz",
      "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw=="
    },
    "camelcase": {
      "version": "6.3.0",
      "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
      "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA=="
    },
    "chalk": {
      "version": "4.1.2",
      "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
      "requires": {
        "ansi-styles": "^4.1.0",
        "supports-color": "^7.1.0"
      },
      "dependencies": {
        "supports-color": {
          "version": "7.2.0",
          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
          "requires": {
            "has-flag": "^4.0.0"
          }
        }
      }
    },
    "chokidar": {
      "version": "3.5.3",
      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
      "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
      "requires": {
        "anymatch": "~3.1.2",
        "braces": "~3.0.2",
        "fsevents": "~2.3.2",
        "glob-parent": "~5.1.2",
        "is-binary-path": "~2.1.0",
        "is-glob": "~4.0.1",
        "normalize-path": "~3.0.0",
        "readdirp": "~3.6.0"
      }
    },
    "cliui": {
      "version": "7.0.4",
      "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
      "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
      "requires": {
        "string-width": "^4.2.0",
        "strip-ansi": "^6.0.0",
        "wrap-ansi": "^7.0.0"
      }
    },
    "color-convert": {
      "version": "2.0.1",
      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
      "requires": {
        "color-name": "~1.1.4"
      }
    },
    "color-name": {
      "version": "1.1.4",
      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
    },
    "concat-map": {
      "version": "0.0.1",
      "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
      "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
    },
    "debug": {
      "version": "4.3.4",
      "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
      "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
      "requires": {
        "ms": "2.1.2"
      },
      "dependencies": {
        "ms": {
          "version": "2.1.2",
          "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
          "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
        }
      }
    },
    "decamelize": {
      "version": "4.0.0",
      "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz",
      "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ=="
    },
    "diff": {
      "version": "5.0.0",
      "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz",
      "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w=="
    },
    "emoji-regex": {
      "version": "8.0.0",
      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
      "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
    },
    "escalade": {
      "version": "3.1.1",
      "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
      "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw=="
    },
    "escape-string-regexp": {
      "version": "4.0.0",
      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
      "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="
    },
    "fill-range": {
      "version": "7.0.1",
      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
      "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
      "requires": {
        "to-regex-range": "^5.0.1"
      }
    },
    "find-up": {
      "version": "5.0.0",
      "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
      "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
      "requires": {
        "locate-path": "^6.0.0",
        "path-exists": "^4.0.0"
      }
    },
    "flat": {
      "version": "5.0.2",
      "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz",
      "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ=="
    },
    "fs.realpath": {
      "version": "1.0.0",
      "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
      "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
    },
    "fsevents": {
      "version": "2.3.2",
      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
      "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
      "optional": true
    },
    "function-bind": {
      "version": "1.1.1",
      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
      "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
    },
    "get-caller-file": {
      "version": "2.0.5",
      "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
      "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="
    },
    "glob": {
      "version": "7.2.0",
      "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
      "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==",
      "requires": {
        "fs.realpath": "^1.0.0",
        "inflight": "^1.0.4",
        "inherits": "2",
        "minimatch": "^3.0.4",
        "once": "^1.3.0",
        "path-is-absolute": "^1.0.0"
      },
      "dependencies": {
        "minimatch": {
          "version": "3.1.2",
          "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
          "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
          "requires": {
            "brace-expansion": "^1.1.7"
          }
        }
      }
    },
    "glob-parent": {
      "version": "5.1.2",
      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
      "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
      "requires": {
        "is-glob": "^4.0.1"
      }
    },
    "has": {
      "version": "1.0.3",
      "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
      "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
      "requires": {
        "function-bind": "^1.1.1"
      }
    },
    "has-ansi": {
      "version": "2.0.0",
      "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
      "integrity": "sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==",
      "requires": {
        "ansi-regex": "^2.0.0"
      },
      "dependencies": {
        "ansi-regex": {
          "version": "2.1.1",
          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
          "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA=="
        }
      }
    },
    "has-flag": {
      "version": "4.0.0",
      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
    },
    "he": {
      "version": "1.2.0",
      "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
      "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="
    },
    "inflight": {
      "version": "1.0.6",
      "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
      "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
      "requires": {
        "once": "^1.3.0",
        "wrappy": "1"
      }
    },
    "inherits": {
      "version": "2.0.4",
      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
    },
    "is-binary-path": {
      "version": "2.1.0",
      "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
      "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
      "requires": {
        "binary-extensions": "^2.0.0"
      }
    },
    "is-core-module": {
      "version": "2.11.0",
      "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz",
      "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==",
      "requires": {
        "has": "^1.0.3"
      }
    },
    "is-extglob": {
      "version": "2.1.1",
      "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
      "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="
    },
    "is-fullwidth-code-point": {
      "version": "3.0.0",
      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
      "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
    },
    "is-glob": {
      "version": "4.0.3",
      "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
      "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
      "requires": {
        "is-extglob": "^2.1.1"
      }
    },
    "is-number": {
      "version": "7.0.0",
      "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
      "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="
    },
    "is-plain-obj": {
      "version": "2.1.0",
      "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz",
      "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA=="
    },
    "is-unicode-supported": {
      "version": "0.1.0",
      "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
      "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw=="
    },
    "js-yaml": {
      "version": "4.1.0",
      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
      "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
      "requires": {
        "argparse": "^2.0.1"
      }
    },
    "locate-path": {
      "version": "6.0.0",
      "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
      "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
      "requires": {
        "p-locate": "^5.0.0"
      }
    },
    "log-symbols": {
      "version": "4.1.0",
      "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
      "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==",
      "requires": {
        "chalk": "^4.1.0",
        "is-unicode-supported": "^0.1.0"
      }
    },
    "minimatch": {
      "version": "5.0.1",
      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz",
      "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==",
      "requires": {
        "brace-expansion": "^2.0.1"
      },
      "dependencies": {
        "brace-expansion": {
          "version": "2.0.1",
          "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
          "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
          "requires": {
            "balanced-match": "^1.0.0"
          }
        }
      }
    },
    "mocha": {
      "version": "10.2.0",
      "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz",
      "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==",
      "requires": {
        "ansi-colors": "4.1.1",
        "browser-stdout": "1.3.1",
        "chokidar": "3.5.3",
        "debug": "4.3.4",
        "diff": "5.0.0",
        "escape-string-regexp": "4.0.0",
        "find-up": "5.0.0",
        "glob": "7.2.0",
        "he": "1.2.0",
        "js-yaml": "4.1.0",
        "log-symbols": "4.1.0",
        "minimatch": "5.0.1",
        "ms": "2.1.3",
        "nanoid": "3.3.3",
        "serialize-javascript": "6.0.0",
        "strip-json-comments": "3.1.1",
        "supports-color": "8.1.1",
        "workerpool": "6.2.1",
        "yargs": "16.2.0",
        "yargs-parser": "20.2.4",
        "yargs-unparser": "2.0.0"
      }
    },
    "mocha-cli": {
      "version": "1.0.1",
      "resolved": "https://registry.npmjs.org/mocha-cli/-/mocha-cli-1.0.1.tgz",
      "integrity": "sha512-cRBJVwtw1+ZE7+J0lvSaR+c0tE7rLX9Slca6f/6f6eq8L0TdcPminauCconsvPiAENbt0qkpleTd2AUCD8nFzg==",
      "requires": {
        "chalk": "^1.1.1",
        "debug": "^2.2.0",
        "resolve": "^1.1.7"
      },
      "dependencies": {
        "ansi-regex": {
          "version": "2.1.1",
          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
          "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA=="
        },
        "ansi-styles": {
          "version": "2.2.1",
          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
          "integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA=="
        },
        "chalk": {
          "version": "1.1.3",
          "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
          "integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==",
          "requires": {
            "ansi-styles": "^2.2.1",
            "escape-string-regexp": "^1.0.2",
            "has-ansi": "^2.0.0",
            "strip-ansi": "^3.0.0",
            "supports-color": "^2.0.0"
          }
        },
        "debug": {
          "version": "2.6.9",
          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
          "requires": {
            "ms": "2.0.0"
          }
        },
        "escape-string-regexp": {
          "version": "1.0.5",
          "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
          "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg=="
        },
        "ms": {
          "version": "2.0.0",
          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
          "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
        },
        "strip-ansi": {
          "version": "3.0.1",
          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
          "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==",
          "requires": {
            "ansi-regex": "^2.0.0"
          }
        },
        "supports-color": {
          "version": "2.0.0",
          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
          "integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g=="
        }
      }
    },
    "ms": {
      "version": "2.1.3",
      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
      "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
    },
    "nanoid": {
      "version": "3.3.3",
      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz",
      "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w=="
    },
    "normalize-path": {
      "version": "3.0.0",
      "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
      "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="
    },
    "once": {
      "version": "1.4.0",
      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
      "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
      "requires": {
        "wrappy": "1"
      }
    },
    "p-limit": {
      "version": "3.1.0",
      "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
      "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
      "requires": {
        "yocto-queue": "^0.1.0"
      }
    },
    "p-locate": {
      "version": "5.0.0",
      "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
      "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
      "requires": {
        "p-limit": "^3.0.2"
      }
    },
    "path-exists": {
      "version": "4.0.0",
      "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
      "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="
    },
    "path-is-absolute": {
      "version": "1.0.1",
      "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
      "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg=="
    },
    "path-parse": {
      "version": "1.0.7",
      "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
      "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="
    },
    "picomatch": {
      "version": "2.3.1",
      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
      "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="
    },
    "randombytes": {
      "version": "2.1.0",
      "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
      "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
      "requires": {
        "safe-buffer": "^5.1.0"
      }
    },
    "readdirp": {
      "version": "3.6.0",
      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
      "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
      "requires": {
        "picomatch": "^2.2.1"
      }
    },
    "require-directory": {
      "version": "2.1.1",
      "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
      "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="
    },
    "resolve": {
      "version": "1.22.1",
      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
      "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==",
      "requires": {
        "is-core-module": "^2.9.0",
        "path-parse": "^1.0.7",
        "supports-preserve-symlinks-flag": "^1.0.0"
      }
    },
    "safe-buffer": {
      "version": "5.2.1",
      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
      "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
    },
    "serialize-javascript": {
      "version": "6.0.0",
      "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz",
      "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==",
      "requires": {
        "randombytes": "^2.1.0"
      }
    },
    "string-width": {
      "version": "4.2.3",
      "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
      "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
      "requires": {
        "emoji-regex": "^8.0.0",
        "is-fullwidth-code-point": "^3.0.0",
        "strip-ansi": "^6.0.1"
      }
    },
    "strip-ansi": {
      "version": "6.0.1",
      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
      "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
      "requires": {
        "ansi-regex": "^5.0.1"
      }
    },
    "strip-json-comments": {
      "version": "3.1.1",
      "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
      "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="
    },
    "supports-color": {
      "version": "8.1.1",
      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
      "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
      "requires": {
        "has-flag": "^4.0.0"
      }
    },
    "supports-preserve-symlinks-flag": {
      "version": "1.0.0",
      "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
      "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="
    },
    "to-regex-range": {
      "version": "5.0.1",
      "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
      "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
      "requires": {
        "is-number": "^7.0.0"
      }
    },
    "workerpool": {
      "version": "6.2.1",
      "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz",
      "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw=="
    },
    "wrap-ansi": {
      "version": "7.0.0",
      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
      "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
      "requires": {
        "ansi-styles": "^4.0.0",
        "string-width": "^4.1.0",
        "strip-ansi": "^6.0.0"
      }
    },
    "wrappy": {
      "version": "1.0.2",
      "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
      "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
    },
    "y18n": {
      "version": "5.0.8",
      "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
      "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="
    },
    "yargs": {
      "version": "16.2.0",
      "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
      "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
      "requires": {
        "cliui": "^7.0.2",
        "escalade": "^3.1.1",
        "get-caller-file": "^2.0.5",
        "require-directory": "^2.1.1",
        "string-width": "^4.2.0",
        "y18n": "^5.0.5",
        "yargs-parser": "^20.2.2"
      }
    },
    "yargs-parser": {
      "version": "20.2.4",
      "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz",
      "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA=="
    },
    "yargs-unparser": {
      "version": "2.0.0",
      "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz",
      "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==",
      "requires": {
        "camelcase": "^6.0.0",
        "decamelize": "^4.0.0",
        "flat": "^5.0.2",
        "is-plain-obj": "^2.1.0"
      }
    },
    "yocto-queue": {
      "version": "0.1.0",
      "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
      "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="
    }
  }
}

Testing Demo with Mocha success

var assert = require('assert');

const calc = require("../mycalc.js")

describe('Array', function() {
  describe('calc', function() {
    it('should be ok', function() {
      assert.equal(calc.add(2, 2), 4);
    });
  });
});

./node_modules/mocha/bin/mocha.js test/first.js

Testing Demo with Mocha failure

var assert = require('assert');

const calc = require("../mycalc.js")

describe('Array', function() {
  describe('calc', function() {
    it('should be ok', function() {
      assert.equal(calc.add(2, 2), 4);
    });
    it('should be ok', function() {
      assert.equal(calc.add(2, 3), 5);
    });
  });
});

./node_modules/mocha/bin/mocha.js test/several.js

Appendix

Other

Because node.js does not live in the browser it does not have the DOM. But it has a main object called process and you can ask for process.pid.

Node exits when it has nothing more to do. It reference counts the callbacks and when that goes to 0 it exits.

We can use global.console.log() or console.log()

A TAP test framework for Node.js

Resources

request

npm install request
node http_request.js

const request = require('request');

//let url = 'http://httpbin.org/get'
let url = 'https://httpbin.org/get'

let options = {
    url: url,
    headers: {
        'User-Agent': 'My Awesone Node browser'
    }
};

request(options, function (error, response, body) {
    if (error) {
        console.log('Something went wrong ', error)
    } else {
        console.log("Status code: " + response.statusCode);
        console.log(response.body);
    }
});

http client

const http = require('http');

url = 'http://httpbin.org/get';

http.get(url, (res) => {
  const { statusCode } = res;

  console.error("StatusCode " + statusCode);
  if (statusCode !== 200) {
    console.log("Error");
    // Consume response data to free up memory
    res.resume();
    return;
  }

  let rawData = '';
  res.on('data', (chunk) => { rawData += chunk; });
  res.on('end', () => {
    console.log(rawData);
    });
});
node http_agent.js

ping

const ping = require("net-ping");

let host = "8.8.8.8";

const session = ping.createSession();
session.pingHost(host, function(error, target) {
    if (error)
        console.log(target + ": " + error.toString());
    else
        console.log(target + ": Alive");
});
npm install net-ping
sudo /opt/node/bin/node ping_demo.js

Telnet server

const telnet = require('telnet');

telnet.createServer(function(client) {
//    client.on("error", function(err) {
//        //console.log(err.stack);
//        console.log(
//    });
    client.write('My banner');
    client.do.window_size();
}).listen(8080);

HTTP Server

const http = require('http')

http.createServer(function(req, res) {
    res.writeHead(200, 'Header');
    res.write('Reply\n');
    let html = '<html><head><title>Title</title></head><body></body></html>';
    res.write(html);
    res.set
    res.end();
}).listen(8080);