четверг, 26 января 2017 г.

JavaScript XML конвертация в объект, DOM и обратно в XML

Файлы:
validate-xml.js
format-xml.js
escape-xml.js
convert-xml-to-dom.js
convert-object-to-dom-xml.js
convert-dom-to-xml.js
convert-dom-xml-to-object.js

Файл validate-xml.js

function validateXML (xml, options) {
    var xmlFragment = false; // false - проверяемый код это целый xml, true - проверяемый код не целый xml, а только его часть
    if (options && options.hasOwnProperty('xmlFragment')) {
        xmlFragment = options.xmlFragment;
    }
    var regTab = /[\n\t\r]+/g
        , regCommentAndCdata = /<!(?:--(?:[^-]|-[^-])*--|\[CDATA\[(?:[^\]]|\][^\]]|\]+[^\>\]])*]{2,})>/g
        , regInstruction = /<\?.*?\?>/
        , regDocType = /<\!DocType.*?>/i
        , regOutTagTextBegin = /^\s*[^<\s]+/
        , regEntityFull = /&(?:#(?:x[a-f\d]{1,4}|\d{2,5})|[a-z][\w\-]*);/gi
        , regAttribute = /(<[a-z_][\w:-]*)((?:\s+[a-z_][\w:-]*\s*=\s*(?:'[^<>']*'|"[^<>"]*"))*)\s*(\/?>)/gi
        , regAttributeUnique = /([a-z_][\w:-]*)\s*=\s*(?:'[^<>']*'|"[^<>"]*")/gi
        , regAttributeMatch = /[a-z_][\w:-]*/gi
        , regSingleTag = /<[a-z_][\w:-]*\/>/gi
        , regDoubleTag = /<([a-zA-Z_][\w:-]*)>[^<]*<\/\1\s*>/g;
    if (xml) {
        // Вырезаем табуляцию и переносы строк
        xml = xml.replace(regTab, ' ');
        // Вырезаем комментарии и CDATA
        xml = xml.replace(regCommentAndCdata, '');
        if (xml.indexOf('<!--') !== -1) {return false;}
        if (xml.indexOf(']]>') !== -1) {return false;}
        // Вырезаем инструкции
        if (!xmlFragment) {
            xml = xml.replace(regInstruction, '');
            if (xml.search(regInstruction) !== -1) {return false;}
        }
        // Вырезаем DocType
        if (!xmlFragment) {xml = xml.replace(regDocType, '');}
        if (xml.search(regDocType) !== -1) {return false;}
        // Ищем текст в начале и в конце строки, выходящий за пределы тегов
        if (!xmlFragment) {
            if (xml.search(regOutTagTextBegin) !== -1) {return false;}
            // Конец строки
            var valueLength = xml.length
                , isSpace = true;
            do {
                valueLength--;
                if (xml.charAt(valueLength) !== ' ') {isSpace = false;}
            } while (isSpace && valueLength > 0);
            if (!isSpace && xml.charAt(valueLength) !== '>') {
                return false;
            } else if (valueLength === 0) {
                return false;
            }
        }
        // Вырезаем Entities
        xml = xml.replace(regEntityFull, '');
        if (xml.indexOf('&') !== -1) {return false;}
        // Вырезаем аттрибуты и проверяем на дублирование
        var attributeUnique = true;
        xml = xml.replace(regAttribute, function ($0, $1, $2 ,$3) {
            $2 = $2.replace(regAttributeUnique, '$1');
            var attribute = $2.match(regAttributeMatch);
            if (attribute) {
                var matchCount = attribute.length;
                if (matchCount > 1) {
                    var i = 0
                        , j;
                    while (attributeUnique && i < matchCount - 1) {
                        j = i + 1;
                        while (attributeUnique && j < matchCount) {
                            if (attribute[i] !== attribute[j]) {
                                j++;
                            } else {
                                attributeUnique = false;
                            }
                        }
                        i++;
                    }
                }
            }
            return $1 + $3;
        });
        if (!attributeUnique) {return false;}
        // Параметр для вырезания тэгов
        var tagReplaceTo = '';
        if (!xmlFragment) {tagReplaceTo = '&';}
        // Вырезаем одинарные тэги
        xml = xml.replace(regSingleTag, tagReplaceTo);
        // Вырезаем двойные тэги
        var previousLen
            , len = 0;
        do {
            previousLen = len;
            xml = xml.replace(regDoubleTag, tagReplaceTo);
            len = xml.length;
        } while (len !== previousLen);
        if (!xmlFragment) {
            if (xml.indexOf(tagReplaceTo) !== xml.lastIndexOf(tagReplaceTo)) {return false;}
        }
        if (xml.indexOf('<') !== -1) {return false;}
        return true;
    } else { // Пустая строка вместо XML
        if (!xmlFragment) {
            return false;
        } else {
            return true;
        }
    }
}

// Test

var xmlString = '<a id="a"><b id="b">hey!</b></a>';
console.log(validateXML(xmlString, {xmlFragment: true})); // true | false

Файл format-xml.js

// Функция делает простое форматирование XML и
// удаляет перенос на другую строку внутри текста, расположенного между тэгами
function formatXML (xml) {
    xml = String(xml); // привести к строке на случай, если xml - это объект
    xml = xml.replace(/\r|\n/g, ''); // удалить уже существующие символы перехода на следующую строку \r и \n в том числе внутри текста, расположенного между тэгами
    xml = xml.replace(/(>)\s*(<)(\/*)/g, '$1\r\n$2$3'); // удалить пробелы между тэгами (<tag>      </tag>), заменив их на символы \r\n, итоговый результат: >\r\n</
    var formattedXML = ''
        , xmlParts = xml.split('\r\n') // разбить содержимое XML на части
        , xmlPartsLength = xmlParts.length
        , i
        , j
        , pad = 0
        , indent
        , padding;
    for (i = 0; i < xmlPartsLength; i++) {
        indent = 0;
        if (xmlParts[i].match(/.+<\/\w[^>]*>$/)) {
            // Пример: some text</tag>
            // любой символ встречается 1 или более раз,
            // за ним идут символы </ и далее одна буква,
            // после этого любой набор символов, кроме символа >
            // и в конце идет закрывающая скобка >
            indent = 0;
        } else if (xmlParts[i].match(/^<\/\w/)) {
            // Пример: </a
            // начинается с </ и одной буквы
            if (pad !== 0) {
                pad -= 1;
            }
        } else if (xmlParts[i].match( /^<\w[^>]*[^\/]>.*$/ )) {
            // Пример: <tag>some text
            // начинается с < и одной буквы,
            // далее идет любой набор символов, кроме символа >,
            // после чего идет любой набор символов, кроме символа /,
            // затем идет символ >
            // после этого идут любые сиволы до конца строки
            indent = 1;
        } else {
            indent = 0;
        }
        padding = '';
        for (j = 0; j < pad; j++) {
            padding += '    '; // 4 пробела можно заменить на Tab: padding += '\t';
        }
        formattedXML += padding + xmlParts[i] + '\r\n';
        pad += indent;
    }
    return formattedXML;
}

// Test

var xmlString = '<a id="a"><b id="b">hey!</b></a>';
console.log(formatXML(xmlString));
// Результ:
// <a id="a">
//   <b id="b">hey!</b>
// </a>

// Функция  делает более сложную проверку и форматирование XML и
// не удаляет перенос на другую строку внутри текста, расположенного между тэгами, оставляя его на разных строках
function alternativeFormatXML (xml) {
    xml = String(xml); // привести к строке на случай, если xml - это объект
    xml = xml.replace(/(>)\s*(<)(\/*)/g, '$1\n$2$3'); // удалить пробелы между тэгами (<tag>      </tag>), заменив их на символ \n, итоговый результат: >\n</
    xml = xml.replace(/ *(.*) +\n/g, '$1\n'); // вставить символ \n после последовательности |      some text    \n|, итоговый результат: |      some text    \n\n|
    xml = xml.replace(/(<.+>)(.+\n)/g, '$1\n$2'); // вставить символ \n между тэгом и текстом, итоговый результат: <tag>\nsome text\n
    var formattedXML = ''
        , transitions = {// 4 типа тэгов: single, closing, opening, other (text, doctype, comment) -  всего 4*4 = 16 вариантов transitions
              'single->single': 0
            , 'single->closing': -1
            , 'single->opening': 0
            , 'single->other': 0
            , 'closing->single': 0
            , 'closing->closing': -1
            , 'closing->opening': 0
            , 'closing->other': 0
            , 'opening->single': 1
            , 'opening->closing': 0
            , 'opening->opening': 1
            , 'opening->other': 1
            , 'other->single': 0
            , 'other->closing': -1
            , 'other->opening': 0
            , 'other->other': 0
          }
        , i
        , j
        , lines = xml.split('\n')
        , linesLength = lines.length
        , line
        , type
        , fromTo
        , lastType = 'other'
        , indent = 0
        , padding;
    for (i = 0; i < linesLength; i++) {
        line = lines[i];
                  if (/<.+\/>/.test(line)) {type = 'single'; // эта линия содержит одиночный (single) тэг, например: <br />
        } else if (/<\/.+>/.test(line)) {type = 'closing'; // эта линия содержит закрывающий (closing) тэг, например: </a>
        } else if (/<[^!].*>/.test(line)) {type = 'opening'; // эта линия содержит открывающий (opening) тэг, но это не что-то вроде <!something>, например: <a>
        } else {type = 'other'; // эта линия содержит text или тэг doctype, comment, например: <!--
        }
        fromTo = lastType + '->' + type;
        lastType = type;
        indent += transitions[fromTo];
        padding = '';
        for (j = 0; j < indent; j++) {
            padding += '    '; // 4 пробела можно заменить на Tab: padding += '\t';
        }
        if (fromTo === 'opening->closing') {
            formattedXML = formattedXML.substr(0, formattedXML.length - 1) + line + '\n'; // substr() удаляет разрыв строки (\n) оставшийся от предыдущего цикла
        } else {
            formattedXML += padding + line + '\n';
        }
    }
    return formattedXML;
}

// Test

var xmlString = '<a id="a"><b id="b">h\ne\ny\n!</b></a>';
console.log(alternativeFormatXML(xmlString));
// Результ:
// <a id="a">
//     <b id="b">
//         h
//         e
//         y
 //    !</b>
// </a>

Файл escape-xml.js

function escapeXML (xml) {
    // Замена символов: &, <, >, \n и пробелы на escape-последовательности
    return xml.replace(/&/g,'&amp;')
                    .replace(/</g,'&lt;')
                    .replace(/>/g,'&gt;')
                    .replace(/ /g, '&nbsp;')
                    .replace(/\n/g,'<br />');
}

// Test

var xmlString = '<a id="a"><b id="b">hey!</b></a>';
console.log(escapeXML(xmlString));

Файл convert-xml-to-dom.js

function convertXMLtoDOM (data) {
    if (typeof data !== 'string') {throw new Error('XML must be a string.');}
    if (data === '') {throw new Error('XML can\'t be an empty string.');}
    var xmlDOM;
    try {
        if (window.DOMParser) { // Standard
            xmlDOM = new DOMParser().parseFromString(data, 'text/xml');
        } else { // IE
            xmlDOM = new ActiveXObject('Microsoft.XMLDOM');
            xmlDOM.async = 'false';
            xmlDOM.loadXML(data);
        }
    } catch (e) {
        xmlDOM = undefined;
    }
    if (
            !xmlDOM
        || !xmlDOM.documentElement
        || xmlDOM.getElementsByTagName('parsererror').length
    ) {
        throw new Error('Invalid XML: ' + data);
    }
    return xmlDOM;
}

// Test

var xmlString = '<a id="a"><b id="b">hey!</b></a>'
    , xmlDOM = convertXMLtoDOM(xmlString);
console.log(xmlDOM.documentElement.nodeName); // Root element

Файл convert-object-to-dom-xml.js

// Create XML from Object

function createXML (oObjTree) {
  function loadObjTree (oParentEl, oParentObj) {
    var vValue, oChild;
    if (oParentObj.constructor === String || oParentObj.constructor === Number || oParentObj.constructor === Boolean) {
      oParentEl.appendChild(oNewDoc.createTextNode(oParentObj.toString())); /* verbosity level is 0 or 1 */
      if (oParentObj === oParentObj.valueOf()) { return; }
    } else if (oParentObj.constructor === Date) {
      oParentEl.appendChild(oNewDoc.createTextNode(oParentObj.toGMTString()));
    }
    for (var sName in oParentObj) {
      if (isFinite(sName)) { continue; } /* verbosity level is 0 */
      vValue = oParentObj[sName];
      if (sName === "keyValue") {
        if (vValue !== null && vValue !== true) { oParentEl.appendChild(oNewDoc.createTextNode(vValue.constructor === Date ? vValue.toGMTString() : String(vValue))); }
      } else if (sName === "keyAttributes") { /* verbosity level is 3 */
        for (var sAttrib in vValue) { oParentEl.setAttribute(sAttrib, vValue[sAttrib]); }
      } else if (sName.charAt(0) === "@") {
        oParentEl.setAttribute(sName.slice(1), vValue);
      } else if (vValue.constructor === Array) {
        for (var nItem = 0; nItem < vValue.length; nItem++) {
          oChild = oNewDoc.createElement(sName);
          loadObjTree(oChild, vValue[nItem]);
          oParentEl.appendChild(oChild);
        }
      } else {
        oChild = oNewDoc.createElement(sName);
        if (vValue instanceof Object) {
          loadObjTree(oChild, vValue);
        } else if (vValue !== null && vValue !== true) {
          oChild.appendChild(oNewDoc.createTextNode(vValue.toString()));
        }
        oParentEl.appendChild(oChild);
      }
    }
  }
  const oNewDoc = document.implementation.createDocument("", "", null);
  loadObjTree(oNewDoc, oObjTree);
  return oNewDoc;
}

var newDoc = createXML(myObject);
console.log((new XMLSerializer()).serializeToString(newDoc));

Файл convert-dom-to-xml.js

function convertDOMtoXML (domElement) {
    var serializer;
    if (window.XMLSerializer) {
        serializer = new XMLSerializer();
    } else {
        throw new Error('No XML serializer found.');
    }
    return serializer.serializeToString(domElement);
}

// Test

console.log(convertDOMtoXML(document));

var inputElement = document.createElement('input');
console.log(convertDOMtoXML(inputElement));

Файл convert-dom-xml-to-object.js

function convertDomXMLtoObject (domXMLelement) {
    var object = {};
    if (domXMLelement.nodeType === 1) { // element
        // Attributes
        if (domXMLelement.attributes.length > 0) {
            object['@attributes'] = {};
            var attribute;
            for (var i = 0, len = domXMLelement.attributes.length; i < len; i++) {
                attribute = domXMLelement.attributes.item(i);
                object['@attributes'][attribute.nodeName] = attribute.nodeValue; // пример: @attributes = {class: 'menu'}
            }
        }
    } else if (domXMLelement.nodeType === 3) { // text
        // Text
        object = domXMLelement.nodeValue;
    }
    // Children
    if (domXMLelement.hasChildNodes()) {
        var item
            , nodeName;
        for(var j = 0, len = domXMLelement.childNodes.length; j < len; j++) {
            item = domXMLelement.childNodes.item(j);
            nodeName = item.nodeName;
            if (typeof object[nodeName] === 'undefined') {
                object[nodeName] = convertDomXMLtoObject(item);
            } else {
                if (typeof object[nodeName].push === 'undefined') {
                    object[nodeName] = [];
                    var old = object[nodeName];
                    object[nodeName].push(old);
                }
                object[nodeName].push(convertDomXMLtoObject(item));
            }
        }
    }
    return object;
}

// Test

console.log(convertDomXMLtoObject(document));

четверг, 19 января 2017 г.

Node.js Краткая шпаргалка

------------------------------------------------------------
Command Line Options
------------------------------------------------------------

node --version
node --help
node server.js

------------------------------------------------------------
Debugger
------------------------------------------------------------

node debug server.js
node --inspect server.js

// File: server.js
const x = 5;
setTimeout(() => {
    debugger;
    console.log('world');
}, 1000);
console.log('hello');

------------------------------------------------------------
Errors
------------------------------------------------------------

const myObject = {};
Error.captureStackTrace(myObject);
myObject.stack // similar to new Error().stack

------------------------------------------------------------
Domain
------------------------------------------------------------

const fs = require('fs');

const domain = require('domain');

const d = domain.create();

d.on('error', (error) => {console.error(`Caught error! ${error}`);}); // d.on('error') handler will be triggered, rather than crashing the program

d.run(() => {
    process.nextTick(() => {
        setTimeout(() => { // simulating some various async stuff
            fs.open('non-existent file', 'r', (error, fileDescriptior) => {
                if (error) {throw error;}
                // proceed...
              });
        }, 100);
    });
});

------------------------------------------------------------
REPL
------------------------------------------------------------

const repl = require('repl');

--------------------

repl.start('> ').context.m = 'message';

--------------------

repl.start({prompt: '> ', eval: function myEval(cmd, context, filename, callback) {
    try {
        callback(null, new require('translator').Translator('en', 'fr').translate(cmd));
    } catch (error) {
        if (isRecoverableError(error)) {
            return callback(new repl.Recoverable(error));
        }
    }
    function isRecoverableError (error) {
        if (error.name === 'SyntaxError') {return /^(Unexpected end of input|Unexpected token)/.test(error.message);}
        return false;
    }
}});

--------------------

const r = repl.start({prompt: '> ', eval: myEval, writer: myWriter});
function myEval(cmd, context, filename, callback) {callback(null, cmd);}
function myWriter(output) {return output.toUpperCase();}

--------------------

replServer.on('exit', () => {
    console.log('Received "exit" event from repl!');
    process.exit();
});

--------------------

const r = repl.start({prompt: '> '});
function initializeContext(context) {context.m = 'test';}
initializeContext(r.context);
r.on('reset', initializeContext);

--------------------

const repl = require('repl');

const replServer = repl.start({prompt: '> '});

replServer.defineCommand('sayhello', {
    help: 'Say hello',
    action: function (name) {
        this.lineParser.reset();
        this.bufferedCommand = '';
        console.log(`Hello, ${name}!`);
        this.displayPrompt();
    }
});

replServer.defineCommand('saybye', () => {
    console.log('Goodbye!');
    this.close();
});

// > .sayhello Node.js User
// Hello, Node.js User!
// > .saybye
// Goodbye!

--------------------

const net = require('net');

const repl = require('repl');

repl.start({prompt: 'Node.js via stdin> ', input: process.stdin, output: process.stdout});

let connections = 0;

net.createServer((socket) => {
    connections += 1;
    repl.start({prompt: 'Node.js via Unix socket> ', input: socket, output: socket}).on('exit', () => {socket.end();});
}).listen('/tmp/node-repl-sock');

net.createServer((socket) => {
    connections += 1;
    repl.start({prompt: 'Node.js via TCP socket> ', input: socket, output: socket}).on('exit', () => {socket.end();});
}).listen(5001);

--------------------

replServer.displayPrompt(preserveCursor);

repl.start({
      prompt: 'Node.js via stdin> '
    , input: process.stdin
    , output: process.stdout
    , terminal
    , eval
    , useColors: true
    , useGlobal: true
    , ignoreUndefined: true
    , writer
    , completer
    , replMode: repl.REPL_MODE_STRICT
});

------------------------------------------------------------
Readline
------------------------------------------------------------

const readline = require('readline');

const rl = readline.createInterface({input: process.stdin, output: process.stdout});

rl.question('What do you think of Node.js? ', (answer) => {
    // TODO: Log the answer in a database
    console.log(`Thank you for your valuable feedback: ${answer}`);
    rl.close();
});

--------------------

const readline = require('readline');

rl.write('Delete this!');
rl.write(null, {ctrl: true, name: 'u'}); // Simulate Ctrl+u to delete the line written previously

rl.setPrompt(prompt);
rl.prompt(preserverCursor);
rl.question('What is your favorite food?', (answer) => {console.log(`Oh, so your favorite food is ${answer}`);});

rl.pause();
rl.resume();

rl.close();

rl.on('line', (input) => {console.log(`Received: ${input}`);});
rl.on('close', () => {});
rl.on('pause', () => {console.log('Readline paused.');});
rl.on('resume', () => {console.log('Readline resumed.');});

rl.on('SIGINT', () => {rl.question('Are you sure you want to exit?', (answer) => {
    if (answer.match(/^y(es)?$/i)) {rl.pause();}
});});
rl.on('SIGCONT', () => {rl.prompt();}); // "prompt" will automatically resume the stream
rl.on('SIGTSTP', () => {console.log('Caught SIGTSTP.');}); // This will override SIGTSTP and prevent the program from going to the background

readline.createInterface({input: process.stdin, output: process.stdout});

readline.clearLine(stream, dir);
readline.clearScreenDown(stream);

readline.cursorTo(stream, x, y);
readline.cursorTo(stream, dx, dy);

readline.emitKeypressEvents(/*stream*/ process.stdin, interface);
if (process.stdin.isTTY) {process.stdin.setRawMode(true);}

--------------------

Tiny CLI

const readline = require('readline');

const rl = readline.createInterface({input: process.stdin, output: process.stdout, prompt: 'OHAI> '});

rl.prompt();

rl.on('line', (line) => {
    switch(line.trim()) {
      case 'hello':
          console.log('world!');
          break;
      default:
          console.log(`Say what? I might have heard '${line.trim()}'`);
          break;
    }
    rl.prompt();
}).on('close', () => {
    console.log('Have a great day!');
    process.exit(0);
});

--------------------

Read File Stream Line-by-Line

const readline = require('readline');
const fs = require('fs');

const rl = readline.createInterface({input: fs.createReadStream('sample.txt')});

rl.on('line', (line) => {console.log(`Line from file: ${line}`);});

------------------------------------------------------------
Assert
------------------------------------------------------------

const assert = require('assert');

assert(true); // OK
assert.ok(true); // OK
assert.deepEqual({a: 1}, {a: 2}, 'error'); // ERROR

------------------------------------------------------------
Console
------------------------------------------------------------

const Console = require('console').Console;

const logger = new Console(fs.createWriteStream('./stdout.log'), fs.createWriteStream('./stderr.log'));
logger.log('number: %d', 5);

console.log('message');
console.info('message');
console.error('message');
console.warn('message');
console.assert(false, 'It is error');
console.dir({a: 1}, {showHidden: true});
console.trace('show trace log');
console.time('100-elements');
console.timeEnd('100-elements');

new console.Console();
const customeConsole = new console.Console(output, errorOutput);
const customeConsole = new Console(process.stdout, process.stderr);

------------------------------------------------------------
Utilities
------------------------------------------------------------

const util = require('util');

const debuglog = util.debuglog('foo');
debuglog('hello from foo [%d]', 123);

exports.puts = util.deprecate(function() {
  for (var i = 0, len = arguments.length; i < len; ++i) {process.stdout.write(arguments[i] + '\n');}
}, 'util.puts: Use console.log instead');

util.format('%s:%d:%j:100%%', 'foo', 1, '[circular]');

util.inherits(MyStream, EventEmitter);

console.log(util.inspect(util, {showHidden: true, depth: null}));

util.inspect.defaultOptions.maxArrayLength = null;

util.inspect.custom.myOption = true;

Deprecated APIs

util._extend(target, source); // alias  Object.assign();

util.log('Timestamped message.');
util.print(...strings); // alias console.log();
util.puts(...strings); // alias console.log();
util.debug('string'); // alias console.error();
util.error(...strings); // alias console.error();

if (util.isArray(object)) {}
if (util.isBoolean(object)) {}
if (util.isBuffer(object)) {}
if (util.isDate(object)) {}
if (util.isError(object)) {}
if (util.isFunction(object)) {}
if (util.isNull(object)) {}
if (util.isNullOrUndefined(object)) {}
if (util.isNumber(object)) {}
if (util.isObject(object)) {}
if (util.isPrimitive(object)) {}
if (util.isRegExp(object)) {}
if (util.isString(object)) {}
if (util.isSymbol(object)) {}
if (util.isUndefined(object)) {}

------------------------------------------------------------
OS
------------------------------------------------------------

const os = require('os');

os.EOL;

os.constants;
os.constants.signals.SIFNGUP;
os.constants.errno.EACCES;

console.log(os.type());
console.log(os.platform());
console.log(os.release());

console.log(os.arch());
console.log(os.constants);

console.log(os.homedir());
console.log(os.tmpdir());

console.log(os.hostname());

console.log(os.userInfo({encoding: 'utf8'}));

console.log(os.cpus().length);
console.log(os.cpus()[0].model);

cosnole.log(os.loadavg());

console.log(os.totalmem());
console.log(os.freemem());

console.log(os.endianness());

console.log(os.networkInterfaces());

console.log(os.uptime());

------------------------------------------------------------
Globals
------------------------------------------------------------

__dirname
__filename

global

require()
require.resolve()
require.extensions
require.cache

module.id
module.filename
module.children
module.exports
exports

Buffer

process

------------------------------------------------------------
Process
------------------------------------------------------------

process.nextTick(() => {console.log('event before all events');}, args);

process.argv.forEach((argument) => {console.log(argument);}); // (0) node (1) server.js (2) one (3) two=three (4) four
console.log(process.argv0); // process.argv0 = process.argv[0]

console.log(process.execArgv); // 'node --harmony script.js --version' ==> ['--harmony']
console.log(process.execPath); // '/usr/local/bin/node'

consoe.log(process.mainModule); // an alternative way of retrieving require.main

console.log(process.cwd());
process.chdir(dir);
console.log(`Starting directory: ${process.cwd()}`);
try {
    process.chdir('/tmp');
    console.log(`New directory: ${process.cwd()}`);
} catch (error) {
    console.log(`chdir: ${error}`);
}

console.log(process.env);
process.env.MY_DEBUG = true;
if (process.env.MY_DEBUG) {console.log('debug mode on');}
delete process.env.MY_DEBUG;

console.log(process.arch); // This processor architecture: 'arm', 'ia32', 'x64'
console.log(process.cpuUsage(previousValue)); // {user: 38579, system: 6986}
console.log(process.memoryUsage()); // {rss: 4935680, heapTotal: 1826816, heapUsed: 650472, external: 49879}
console.log(process.uptime());

console.log(process.hrtime(time));
var time = process.hrtime(); // [1800216, 25]
setTimeout(() => {
    var diff = process.hrtime(time); // [1, 552]
    console.log(`Benchmark took ${diff[0] * 1e9 + diff[1]} nanoseconds`); // benchmark took 1000000527 nanoseconds
}, 1000);

process.stderr...

process.stdin...
process.stdin.setEncoding('utf8');
process.stdin.on('readable', () => {
    var chunk = process.stdin.read();
    if (chunk !== null) {process.stdout.write(`data: ${chunk}`);}
});
process.stdin.on('end', () => {process.stdout.write('end');});

process.stdout...
console.log = function (msg)  {
    process.stdout.write(`${msg}\n`);
};

console.log(process.title);
console.log(process.version);
console.log(process.versions); // {http_parser: '2.3.0', node: '1.1.1', v8: '4.1.0.14', ...}

if (process.channel) {}

console.log(process.config);

if (process.connected) {process.send(message);}

process.disconnect();

process.abort();

process.exit(0);
if (process.exitCode === 0) {console.log('it is ok');}

process.kill(process.pid, 'SIGHUP');

process.send(message, sendHandle, options, callback);

process.emitWarning('Something Happened!', 'CustomWarning', constructor); // Emits: (node:56338) CustomWarning: Something Happened!

console.log(process.pid);
console.log(process.platform); // 'darwin', 'freebsd', 'linux', 'sunos', 'win32'
console.log(process.release); // {
    name: 'node',
    lts: 'Argon',
    sourceUrl: 'https://nodejs.org/download/release/v4.4.5/node-v4.4.5.tar.gz',
    headersUrl: 'https://nodejs.org/download/release/v4.4.5/node-v4.4.5-headers.tar.gz',
    libUrl: 'https://nodejs.org/download/release/v4.4.5/win-x64/node.lib'
}

console.log(process.getegid());
console.log(process.geteuid());
console.log(process.getgid());
console.log(process.getgroups());
console.log(process.getuid());

process.setegid(id);
process.seteuid(id);
process.setgid(id);
process.setgroups(groups);
process.setuid(id);

process.initgroups('bnoordhuis', 1000);   // switch user

process.umask(0o022);

process.on('beforeExit', () => {});
process.on('exit', (code) => {console.log(`exit with code: ${code}`);});
process.on('disconnect', () => {});

process.on('SIGINT', () => {console.log('Received SIGINT.  Press Control-D to exit.');});

process.on('uncaughtException', (error) => {fs.writeSync(1, `Caught exception: ${error}`);});

somePromise.then((response) => {
    return reportToUser(JSON.pasre(response)); // note the typo (`pasre`)
}); // no `.catch` or `.then`
const unhandledRejections = new Map();
process.on('unhandledRejection', (reason, promise) => {
    console.log(`Unhandled Rejection at: Promise ${promise}, reason: ${reason}`);
    unhandledRejections.set(promise, reason);
});
process.on('rejectionHandled', (promise) => {
    unhandledRejections.delete(promise);
});

process.on('message', (message, sendHandle) => {console.log(message);});

process.on('warning', (warning) => {
    console.warn(warning.name);
    console.warn(warning.message);
    console.warn(warning.stack);
});

------------------------------------------------------------
Child Process
------------------------------------------------------------

const child_process = require('child_process');

--------------------
child_process.exec(/*terminalCommand*/ 'ls -lh /user', /*options*/ {shell: true}, function callback (error, stdout, stderr) {console.log(stdout);}); // In command window
--------------------

const processStream = child_process.spawn/*terminalCommand*/ 'ls', /*args*/ ['-lh', '/user'], /*options*/ {shell: true}); // Stream in command window
processStream.stdout.on('data', (data) => {console.log(data);});
processStream.stderr.on('data', (data) => {console.log(data);});
processStream.on('close', (code) => {console.log(`exit code: ${code}`);});
processStream.on('exit', (code) => {console.log(`exit code: ${code}`);});
processStream.on('error', (error) => {console.log('Error!');});

childProcess.on('disconnect', () => {});
childProcess.on('close', (code, signal) => {console.log(code);});
childProcess.on('exit', (code, signal) => {console.log(code);});
childProcess.on('error', (error) => {console.log(error);});
childProcess.on('message', (message, sendHandle) => {console.log(message);}); // sendHandle = net.Socket or netServer or undefined

--------------------
child_process.execFile(/*filePath*/ 'node.exe', /*args*/ ['--version'], options, function callback (error, stdout, stderr) {console.log(stdout);}); // exec standalone file
--------------------

const child = child_process.fork(modulePath, args, options); // standalone Node process

child.send(/*message*/ {msg: 'hello'}, sendHandle, options, callback);
child.disconnect();
child.kill(/*signal*/ 'SIGHUP');

child.pid; // process number
child.channel; // object
if (child.connected) {}

child.stdin
child.stdout
child.stderr
child.strio

------------------------------------------------------------
Cluster
------------------------------------------------------------

const http = require('http');
const numCPUs = require('os').cpus().length;

const cluster = require('cluster');

if (cluster.isMaster) { // Fork workers
    numCPUs.forEach(() => {
        const worker = cluster.fork();
        const timeout;
        worker.on('listening', (address) => {
            worker.send('shutdown');
            worker.disconnect();
            timeout = setTimeout(() => {worker.kill()}, 2000);
        });
        worker.on('disconnect', (worker) => {
            console.log(`worker ${worker.id} disconncted`);
            clearTimeout(timeout);
        });
        worker.send('hi there');
    });
    const timeouts = [];
    function errorMessage () {console.error('Something wrong with connection...');}
    cluster.on('fork', (worker) => {
        timeouts[worker.id] = setTimeot(errorMessage, 2000);
    });
    cluster.on('listening', (worker, address) => {
        clearTimeout(timeouts[worker.id]);
        console.log(`worker is now connected to ${address.address}:${address.port}`);
    });
    cluster.on('exit', (worker, code, signal) => {
        clearTimeout(timeouts[worker.id]);
        errorMessage();
        console.log(`worker ${worker.process.pid} died`);
        if (worker.exitedAfterDisconnect) {console.log('OK');}
        cluster.fork();
    });
    cluster.on('message', (worker, message, handle) => {console.log(message);});
    cluster.on('online', (worker) => {console.log(`worker ${worker.id} responded after it was forked`);});
    for (const id in cluster.workers) {
        cluster.workers[id].on('message', function messageHandler (msg) {
            if (msg.cmd === 'notifyRequest') {console.log('request complete');}
            if (msg.cmd === 'kill') {cluster.workers[id].kill();}
        });
    }
    console.log(`master ${process.pid} is running`);
} else if (cluster.isWorker) {
    http.createServer((request, response) => {
        response.writeHead(200);
        response.end('hello');
        process.send({cmd: 'notifyRequest'});
    }).listen('127.0.0.1:80');
    process.on('message', (msg) => {
        if (msg === 'shutdown') {console.log('shutting down');}
        process.send(msg);
    });
    console.log(`i am worker #${cluster.worker.id}`);
}
console.log(`worker ${process.pid} started`);

// master 3596 is running
// worker 4324 started
// worker 4520 started
// worker 6056 started
// worker 5644 started

cluster.on('fork', (worker) => {console.log(worker.id);});
cluster.on('setup', () => {});
cluster.on('disconnect', () => {});
cluster.on('exit', (worker, code, signal) => {console.log(worker.process.pid);});
cluster.on('listening', (worker, address) => {console.log(address.address);});
cluster.on('online', (worker) => {});
cluster.on('message', (worker, message, handle) => {console.log(message)});

cluster.fork(env); // env - key/value pairs to add to worker process environment
cluster.disconnect(function callback () {});
cluster.setupMaster(settings);

if (cluster.isMaster) {}
if (cluster.isWorker) {}

cluster.settings;
cluster.schedulingPolicy;
cluster.worker;
cluster.workers[id];

for (const id in cluster.workers) {cluster.workers[id].send('message to all workers');}

--------------------

const cluster = require('cluster');

cluster.setupMaster({
  exec: 'worker.js',
  args: ['--use', 'https'],
  silent: true
});
cluster.fork(); // https worker

cluster.setupMaster({
  exec: 'worker.js',
  args: ['--use', 'http']
});
cluster.fork(); // http worker

--------------------

const worker = cluster.fork();

worker.on('online', () => {console.log('worker is online');});
worker.on('disconnect', () => {console.log('worker has disconnected');});
worker.on('listening', (address) => {console.log('worker is listening');});
worker.on('error', (error) => {console.log(error);});
worker.on('exit', (code, signal) => {console.log(code);});
worker.on('message', (message, handle) => {});

worker.disconnect();
worker.send(message, sendHandle, function callback () {});

if (worker.exitedAfterDisconnect) {}
if (worker.isConnected()) {}
if (worker.isDead()) {}

worker.id;
worker.process;

worker.kill(/*signal*/ 'SIGTERM');

------------------------------------------------------------
Timers
------------------------------------------------------------

let timeoutId = setTimeout(callback, delay, ...args);
clearTimeout(timeoutId);

let intervalId = setInterval(callback, delay, ...args);
clearInterval(timeoutId);

let immediateId = setImmediate(callback, ...args);
clearImmediate(immediateId);

timeoutId.ref(); // requests that the Node.js event loop not exit so long as the Timeout is active.
timeoutId.unref(); // the active Timeout object will not require the Node.js event loop to remain active.

------------------------------------------------------------
Events
------------------------------------------------------------

const EventEmitter = require('events');

class MyEmitter extends EventEmitter {}

const myEmitter = new MyEmitter();

myEmitter.prependListener('event_name', function (a, b) {console.log(a + b);});

myEmitter.addListener('event_name', function (a, b) {console.log(a + b);});

myEmitter.on('my_event', function (a, b) {
    console.log(`my_event event occurred! arguments: ${a}, ${b}`);
    console.log(a, b, this);
    // Prints:
    //   a b MyEmitter {
    //     domain: null,
    //     _events: { event: [Function] },
    //     _eventsCount: 1,
    //     _maxListeners: undefined }
});


myEmitter.emit('my_event', 'a', 'b');

myEmitter.prependOnceListener('event', () => {console.log('emit only once, then deleted');});

myEmitter.once('event', () => {console.log('emit only once, then deleted');});

myEmitter.removeListener('my_event', callbackFunction);

myEmitter.removeAllListeners('my_event');

myEmitter.emit('error', new Error('whoops!')); // Throws and crashes Node.js

myEmitter.on('error', (error) => {console.log('whoops! there was an error');});

process.on('uncaughtException', (error) => {console.log('whoops! there was an error');});

console.log(EventEmitter.listenerCount(myEmitter, 'my_event'));

console.log(myEmitter.listeners('my_event'));

myEmitter.setMaxListeners(myEmitter.getMaxListeners() + 1); // 0 - infinity listeners, default = 0

console.log(myEmitter.eventNames());

------------------------------------------------------------
Modules
------------------------------------------------------------

if (require.main === module) {
    console.log('we run via: "node index.js"');
} else {
    console.log('we run via: require("./index.js")');
}

--------------------

console.log(module.id);
console.log(module.filename);
console.log(module.parent);
console.log(module.children);

if (module.loaded) {console.log('module is done loading');}

module.exports = {a: 'hello'}; // Only one export value
module.exports.someVar = 1; // Exported from require of module
exports.someVar = 1; // Exported from require of module
exports = {hello: false};  // Not exported, only available in the module

module.exports = exports = function func () {};

module.require('./../path/to/file.js');

--------------------

// File: square.js'

module.exports = function (width) {
    return {
        area: function () {return width * width;}
    };
}

// File: index.js

const square = require('./../folder/square.js');

console.log(square(2).area());

--------------------

Before a module's code is executed, Node.js will wrap it with a function wrapper that looks like the following:

(function (exports, require, module, __filename, __dirname) {
    // Your module code actually lives in here
    var a = 1;
    console.log(a);
});

It keeps top-level variables (defined with var, const or let) scoped to the module rather than the global object.
The module and exports objects that the implementor can use to export values from the module.
The convenience variables __filename and __dirname, containing the module's absolute filename and directory path.

--------------------

Node.js will search modules in the following locations:
1: $HOME/.node_modules
2: $HOME/.node_libraries
3: $PREFIX/lib/node

------------------------------------------------------------
Path
------------------------------------------------------------

const path = require('path');

path.join('/foo', 'bar', 'baz/asdf', 'quux', '..'); // Returns: '/foo/bar/baz/asdf'

path.normalize('/foo/bar//baz/asdf/quux/..'); // Returns: '/foo/bar/baz/asdf'
path.normalize('C:\\temp\\\\foo\\bar\\..\\'); // Returns: 'C:\\temp\\foo\\'

path.resolve('wwwroot', 'static_files/png/', '../gif/image.gif'); // Returns: '/home/myself/node/wwwroot/static_files/gif/image.gif'

path.relative(from, to);
path.relative('C:\\orandea\\test\\aaa', 'C:\\orandea\\impl\\bbb'); // Returns: '..\\..\\impl\\bbb'

if (path.isAbsolute('/foo/bar')) {}

path.basename(path, ext);
path.basename('/foo/bar/baz/asdf/quux.html', '.html'); // Returns: 'quux.html'

path.win32.basename('C:\\temp\\myfile.html'); // Returns: 'myfile.html'
path.posix.basename('/tmp/myfile.html'); // Returns: 'myfile.html'

'foo/bar/baz'.split(path.sep); // Returns: ['foo', 'bar', 'baz']
process.env.PATH.split(path.delimiter); // Returns: ['C:\\Windows\\system32', 'C:\\Windows', 'C:\\Program Files\\node\\']

console.log(path.dirname('/foo/bar/baz/asdf/quux')); // Returns: '/foo/bar/baz/asdf'
console.log(path.extname('index.coffee.md')); // Returns: '.md'

path.format({
    dir: '/home/user/dir',
    root: '/',
    base: 'file.txt',
    name: 'file',
    ext: '.txt'
}); // Returns: '/home/user/dir/file.txt'

path.parse('/home/user/dir/file.txt');
// Returns:
// {
//    root : "/",
//    dir : "/home/user/dir",
//    base : "file.txt",
//    ext : ".txt",
//    name : "file"
// }

------------------------------------------------------------
File System
------------------------------------------------------------

const fs = require('fs');

--------------------

fs.mkdir(path, mode, callback);
fs.mkdtemp('/tmp/foo-', (error, folder) => {console.log(folder);}); // Prints: /tmp/foo-itXde2
fs.readdir(path, options, callback);
fs.readFile('/etc/passwd', 'utf8', (error, data) => {console.log(data);});
fs.readlink(path, options, callback);
fs.read(fd, buffer, offset, length, position, callback);
fs.writeFile('message.txt', 'Hello Node.js', 'utf8', (error) => {console.log('It is saved!');});
fs.write(fd, buffer, offset, length, position, callback);
fs.write(fd, string, position, encoding, callback);
fs.open('./some/file.js', 'wx', (error, fileDescriptor) => {});
fs.close(fileDescriptor, () => {});
fs.appendFile('message.txt', 'data to append', 'utf8', (error) => {console.log('The "data to append" was appended to file!');});
fs.truncate(path, len, callback);
fs.rmdir(path, callback);
fs.unlink('./some/file.js', (error) => {console.log('file deleted');});
fs.link(existingPath, newPath, callback);
fs.rename('./some/file1.js', './some/file2.js', (error) => {console.log('file renamed');}); // and move
fs.symlink('./foo', './new-port');
fs.realpath(path, options, callback);
fs.stat('./some/file.js', (error,stats) => {console.log(`stats: ${JSON.stringify(stats)}`);});
fs.access('/etc/passwd', fs.constants.R_OK | fs.constants.W_OK, (error) => {console.log(error ? 'no access!' : 'can read/write');});
fs.exists('/etc/passwd', (exists) => {console.log(exists ? 'it is there' : 'no passwd!');});
fs.chmod(path, mode, callback);
fs.chown(path, uid, gid, callback);
fs.utimes(path, atime, mtime, callback);
fs.createReadStream(path, options);
fs.createWriteStream(path, options);
fs.watch('./tmp', {encoding: 'buffer'}, (eventType, filename) => {console.log(filename);});
fs.watchFile('message.txt', (current, previous) => {console.log(`the current mtime is ${current.mtime} and previous mtime was ${previous.mtime}`););
fs.unwatchFile(filename, listener);
fs.constants;

--------------------

fs.open('./some/file.js', 'wx', (error, fileDescriptor) => {
    if (error) {
        if (error.code === 'EEXIST') {
            console.error('myfile already exists');
            return;
        } else {
            throw error;
        }
    }
    writeMyData(fileDescriptor);
    fs.close(fileDescriptor, () => {});
});

--------------------

// API is not 100% consistent across platforms, and is unavailable in some situations.

const fileWatcher = fs.watch('./tmp', {encoding: 'buffer'}, (eventType, filename) => {console.log(filename);});

fileWatcher.on('change', (eventType, filename) => {});
fileWatcher.on('error', (error) => {});

fileWatcher.close();

------------------------------------------------------------
Buffer
------------------------------------------------------------

const buf = Buffer.from([1, 2, 3]);
const buf = Buffer.alloc(/*size*/ 10, /*fill*/ 1, 'utf-8');
if (Buffer.isBuffer(buf)) {}

------------------------------------------------------------
StringDecoder
------------------------------------------------------------

const StringDecoder = require('string_decoder').StringDecoder;

const decoder = new StringDecoder('utf8');

decoder.write(Buffer.from([0xC2, 0xA2]));
decoder.end(Buffer.from([0xC2, 0xA2]));

------------------------------------------------------------
Stream
------------------------------------------------------------

Readable - streams from which data can be read.
Writable - streams to which data can be written.
Duplex - streams that are both Readable and Writable.
Transform - Duplex streams that can modify or transform the data as it is written and read.

--------------------

const stream = require('stream');

--------------------

const writableStream = new require('stream').Writable();

writableStream.setDefaultEncoding('utf8');
writableStream.write('some data', 'utf8', callback);
writableStream.end('some data', 'utf8', callback);
writableStream.cork(); // forces all written data to be buffered in memory.
writableStream.uncork(); // buffered data will be flushed when stream.uncork()

stream.cork();
stream.write('some ');
stream.write('data ');
process.nextTick(() => stream.uncork());

writableStream.on('drain', () => {});
writableStream.on('finish', () => {console.log('All writes are now complete.');});
writableStream.on('close', () => {});
writableStream.on('error', () => {});
writableStream.on('pipe', (source) => {console.log('something is piping into the writer');});
writableStream.on('unpipe', (source) => {console.log('something has stopped piping into the writer.');});

--------------------

const readableStream = new require('stream').Readable();

readableStream.setEncoding('utf8');
let chunk = readableStream.read(size);
let chunk = readableStream.read(0); // return null
readableStream.pause(); // There will be no additional data for some time.
if (readableStream.isPaused()) {}
readableStream.resume(); // Now data will start flowing again.
readableStream.pipe(writableStream, options); // Start writing to file
readableStream.unpipe(writableStream); // Stop writing to file
readableStream.unshift(chunk); // Push a chunk of data back into the internal buffer
readableStream.wrap(stream); // create a Readable stream that uses the old stream as its data source

readableStream.on('readable', () => {console.log('there is some data to read now'); let chunk = readableStream.read()});
readableStream.on('data', (chunk) => {console.log(`Received ${chunk.length} bytes of data.`);});
readableStream.on('end', () => {console.log('There will be no more data.');});
readableStream.on('close', () => {});
readableStream.on('error', () => {});

--------------------

const duplexStream = new require('stream').Duplex;
const transformStream = new require('stream').Transform;

--------------------

const file = fs.createWriteStream('example.txt');
file.write('hello, ');
file.end('world!');

--------------------

const fs = require('fs');
const rr = fs.createReadStream('foo.txt');
rr.on('readable', () => {console.log('readable:', rr.read());});
rr.on('end', () => {console.log('end');});

--------------------

const readableStream = fs.createReadStream('file.txt');
const gzipStream = zlib.createGzip();
const writableStream = fs.createWriteStream('file.txt.gz');
readableStream.pipe(gzipStream).pipe(writableStream);

--------------------

reader.pipe(writer, {end: false});
reader.on('end', () => {writer.end('Goodbye\n');});

--------------------

const OldReader = require('./old-api-module.js').OldReader;
const Readable = require('stream').Readable;

const oreader = new OldReader;
const myReader = new Readable().wrap(oreader);

myReader.on('readable', () => {myReader.read();});

------------------------------------------------------------
ZLIB
------------------------------------------------------------

const gzip = zlib.createGzip();

const fs = require('fs');
const inp = fs.createReadStream('input.txt');
const out = fs.createWriteStream('input.txt.gz');

inp.pipe(gzip).pipe(out);

response.pipe(zlib.createGunzip()).pipe(output); // 'gzip'
response.pipe(zlib.createInflate()).pipe(output); // 'deflate'

const output = zlib.createGzip();
output.pipe(response);
output.flush();

--------------------

zlib.flush(kind, callback);
zlib.params(level, strategy, callback);

zlib.reset();

zlib.createDeflate(options);
zlib.createDeflateRaw(options);

zlib.createGunzip(options);
zlib.createGzip(options);

zlib.createInflate(options);
zlib.createInflateRaw(options);

zlib.createUnzip(options);

zlib.deflate(buf, options, callback)'
zlib.deflateSync(buf, options)'
zlib.deflateRaw(buf, options, callback);
zlib.deflateRawSync(buf, options]);

zlib.gunzip(buf, options, callback);
zlib.gunzipSync(buf, options]);

zlib.gzip(buf, options, callback);
zlib.gzipSync(buf, options);

zlib.inflate(buf, options, callback);
zlib.inflateSync(buf, options);
zlib.inflateRaw(buf, options, callback);
zlib.inflateRawSync(buf, options);

zlib.unzip(buf, options, callback);
zlib.unzipSync(buf, options);

zlib.constants.Z_NO_FLUSH;
zlib.constants.Z_PARTIAL_FLUSH;
zlib.constants.Z_SYNC_FLUSH;
zlib.constants.Z_FULL_FLUSH;
zlib.constants.Z_FINISH;
zlib.constants.Z_BLOCK;
zlib.constants.Z_TREES;

------------------------------------------------------------
Crypto
------------------------------------------------------------

// Cipher

const crypto = require('crypto');

const cipher = crypto.createCipher('aes192', 'a password');

var encrypted = '';

cipher.on('readable', () => {
    var data = cipher.read();
    if (data) {encrypted += data.toString('hex');}
});

cipher.on('end', () => {
    console.log(encrypted); // Prints: ca981be48e90867604588e75d04feabb63cc007a8f8ad89b10616ed84d815504
});

cipher.write('some clear text data');
cipher.end();

--------------------

// Decipher

const crypto = require('crypto');

const decipher = crypto.createDecipher('aes192', 'a password');

var decrypted = '';

decipher.on('readable', () => {
    var data = decipher.read();
    if (data) {decrypted += data.toString('utf8');}
});

decipher.on('end', () => {
    console.log(decrypted); // Prints: some clear text data
});

var encrypted = 'ca981be48e90867604588e75d04feabb63cc007a8f8ad89b10616ed84d815504';

decipher.write(encrypted, 'hex');
decipher.end();

------------------------------------------------------------
Punycode
------------------------------------------------------------

const punycode = require('punycode');

console.log(punycode.version);

punycode.decode('maana-pta'); // 'manana'
punycode.encode('manana'); // 'maana-pta'

punycode.toASCII('manana.com');  // 'xn--maana-pta.com'
punycode.toUnicode('xn--maana-pta.com'); // 'manana.com'

punycode.ucs2.decode('abc'); // [0x61, 0x62, 0x63]
unycode.ucs2.encode([0x61, 0x62, 0x63]); // 'abc'

------------------------------------------------------------
DNS
------------------------------------------------------------

const dns = require('dns');

dns.lookup('nodejs.org', (error, addresses, family) => {console.log(`addresses: ${addresses}`);});
dns.lookupService('127.0.0.1', 22, (error, hostname, service) => {console.log(hostname, service);});
dns.resolve('nodejs.org', rrtype, (error, addresses) => {});

------------------------------------------------------------
HTTP
------------------------------------------------------------

const http = require('http');

console.log(http.METHODS);
console.log(http.STATUS_CODES);

if (http.STATUS_CODES[404] === 'Not Found') {console.log('yes');}

--------------------

const http = require('http');

const options = {
    hostname: 'localhost',
    port: 80,
    path: '/',
    agent: false  // create a new agent just for this one request
};

options.agent = new http.Agent({keepAlive: true}); // connection keep alive agent

http.request(options, onResponseCallback);

--------------------

const http = require('http');

http.get('http://nodejs.org/dist/index.json', (response) => {
    const statusCode = response.statusCode;
    const contentType = response.headers['content-type'];
    let error;
    if (statusCode !== 200) {
        error = new Error(`Request Failed. Status Code: ${statusCode}`);
    } else if (!/^application\/json/.test(contentType)) {
        error = new Error(`Invalid content-type. Expected application/json but received ${contentType}`);
    }
    if (error) {
        console.log(error.message);
        // consume response data to free up memory
        response.resume();
        return;
    }
    response.setEncoding('utf8');
    let rawData = '';
    res.on('data', (chunk) => rawData += chunk);
    res.on('end', () => {
        try {
            let parsedData = JSON.parse(rawData);
            console.log(parsedData);
        } catch (e) {
            console.log(e.message);
        }
    });
}).on('error', (e) => {console.log(`Got error: ${e.message}`);});

--------------------

const http = require('http');

var postData = 'msg=Hello';

var options = {
    port: 1337,
    hostname: '127.0.0.1',
    path: 'www.google.com:80'
    method: 'POST',
    headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
        'Content-Length': Buffer.byteLength(postData)
    }
};

const clientRequest = http.request(options, (response) => {
    console.log(`STATUS: ${response.statusCode}`);
    console.log(`HEADERS: ${response.headers}`);
    response.setEncoding('utf8');
    response.on('data', (chunk) => {console.log(`BODY: ${chunk}`);});
    response.on('end', () => {console.log('No more data in response.');});
});
clientRequest.on('error', (error) => {console.log(`problem with request: ${error.message}`);});
clientRequest.write(postData, 'utf8', callback);
clientRequest.end(data, encoding, callback);

clientRequest.setNoDelay(noDelay);
clientRequest.setSocketKeepAlive(enable, initialDelay);
clientRequest.setTimout(timeout, callback);

clientRequest.abort();
clientRequest.flushHeaders();

clientRequest.on('connect', (response, socket, head) => {console.log('got connected');});
clientRequest.on('abort', () => {});
clientRequest.on('aborted', () => {});
clientRequest.on('continue', () => {});
clientRequest.on('upgrade', (response, socket, head) => {console.log('got upgraded');});
clientRequest.on('socket', (socket) => {});
clientRequest.on('response', (response) => {});

--------------------

const http = require('http');

const server = http.createServer((request, response) => {

    // --- Request ---

    console.log(request.httpVersion);
    console.log(request.headers); // {'user-agent': 'curl/7.22.0', host: '127.0.0.1:8000', accept: '*/*'}
    console.log(request.rawHeaders);// ['User-Agent', 'curl/7.22.0', 'Host', '127.0.0.1:8000', 'ACCEPT', '*/*']
    console.log(request.trailers);
    console.log(request.rawTrailers);
    console.log(request.socket.getPeerCertificate());

    if (request.method === 'POST') {
        // process POST data
    } else if (request.method === 'GET') {
        // process GET data
    }

    if (request.url === '/status?name=ryan') {
        console.log(require('url').parse(request.url, true).query.name); // ryan
    }

    request.setTimeout(msecs, callback);

    request.destroy();

    request.on('aborted', () => {});
    request.on('close', () => {});

    // --- Resoponse ---

    response.setHeader('Content-Type', 'text/html');
    response.setHeader('Set-Cookie', ['type=ninja', 'language=javascript']);
    response.removeHeader('Content-Encoding');
    var contentType = response.getHeader('content-type');

    response.statusCode = 404;
    response.statusMessage = 'Not found';

    response.writeHead(200, 'OK', {'Content-Type': 'text/plain',
                                                 'Trailer': 'Content-MD5'});
    response.write('Hello', 'utf8', () => {console.log('chunk sent to browser');});
    response.writeContinue();
   
    response.addTrailers({'Content-MD5': '7895bf4b8828b55ceaf47747b4bca667'});

    console.log(response.headersSent);

    if (response.sendDate) {console.log('Date header will be automatically sent');}

    if (response.finished) {console.log('no');} // starts as false
    response.end('world', 'utf8', () => {console.log('page sent to browser');});
    if (response.finished) {console.log('yes');} true after response.end() executed

    response.setTimeout(msecs, callback);

    response.on('close', () => {});
    response.on('finish', () => {});

});

console.log(server.timeout);
server.setTimeout(msec, callback);

server.listen(80, '127.0.0.1', backlogNumber, () => {
    console.log('Server started at 127.0.0.1:80');
});

server.close();

if (server.listening) {console.log('server is listening');}

console.log(server.maxHeadersCount);

server.on('request', (request, response) => {});
server.on('connect', (request, socket, head) => {});
server.on('connection', (socket) => {'we are connected'});
server.on('close', () => {console.log('connection closed');});
server.on('checkContinue', (request, response) => {});
server.on('checkExpectation', (request, response) => {});
server.on('upgrade', (request, socket, head) => {});
server.on('clientError', (error, socket) => {socket.end('HTTP/1.1 400 Bad Request\r\n\r\n');});

------------------------------------------------------------
HTTPS
------------------------------------------------------------

const https = require('https');

https.get('https://encrypted.google.com/', (response) => {
    console.log('statusCode:', response.statusCode);
    console.log('headers:', res.headers);
    response.on('data', (data) => {
        process.stdout.write(data);
    });
}).on('error', (error) => {console.error(error);});

--------------------

const https = require('https');

var options = {
    hostname: 'encrypted.google.com',
    port: 443,
    path: '/',
    method: 'GET',
    key: fs.readFileSync('test/fixtures/keys/agent2-key.pem'),
    cert: fs.readFileSync('test/fixtures/keys/agent2-cert.pem'),
    agent: false
};

options.agent = new https.Agent(options);

var clientRequest = https.request(options, (response) => {
    console.log('statusCode:', response.statusCode);
    console.log('headers:', response.headers);
    response.on('data', (data) => {
        process.stdout.write(data);
    });
});

clientReques.on('error', (error) => {console.error(error);});
clientReques.end();

--------------------

// curl -k https://localhost:8000/

const https = require('https');

const fs = require('fs');

const options = {
    key: fs.readFileSync('test/fixtures/keys/agent2-key.pem'),
    cert: fs.readFileSync('test/fixtures/keys/agent2-cert.pem')
};

// or
// const options = {
//     pfx: fs.readFileSync('server.pfx')
// };

https.createServer(options, (request, response) => {
    request.writeHead(200);
    request.end('hello world');
}).listen(8000);

------------------------------------------------------------
URL
------------------------------------------------------------

const url = require('url');

http://user:pass@host.com:8080/p/a/t/h?query=string#hash

urlObject.href; // 'http://user:pass@host.com:8080/p/a/t/h?query=string#hash'
urlObject.protocol; // 'http:'
if (urlObject.slashes) {}
urlObject.auth; // 'user:pass'
urlObject.host; // 'host.com:8080'
urlObject.hostname; // 'host.com'
urlObject.port; // '8080'
urlObject.path; // /p/a/t/h?query=string'
urlObject.pathname; // '/p/a/t/h'
urlObject.search; // '?query=string'
urlObject.query; // 'query=string'
urlObject.hash; // '#hash'

url.format(urlObject);
url.parse(urlString, parseQueryString, slashesDenoteHost);

url.resolve(from, to);
url.resolve('http://example.com/one', '/two') // 'http://example.com/two');

Escaped Characters: < > " ` \r \n \t { } | \ ^ '

------------------------------------------------------------
Query String
------------------------------------------------------------

const querystring = require('querystring');

querystring.escape(str);
querystring.unescape(str);

querystring.parse('foo=bar&abc=xyz&abc=123', sep, eq, {decodeURIComponent: gbkDecodeURIComponent}); // {foo: 'bar', abc: ['xyz', '123']}
querystring.stringify({foo: 'bar', baz: 'qux'}, ';', ':', options); // returns 'foo:bar;baz:qux'

------------------------------------------------------------
Net
------------------------------------------------------------

const net = require('net');

// Одно и то же
// const client = net.connect({path: '/tmp/echo.sock'}, () => {});
// const client = net.connect({path: '/tmp/echo.sock'}, () => {});
// const client = net.createConnection({port: 8124}, () => {});

const client = net.connect({port: 8124}, () => { // 'connect' listener
    console.log('connected to server!');
    client.write('world!');
});

client.on('data', (data) => {
    console.log(data.toString());
    client.end();
});

client.on('end', () => {
    console.log('disconnected from server');
});

--------------------

const net = require('net');

const server = net.createServer((connection) => { // 'connection' listener
    console.log('client connected');
    connection.on('end', () => {console.log('client disconnected');});
    connection.write('hello');
    connection.pipe(connection);
});
server.on('error', (error) => {throw error;});
server.listen(8124, () => {console.log('server bound');});

--------------------

const net = require('net');

var server = net.createServer((socket) => {
    socket.end('goodbye');
}).on('error', (error) => {throw error;});

// use random port
server.listen(() => {
    console.log('opened server on', server.address());
});

server.close(callback);
server.address();
server.getConnections(callback); // server.connections

server.listen({port, host, backlog, path, exlcusive}, callback);
server.listen(handle, backlogNumber, callback);
server.listen(path, backlogNumber, callback]);
server.listen(port, hostname, backlogNumber, callback);
server.listen({
    host: 'localhost',
    port: 80,
    exclusive: true
});
server.listen(path.join('\\\\?\\pipe', process.cwd(), 'myctl'));

if (server.listening) {console.log('server is listening');}

console.log(server.maxConnections);

server.ref();
server.unref();

server.on('close', () => {});
server.on('connection', (socket) => {});
server.on('error', (error) => {
    if (error).code == 'EADDRINUSE') {server.close();}
});
server.on('listening', () => {});

--------------------

const net = require('net');

const socket = new net.Socket({
    fd: null,
    allowHalfOpen: false,
    readable: false,
    writable: false
});

socket.connect({port, host, localAddress, localPort, path, family, hints, lookpup});
socket.connect(port, host, connectListener);
socket.connect(path, connectListener);

socket.setEncoding('utf8');
socket.setKeepAlive(enable, initialDelay);
socket.setNoDelay(noDelay);
socket.setTimeout(timeout, callback);

socket.write(data, encoding, callback);
socket.end(data, encoding);

console.log(socket.address());
console.log(socket.localAddress);
console.log(socket.localPort);
console.log(socket.remoteAddress);
console.log(socket.remotePort);
console.log(socket.remoteFamily);
console.log(socket.bufferSize);
console.log(socket.bytesRead);
console.log(socket.bytesWritten);

if (socket.connecting) {}

socket.pause();
socket.resume();
socket.ref();
socket.unref();
socket.destroy();

if (socket.destroyed) {}

socket.on('lookup', (error) => {});
socket.on('connect', () => {});
socket.on('close', () => {});
socket.on('data', (data) => {});
socket.on('drain', () => {});
socket.on('end', () => {});
socket.on('timeout', () => {});
socket.on('error', (error) => {});

--------------------

const net = require('net');

if (net.isIP(input)) {}
if (net.isIPv4(input)) {}
if (net.isIPv6(input)) {}

------------------------------------------------------------
UDP/Datagram
------------------------------------------------------------

const dgram = require('dgram');

const socket = dgram.createSocket('udp4');

server.on('error', (error) => {
    console.log(`server error:\n${error.stack}`);
    server.close();
});

server.on('message', (msg, rinfo) => {
    console.log(`server got: ${msg} from ${rinfo.address}:${rinfo.port}`);
});

server.on('listening', () => {
    var address = server.address();
    console.log(`server listening ${address.address}:${address.port}`);
});

server.bind(41234); // server listening 0.0.0.0:41234

--------------------

const dgram = require('dgram');

const client = dgram.createSocket('udp4');
const message = Buffer.from('Some bytes');
client.send(message, 41234, 'localhost', (err) => {
    client.close();
});

--------------------

const socket = dgram.createSocket('udp4');
socket.bind(1234);
socket.addMembership('224.0.0.114');

--------------------

socket.addMembership(multicastAddress, multicastInterface);

socket.address();

socket.bind(port, address, callback);
socket.bind({address: 'localhost', port: 8000, exclusive: true}, callback);

socket.close(callback);

socket.dropMembership(multicastAddress, multicastInterface);

socket.send(msg, offset, length, port, address, callback);

socket.setBroadcast(flag);
socket.setMulticastLoopback(flag);
socket.setMulticastTTL(ttl);
socket.setTTL(ttl);

socket.ref();
socket.unref();

socket.on('close', () => {});
socket.on('error', (error) => {});
socket.on('listening', () => {});
socket.on('message', (message, {address, family, port, size}) => {});

--------------------

dgram.createSocket(options, callback);
dgram.createSocket(type, callback);

------------------------------------------------------------
TLS (SSL)
------------------------------------------------------------

const tls = require('tls');

tlsServer.listen(port, hostname, callback);
tlsServer.addContext(hostname, context);
console.log(tlsServer.address());
console.log(tlsServer.connections);
tlsServer.setTicketKeys(keys);
tlsServer.getTicketKeys();
tlsServer.close(callback);

tlsServer.on('OCSPRequest', () => {});
tlsServer.on('secureConnection', () => {});
tlsServer.on('tlsClientError', () => {});
tlsServer.on('newSession', (id, data, callback) => {});
tlsServer.on('resumeSession', (id, callback) => {});

--------------------

tlsSocket.address();
if (tlsSocket.authorized) {}
tlsSocket.authorizationError;
if (tlsSocket.encrypted) {};
tlsSocket.getCipher();
tlsSocket.getEphemeralKeyInfo();
tlsSocket.getPeerCertificate(detailed);
tlsSocket.getProtocol();
tlsSocket.getSession();
tlsSocket.getTLSTicket();
console.log(tlsSocket.localAddress);
console.log(tlsSocket.localPort);
console.log(tlsSocket.remoteAddress);
console.log(tlsSocket.remoteFamily);
console.log(tlsSocket.remotePort);
tlsSocket.renegotiate(options, callback);
tlsSocket.setMaxSendFragment(size);

tlsTLSSocket.on('OCSPResponse' => {});
tlsTLSSocket.on('secureConnect' => {});

--------------------

tls.connect(port, host, options, callback);
tls.connect(path, options, callback);
tls.connect(options, callback);
tls.createSecureContext(options);
tls.createServer(options, secureConnectionListener);
tls.getCiphers();
tls.DEFAULT_ECDH_CURVE

--------------------

const tls = require('tls');
const fs = require('fs');

const options = {
    // Necessary only if using the client certificate authentication
    key: fs.readFileSync('client-key.pem'),
    cert: fs.readFileSync('client-cert.pem'),
    // Necessary only if the server uses the self-signed certificate
    ca: [ fs.readFileSync('server-cert.pem') ]
};

const socket = tls.connect(8000, options, () => {
    console.log('client connected', socket.authorized ? 'authorized' : 'unauthorized');
    process.stdin.pipe(socket);
    process.stdin.resume();
});
socket.setEncoding('utf8');
socket.on('data', (data) => {console.log(data);});
socket.on('end', () => {server.close();});

--------------------

const tls = require('tls');
const fs = require('fs');

const options = {
    key: fs.readFileSync('server-key.pem'),
    cert: fs.readFileSync('server-cert.pem'),
    // This is necessary only if using the client certificate authentication.
    requestCert: true,
    // This is necessary only if the client uses the self-signed certificate.
    ca: [ fs.readFileSync('client-cert.pem') ]
};

const server = tls.createServer(options, (socket) => {
    console.log('server connected', socket.authorized ? 'authorized' : 'unauthorized');
    socket.write('welcome!\n');
    socket.setEncoding('utf8');
    socket.pipe(socket);
});
server.listen(8000, () => {console.log('server bound');});

------------------------------------------------------------
TTY - text terminal
------------------------------------------------------------

const tty = require('tty');

--------------------

tty.ReadStream

if (readStream.isRaw) {}

readStream.setRawMode(mode);

--------------------

tty.WriteStream

process.stdout.on('resize', () => {
    console.log('screen size has changed!');
    console.log(`${process.stdout.columns}x${process.stdout.rows}`);
});

console.log(writeStream.columns);
console.log(writeStream.rows);

--------------------

if (tty.isatty(fd)) {}

------------------------------------------------------------
V8
------------------------------------------------------------

const v8 = require('v8');

v8.setFlagsFromString('--trace_gc');
v8.setFlagsFromString('--notrace_gc');

console.log(v8.getHeapStatistics());
console.log(v8.getHeapSpaceStatistics());

------------------------------------------------------------
VM - Eval code
------------------------------------------------------------

const vm = require('vm');

const script = new vm.Script(code, options);

script.runInContext(contextifiedSandbox, options);
script.runInNewContext(sandbox, options);
script.runInThisContext(options);

vm.createContext(sandbox);
if (vm.isContext(sandbox)) {}
vm.runInContext(code, contextifiedSandbox, options);
vm.runInDebugContext(code);
vm.runInNewContext(code[, sandbox, options]);
vm.runInThisContext(code, options);

--------------------

const vm = require('vm');
const util = require('util');

const sandbox = {animal: 'cat', count: 2};
const script = new vm.Script('count += 1; name = "kitty";');
const context = new vm.createContext(sandbox);
for (var i = 0; i < 10; ++i) {script.runInContext(context);}

console.log(util.inspect(sandbox)); // { animal: 'cat', count: 12, name: 'kitty' }

--------------------

const vm = require('vm');
const util = require('util');

const script = new vm.Script('globalVar = "set"');
const sandboxes = [{}, {}, {}];
sandboxes.forEach((sandbox) => {script.runInNewContext(sandbox);});

console.log(util.inspect(sandboxes));// [{ globalVar: 'set' }, { globalVar: 'set' }, { globalVar: 'set' }]

--------------------

const vm = require('vm');

global.globalVar = 0;
const script = new vm.Script('globalVar += 1', { filename: 'myfile.vm' });

for (var i = 0; i < 1000; ++i) {script.runInThisContext();}

console.log(globalVar); // 1000

--------------------

const vm = require('vm');
const util = require('util');

var globalVar = 3;

const sandbox = { globalVar: 1 };
vm.createContext(sandbox);

vm.runInContext('globalVar *= 2;', sandbox);

console.log(util.inspect(sandbox)); // 2
console.log(util.inspect(globalVar)); // 3

--------------------

const vm = require('vm');
const util = require('util');

const sandbox = { globalVar: 1 };
vm.createContext(sandbox);

for (var i = 0; i < 10; ++i) {vm.runInContext('globalVar *= 2;', sandbox);}
console.log(util.inspect(sandbox)); // { globalVar: 1024 }

--------------------

const vm = require('vm');
const Debug = vm.runInDebugContext('Debug');
console.log(Debug.findScript(process.emit).name);  // 'events.js'
console.log(Debug.findScript(process.exit).name);  // 'internal/process.js'

--------------------

const vm = require('vm');
const util = require('util');

const sandbox = {animal: 'cat', count: 2};

vm.runInNewContext('count += 1; name = "kitty"', sandbox);
console.log(util.inspect(sandbox)); // { animal: 'cat', count: 3, name: 'kitty' }

--------------------

const vm = require('vm');

var localVar = 'initial value';

const vmResult = vm.runInThisContext('localVar = "vm";');
console.log('vmResult:', vmResult);
console.log('localVar:', localVar);

const evalResult = eval('localVar = "eval";');
console.log('evalResult:', evalResult);
console.log('localVar:', localVar);

// vmResult: 'vm', localVar: 'initial value'
// evalResult: 'eval', localVar: 'eval'

--------------------

Running an HTTP Server within a VM

const vm = require('vm');

let code =
`(function(require) {

   const http = require('http');

   http.createServer( (request, response) => {
     response.writeHead(200, {'Content-Type': 'text/plain'});
     response.end('Hello World\\n');
   }).listen(8124);

   console.log('Server running at http://127.0.0.1:8124/');
})`;

vm.runInThisContext(code)(require);