├── stream-adventure ├── zipped-books.gz ├── input_output.js ├── beep_boop.js ├── meet_pipe.js ├── crypt.js ├── websockets.js ├── http_client.js ├── concat.js ├── html_stream.js ├── duplexer.js ├── secretz.js ├── transform.js ├── lines.js ├── duplexer_redux.js ├── http_server.js └── combiner.js ├── .gitignore ├── file-system ├── print-content │ ├── file1.txt │ ├── file2.txt │ ├── file3.txt │ ├── print-content.test.js │ ├── print-content-stream.test.js │ ├── print-content.js │ └── print-content-stream.js ├── concat │ ├── concat-files.js │ └── concat-files.test.js └── watcher │ ├── watcher.js │ └── watcher.test.js ├── streams ├── csv.csv ├── unzipped-encripted.txt ├── zipped-encripted.gz ├── csv │ ├── example.csv │ ├── result.json │ └── custom-csv-parser.js ├── csv-stream.js ├── stream-duplex-echo.js ├── zip-decrypt.js ├── stream-codify-text.js ├── zip-encrypt.js ├── readable-to-output.js ├── stream-object-string-transform.js └── stream-object-string-transform-by-char.js ├── module-system ├── data.json ├── children-exports-nothing.js ├── parent.js └── children-module.js ├── child-process ├── long-run │ ├── long-run.js │ ├── long-run-in-child-fork.js │ │ ├── long-run-fork.js │ │ └── index.js │ └── long-run-in-exec-file │ │ ├── long-run-exec-file.js │ │ └── index.js ├── exec.js ├── fork │ ├── index.js │ └── fork-child.js ├── spawn-as-exec.js └── spawn.js ├── .eslintrc.json ├── error-handling ├── async-error-right.js ├── uncaught-exception.js ├── unhandled-promise-error.js ├── async-error-wrong.js ├── unhandled-promise-error-test.test.js └── unhandled-error-event.js ├── prototype ├── prototype-data.js └── prototype-data.test.js ├── net ├── socket-server.js └── socket-client.js ├── scope-chains-closures ├── scope-chain.js ├── closure.js ├── scopes.js ├── global-scope-shadow.js └── garbage-collector.js ├── debug ├── server.js └── console.js ├── control-flow ├── async-secuencial-for-loop.js ├── async-parallel-for-loop.js ├── async-parallel-for-loop-waranty-order.js ├── async-parallel-for-loop-limit-concurency.js └── async-parallel-for-loop-limit-concurency-waranty-result.js ├── README.md ├── process-operating └── index.js ├── buffers └── sized-buffer.js ├── events ├── server-request-wit-events │ ├── date-event-handler-without-class.js │ ├── index-without-class.js │ ├── index-with-class.js │ └── date-event-handler-with-class.js └── event-evemitter │ ├── event-emitter-class.js │ ├── event-emitter-class-map.js │ ├── event-emitter-obj.js │ ├── event-emitter-proto.js │ ├── event-emitter-class.test.js │ ├── event-emitter-obj.test.js │ ├── event-emitter-proto.test.js │ └── event-emitter-class-map.test.js ├── package.json └── study-guide-resource └── Node.js Certification Study Guide by Hey Node_files ├── analytics.js └── gtm.js /stream-adventure/zipped-books.gz: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store -------------------------------------------------------------------------------- /file-system/print-content/file1.txt: -------------------------------------------------------------------------------- 1 | File1.text content 2 | -------------------------------------------------------------------------------- /file-system/print-content/file2.txt: -------------------------------------------------------------------------------- 1 | File2.text content 2 | -------------------------------------------------------------------------------- /file-system/print-content/file3.txt: -------------------------------------------------------------------------------- 1 | File3.text content 2 | -------------------------------------------------------------------------------- /streams/csv.csv: -------------------------------------------------------------------------------- 1 | name:surname 2 | ange:cere 3 | pepe:garc 4 | juan:romero -------------------------------------------------------------------------------- /module-system/data.json: -------------------------------------------------------------------------------- 1 | { 2 | "prop1": "prop1", 3 | "prop2": "prop2" 4 | } -------------------------------------------------------------------------------- /streams/unzipped-encripted.txt: -------------------------------------------------------------------------------- 1 | 2 | Hello There! 3 | How are you? 4 | 5 | See you next time! 6 | -------------------------------------------------------------------------------- /streams/zipped-encripted.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amcereijo/node-certification/HEAD/streams/zipped-encripted.gz -------------------------------------------------------------------------------- /streams/csv/example.csv: -------------------------------------------------------------------------------- 1 | name:surname:age:sex 2 | nameA:surnameA:10:m 3 | nameB:surnameB:15:f 4 | nameC:surnameC:5:f 5 | nameD:surnameD:80:m -------------------------------------------------------------------------------- /child-process/long-run/long-run.js: -------------------------------------------------------------------------------- 1 | const interval = setInterval(() => { 2 | console.log('interval...'); 3 | }, 100); 4 | 5 | console.log('init'); 6 | for (let i = 0; i < 9000000000; i += 1) { /* */ } 7 | console.log('end'); 8 | clearInterval(interval); 9 | -------------------------------------------------------------------------------- /stream-adventure/input_output.js: -------------------------------------------------------------------------------- 1 | /* 2 | Take data from `process.stdin` and pipe it to `process.stdout`. 3 | 4 | With `.pipe()`. `process.stdin.pipe()` to be exact. 5 | 6 | Don't overthink this. 7 | */ 8 | 9 | process.stdin.pipe(process.stdout); 10 | -------------------------------------------------------------------------------- /child-process/long-run/long-run-in-child-fork.js/long-run-fork.js: -------------------------------------------------------------------------------- 1 | 2 | process.once('message', (data) => { 3 | console.log('fork start for', data); 4 | for (let i = 0; i < 9000000000; i += 1) { /* do nothing */ } 5 | console.log('\nfork ends for', data); 6 | process.send('Finish!'); 7 | }); 8 | -------------------------------------------------------------------------------- /child-process/exec.js: -------------------------------------------------------------------------------- 1 | const { exec } = require('child_process'); 2 | 3 | exec('find . -type f | wc -f', (err, stdout, stderr) => { 4 | if (err) { 5 | console.error(`exec error ${err} => ${stderr}`); 6 | return; 7 | } 8 | 9 | console.log(`Number of lines ${stdout}`); 10 | }); 11 | -------------------------------------------------------------------------------- /streams/csv/result.json: -------------------------------------------------------------------------------- 1 | { 2 | "data": [ 3 | {"name":"nameA","surname":"surnameA","age":"10","sex":"m"}, 4 | {"name":"nameB","surname":"surnameB","age":"15","sex":"f"}, 5 | {"name":"nameC","surname":"surnameC","age":"5","sex":"f"}, 6 | {"name":"nameD","surname":"surnameD","age":"80","sex":"m"}, 7 | ] 8 | } -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb-base", 3 | "env": { 4 | "commonjs": true, 5 | "es6": true, 6 | "node": true, 7 | "jest": true 8 | }, 9 | "rules": { 10 | "no-console": "off" 11 | }, 12 | "ignorePatterns": ["node_modules/", "study-guide-resource/"] 13 | } -------------------------------------------------------------------------------- /error-handling/async-error-right.js: -------------------------------------------------------------------------------- 1 | const func = () => new Promise((resolve, reject) => { 2 | setImmediate(() => reject(new Error('opss'))); 3 | }); 4 | 5 | const main = async () => { 6 | try { 7 | await func(); 8 | } catch (ex) { 9 | console.log('Err will be executed: ', ex); 10 | } 11 | }; 12 | 13 | main(); 14 | -------------------------------------------------------------------------------- /stream-adventure/beep_boop.js: -------------------------------------------------------------------------------- 1 | /* 2 | Create a new file called beep_boop.js that uses console.log to output "beep boop". 3 | 4 | To verify your program has the expected output, run: 5 | 6 | stream-adventure verify beep_boop.js 7 | 8 | for more options, run `stream-adventure help`. 9 | */ 10 | 11 | console.log('beep boop'); 12 | -------------------------------------------------------------------------------- /child-process/fork/index.js: -------------------------------------------------------------------------------- 1 | const { fork } = require('child_process'); 2 | 3 | const forked = fork(`${__dirname}/fork-child.js`); 4 | 5 | forked.on('message', (data) => { 6 | console.log('Message from child %j', data); 7 | if (data.end) { 8 | forked.kill('SIGINT'); 9 | } 10 | }); 11 | 12 | forked.send({ hello: 'wold' }); 13 | -------------------------------------------------------------------------------- /child-process/fork/fork-child.js: -------------------------------------------------------------------------------- 1 | process.on('message', (data) => { 2 | console.log('Mesage from parent %j', data); 3 | }); 4 | 5 | let counter = 0; 6 | const limit = 10; 7 | setInterval(() => { 8 | if (counter < limit) { 9 | process.send({ counter: counter += 1 }); 10 | } else { 11 | process.send({ end: true }); 12 | } 13 | }, 1000); 14 | -------------------------------------------------------------------------------- /module-system/children-exports-nothing.js: -------------------------------------------------------------------------------- 1 | // this will not work as module.exports does becauses exports === module.exports (true) 2 | console.log('\nNow this exports === module.export is', (exports === module.exports)); 3 | exports = { 4 | thisWillNotExists: true, 5 | }; 6 | console.log('Now this exports === module.export is', (exports === module.exports), '\n'); 7 | -------------------------------------------------------------------------------- /child-process/long-run/long-run-in-exec-file/long-run-exec-file.js: -------------------------------------------------------------------------------- 1 | process.stdin.on('data', (data) => { 2 | console.log('exec - file start for', String(data), ' with ', process.env.NAME); 3 | for (let i = 0; i < 9000000000; i += 1) { /* do nothing */ } 4 | console.log('exec - file ends for', String(data)); 5 | // process.stdout.write('Finish!'); 6 | process.exit(0); 7 | }); 8 | -------------------------------------------------------------------------------- /error-handling/uncaught-exception.js: -------------------------------------------------------------------------------- 1 | process.on('uncaughtException', (err) => { 2 | console.log('Error happened', err); 3 | }); 4 | 5 | function callError() { 6 | throw new Error('New Error'); 7 | } 8 | 9 | setTimeout(() => { 10 | // without process.on('uncaughtException', this will nor work 11 | console.log('Error not break the flow'); 12 | }, 1000); 13 | 14 | callError(); 15 | -------------------------------------------------------------------------------- /prototype/prototype-data.js: -------------------------------------------------------------------------------- 1 | function SuperAritmetic() { 2 | 3 | } 4 | 5 | SuperAritmetic.prototype.subtract = function subtract() {}; 6 | 7 | function Aritmetic() { 8 | this.sum = function sum() {}; 9 | } 10 | 11 | Aritmetic.prototype = Object.create(SuperAritmetic.prototype); 12 | Aritmetic.prototype.plus = function plus() {}; 13 | 14 | module.exports = { 15 | Aritmetic, 16 | }; 17 | -------------------------------------------------------------------------------- /file-system/concat/concat-files.js: -------------------------------------------------------------------------------- 1 | const contactFilesModule = require('concat-files'); 2 | 3 | function concatFiles(files, destFile) { 4 | return new Promise((resolve, reject) => { 5 | contactFilesModule(files, destFile, (err) => { 6 | if (err) { 7 | return reject(err); 8 | } 9 | return resolve(); 10 | }); 11 | }); 12 | } 13 | 14 | module.exports = { concatFiles }; 15 | -------------------------------------------------------------------------------- /net/socket-server.js: -------------------------------------------------------------------------------- 1 | const net = require('net'); 2 | 3 | const socket = net.createServer((c) => { 4 | console.log('Connected client:'); 5 | c.write('Hello'); 6 | 7 | c.on('end', () => { 8 | console.log('Disconected client'); 9 | }); 10 | 11 | c.on('data', (data) => { 12 | console.log('server received: ', String(data)); 13 | }); 14 | }); 15 | 16 | 17 | socket.listen(1337, () => { 18 | console.log('Server ready'); 19 | }); 20 | -------------------------------------------------------------------------------- /file-system/watcher/watcher.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const { EventEmitter } = require('events'); 3 | 4 | function watchFolder(folderName) { 5 | const eventEmiter = new EventEmitter(); 6 | 7 | const watcher = fs.watch(folderName, (event, filename) => { 8 | eventEmiter.emit('change', filename); 9 | }); 10 | 11 | eventEmiter.on('stop', () => { 12 | watcher.close(); 13 | }); 14 | 15 | return eventEmiter; 16 | } 17 | 18 | exports.watchFolder = watchFolder; 19 | -------------------------------------------------------------------------------- /scope-chains-closures/scope-chain.js: -------------------------------------------------------------------------------- 1 | /* 2 | # Your Mission 3 | 4 | Modify your solution from lesson 1 so foo, in addition to lexically scoped variable bar, 5 | contains a function zip 6 | which itself contains one variable lexically scoped called quux 7 | 8 | Once complete, execute @workshoppers/scope-chains-closures verify to verify your 9 | solution. 10 | */ 11 | 12 | function foo() { 13 | let bar; 14 | 15 | function zip() { 16 | let quux; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /streams/csv-stream.js: -------------------------------------------------------------------------------- 1 | const csv = require('csv'); 2 | const fs = require('fs'); 3 | const { Writable } = require('readable-stream'); 4 | 5 | const csvParser = csv.parse({ 6 | delimiter: ':', 7 | }); 8 | 9 | const write = new Writable({ 10 | objectMode: true, 11 | write(chunk, enc, cb) { 12 | console.log('chunk', chunk); 13 | cb(); 14 | }, 15 | }); 16 | 17 | fs.createReadStream(`${__dirname}/csv.csv`, { encoding: 'utf8' }) 18 | .pipe(csvParser) 19 | .pipe(write); 20 | -------------------------------------------------------------------------------- /debug/server.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | 3 | const app = express(); 4 | const debug = require('debug')('my-server'); 5 | 6 | const past = (age, gap) => `${gap} years ago you were ${Number(age) - gap}
`; 7 | const future = (age, gap) => `In ${gap} years you will be ${Number(age) + gap}
`; 8 | 9 | app.get('/:age/', (req, res) => { 10 | debug('calling my server'); 11 | res.send(past(req.params.age, 10) + future(req.params.age, 10)); 12 | }); 13 | 14 | app.listen(3000); 15 | -------------------------------------------------------------------------------- /child-process/spawn-as-exec.js: -------------------------------------------------------------------------------- 1 | const { spawn } = require('child_process'); 2 | 3 | const child = spawn('echo $NAME & find . -type f | wc -l', { 4 | stdio: 'inherit', 5 | shell: true, 6 | cwd: '../../', 7 | env: { NAME: 'Angel' }, 8 | detached: true, 9 | }); 10 | 11 | child.on('exit', (code, signal) => { 12 | console.log(`process child exited with ${code} and ·${signal}`); 13 | }); 14 | 15 | // playing with "detached" - main process can ends before child process 16 | child.unref(); 17 | -------------------------------------------------------------------------------- /scope-chains-closures/closure.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | # Your Mission 4 | 5 | Modify your solution from the previous lesson to set bar = true inside zip(), 6 | then return the function zip as the result of foo() 7 | 8 | Once complete, execute @workshoppers/scope-chains-closures verify to verify your 9 | solution. 10 | 11 | */ 12 | function foo() { 13 | let bar; 14 | quux = 'quux'; 15 | 16 | function zip() { 17 | const quux = 'other'; 18 | bar = true; 19 | } 20 | 21 | return zip; 22 | } 23 | -------------------------------------------------------------------------------- /child-process/long-run/long-run-in-child-fork.js/index.js: -------------------------------------------------------------------------------- 1 | const { fork } = require('child_process'); 2 | 3 | const interval = setInterval(() => { 4 | process.stdout.write('.'); 5 | }, 100); 6 | 7 | console.log('Init index'); 8 | 9 | const longRun = fork(`${__dirname}/long-run-fork.js`); 10 | 11 | longRun.on('message', (data) => { 12 | console.log('Long run eneded with', data); 13 | }); 14 | longRun.on('exit', () => { 15 | clearInterval(interval); 16 | }); 17 | 18 | longRun.send('[Test]'); 19 | 20 | console.log('End index'); 21 | -------------------------------------------------------------------------------- /error-handling/unhandled-promise-error.js: -------------------------------------------------------------------------------- 1 | // process.on('unhandledRejection', (err, promise) => { 2 | // console.log('Handled rejection promise:', err); 3 | // console.log('Handled rejection promise:', promise); 4 | // }); 5 | 6 | function callError() { 7 | // without process.on('unhandledRejection' error will be silent with a warning 8 | Promise.reject(new Error('Unhandled Error')); 9 | } 10 | 11 | setTimeout(() => { 12 | // without process.on('uncaughtException', this will nor work 13 | console.log('Error not break the flow'); 14 | }, 1000); 15 | 16 | callError(); 17 | -------------------------------------------------------------------------------- /streams/stream-duplex-echo.js: -------------------------------------------------------------------------------- 1 | const { Duplex } = require('stream'); 2 | 3 | const duplex = new Duplex({ 4 | read(/* size */) { 5 | if (this.charCode < 90) { 6 | this.push(String.fromCharCode(this.charCode)); 7 | this.charCode += 1; 8 | } else { 9 | this.push(null); 10 | } 11 | }, 12 | 13 | write(chunk, enconding, cb) { 14 | console.log('Chunk=> ', String(chunk)); 15 | setTimeout(cb, 500); 16 | }, 17 | }); 18 | 19 | 20 | duplex.charCode = 45; 21 | 22 | duplex.pipe(duplex); 23 | // To pipe input from console 24 | // process.stdin.pipe(duplex).pipe(process.stdout); 25 | -------------------------------------------------------------------------------- /error-handling/async-error-wrong.js: -------------------------------------------------------------------------------- 1 | process.on('uncaughtException', (err) => { 2 | console.log('Wrong Error handling!', err); 3 | }); 4 | 5 | const func = () => new Promise((/* resolve, reject */) => { 6 | setImmediate(() => { 7 | // this should be return reject(new Error('opss')); to be handled in catch block 8 | // it will be handled by process.on('uncaughtException',.. 9 | throw new Error('opss'); 10 | }); 11 | }); 12 | 13 | const main = async () => { 14 | try { 15 | await func(); 16 | } catch (ex) { 17 | console.log('Err will no executed: ', ex); 18 | } 19 | }; 20 | 21 | main(); 22 | -------------------------------------------------------------------------------- /scope-chains-closures/scopes.js: -------------------------------------------------------------------------------- 1 | /* 2 | # Your Mission 3 | 4 | In an empty file, create a function foo which contains one variable lexically 5 | scoped named bar. 6 | 7 | Once complete, execute @workshoppers/scope-chains-closures verify to verify your 8 | solution. 9 | 10 | ## Notes 11 | 12 | * [1]: There are also 4 other scopes in the language: Global, `with`, `catch`, 13 | * and `eval`. These tend not to be used much, so we will ignore them. 14 | * 15 | * 16 | * [2]: This workshop will concentrate only on Lexical Scoping. 17 | 18 | */ 19 | 20 | function foo() { 21 | let bar; 22 | } 23 | -------------------------------------------------------------------------------- /scope-chains-closures/global-scope-shadow.js: -------------------------------------------------------------------------------- 1 | /* 2 | # Your Mission 3 | 4 | Starting with your solution from the previous lesson, assign a value to the global variable 5 | quux inside foo() (don't use var or let). Create a shadow variable in of quux 6 | inside zip(). The value in the global variable quux has to be different than the 7 | value of quux inside zip(). 8 | 9 | Once complete, execute @workshoppers/scope-chains-closures verify to verify your 10 | solution. 11 | */ 12 | function foo() { 13 | let bar; 14 | quux = 'quux'; 15 | 16 | function zip() { 17 | const quux = 'other'; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /file-system/print-content/print-content.test.js: -------------------------------------------------------------------------------- 1 | const { print, printNextTick } = require('./print-content'); 2 | 3 | describe('print-content', () => { 4 | let interval; 5 | beforeAll(() => { 6 | interval = setInterval(() => { console.log('---'); }, 1); 7 | }); 8 | afterAll(() => { 9 | clearInterval(interval); 10 | }); 11 | 12 | it('print', async () => { 13 | await print(`${__dirname}/file1.txt`, 14 | `${__dirname}/file2.txt`, 15 | `${__dirname}/file3.txt`); 16 | }); 17 | it('printNextTick', async () => { 18 | await printNextTick(`${__dirname}/file1.txt`, 19 | `${__dirname}/file2.txt`, 20 | `${__dirname}/file3.txt`); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /control-flow/async-secuencial-for-loop.js: -------------------------------------------------------------------------------- 1 | /* 2 | we need to do five actions, and each of those actions needs data from 3 | the previous one, so we have to run one after another. 4 | */ 5 | 6 | function doAsyncTask(numericData, cb) { 7 | console.log(`to run doAsyncTask for ${numericData} - takes 1sec`); 8 | setTimeout(() => cb(numericData * 2), 1000); 9 | } 10 | const actions = [1, 2, 3, 4, 5]; 11 | const results = []; 12 | 13 | function run(element) { 14 | if (element) { 15 | doAsyncTask(element, (result) => { 16 | results.push(result); 17 | run(actions.shift()); 18 | }); 19 | } else { 20 | console.log('End: ', results); 21 | } 22 | } 23 | 24 | run(actions.shift()); 25 | -------------------------------------------------------------------------------- /file-system/print-content/print-content-stream.test.js: -------------------------------------------------------------------------------- 1 | const { print, printNextTick } = require('./print-content-stream'); 2 | 3 | describe('print-content', () => { 4 | let interval; 5 | beforeAll(() => { 6 | interval = setInterval(() => { console.log('---'); }, 1); 7 | }); 8 | afterAll(() => { 9 | clearInterval(interval); 10 | }); 11 | 12 | it('print', async () => { 13 | await print(`${__dirname}/file1.txt`, 14 | `${__dirname}/file2.txt`, 15 | `${__dirname}/file3.txt`); 16 | }); 17 | it('printNextTick', async () => { 18 | await printNextTick(`${__dirname}/file1.txt`, 19 | `${__dirname}/file2.txt`, 20 | `${__dirname}/file3.txt`); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /control-flow/async-parallel-for-loop.js: -------------------------------------------------------------------------------- 1 | /* 2 | want to take a small set of operations, launch them all in parallel 3 | and then do something when all of them are complete 4 | */ 5 | 6 | function doAsyncTask(numericData, cb) { 7 | console.log(`to run doAsyncTask for ${numericData} - takes 1sec`); 8 | // run in random time 9 | setTimeout(() => cb(numericData * 2), Math.ceil(Math.random() * 1000)); 10 | } 11 | const actions = [1, 2, 3, 4, 5]; 12 | const results = []; 13 | 14 | actions.forEach((item) => { 15 | doAsyncTask(item, (result) => { 16 | results.push(result); 17 | 18 | if (results.length === actions.length) { 19 | console.log('End not ordered result: ', results); 20 | } 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Resources 2 | * https://www.nodecertification.com/ : it has links to resources for each topic. Some reouces are under registration process and others are quite old. It it also downloaded in the [study-guide-resource](./study-guide-resource) folder. 3 | * Article about buffers: https://www.digitalocean.com/community/tutorials/using-buffers-in-node-js 4 | * [JavaScript for impatient programmers](https://exploringjs.com/impatient-js/toc.html) : book where read about some topics. 5 | * https://www.nearform.com/blog/node-js-developer-certification-what-to-expect/ : nearform blog entry about the node certification process. 6 | * https://dev.to/kryz/node-js-certification-first-impressions-21a1 : impressions for the certification process. -------------------------------------------------------------------------------- /child-process/long-run/long-run-in-exec-file/index.js: -------------------------------------------------------------------------------- 1 | const { execFile } = require('child_process'); 2 | 3 | const interval = setInterval(() => { 4 | process.stdout.write('.'); 5 | }, 100); 6 | 7 | console.log('Init index'); 8 | 9 | const longRun = execFile('node', ['long-run-exec-file.js'], { 10 | cwd: __dirname, 11 | env: { PATH: process.env.PATH, NAME: 'Run_in_exec_file' }, 12 | }, (err, data) => { 13 | if (err) { 14 | throw err; 15 | } 16 | console.log('-- Total data writen in sub-task:', data); 17 | clearInterval(interval); 18 | }); 19 | 20 | longRun.stdout.on('data', (data) => { 21 | console.log('\nData from long task:', data); 22 | }); 23 | 24 | longRun.stdin.write('[Test]'); 25 | 26 | console.log('End index'); 27 | -------------------------------------------------------------------------------- /process-operating/index.js: -------------------------------------------------------------------------------- 1 | const os = require('os'); 2 | 3 | process.on('exit', (code) => { 4 | console.log(`\n--- Exiting with code ${code}...`); 5 | }); 6 | 7 | console.log('--- Starting...\n'); 8 | console.log(`Running from ${process.cwd()} with user home dir ${os.homedir}`); 9 | console.log('Env vars:', process.env); 10 | console.log(`Running in "${os.arch()}" Arq. for plartform "${os.platform()}" of type "${os.type()}" with ${os.cpus().length} cpus`); 11 | console.log(`System has ${os.freemem()} bytes free memory of ${os.totalmem()} bytes`); 12 | console.log(`Hostname: ${os.hostname()}`); 13 | console.log('Running user %j', os.userInfo()); 14 | console.log(`Machine running for ${os.uptime()} seconds`); 15 | 16 | process.exit(208); 17 | -------------------------------------------------------------------------------- /error-handling/unhandled-promise-error-test.test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const sinon = require('sinon'); 3 | 4 | const obj = { 5 | fn: () => {}, 6 | }; 7 | 8 | beforeAll(() => { 9 | sinon.stub(obj, 'fn').returns(Promise.reject(new Error('test'))); 10 | }); 11 | 12 | it('works', () => obj.fn().catch((err) => { 13 | assert.equal(err.message, 'test'); 14 | })); 15 | 16 | process.on('unhandledRejection', (error, promise) => { 17 | // Will print "unhandledRejection err is not defined" 18 | console.log('unhandledRejection', error.message); 19 | console.log('unhandledRejection', promise); 20 | }); 21 | 22 | it('wait', () => new Promise((resolve) => { 23 | setTimeout(() => { 24 | resolve(); 25 | }, 3000); 26 | })); 27 | -------------------------------------------------------------------------------- /child-process/spawn.js: -------------------------------------------------------------------------------- 1 | const { spawn } = require('child_process'); 2 | 3 | const childFind = spawn('find', ['.', '-type', 'f']); 4 | const childWc = spawn('wc', ['-l']); 5 | 6 | childFind.on('exit', (code, signal) => { 7 | console.log(`process childFind exited with ${code} and ·${signal}`); 8 | }); 9 | 10 | 11 | childFind.stdout.on('data', (data) => { 12 | console.log(`process childFind writes ${data}`); 13 | }); 14 | 15 | childWc.on('exit', (code, signal) => { 16 | console.log(`process childWc exited with ${code} and ·${signal}`); 17 | }); 18 | 19 | childWc.stdout.on('data', (data) => { 20 | console.log(`process childWc writes ${data}`); 21 | }); 22 | 23 | childFind.stdout.pipe(childWc.stdin); 24 | childWc.stdout.pipe(process.stdout); 25 | -------------------------------------------------------------------------------- /control-flow/async-parallel-for-loop-waranty-order.js: -------------------------------------------------------------------------------- 1 | /* 2 | want to take a small set of operations, launch them all in parallel 3 | and then do something when all of them are complete 4 | */ 5 | 6 | function doAsyncTask(numericData, cb) { 7 | console.log(`to run doAsyncTask for ${numericData} - takes 1sec`); 8 | // run in random time 9 | setTimeout(() => cb(numericData * 2), Math.ceil(Math.random() * 1000)); 10 | } 11 | const actions = [1, 2, 3, 4, 5]; 12 | const results = []; 13 | let filled = actions.length; 14 | 15 | actions.forEach((item, pos) => { 16 | doAsyncTask(item, (result) => { 17 | results[pos] = result; 18 | filled -= 1; 19 | 20 | if (!filled) { 21 | console.log('End ordered result: ', results); 22 | } 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /stream-adventure/meet_pipe.js: -------------------------------------------------------------------------------- 1 | /* 2 | You will get a file as the first argument to your program (process.argv[2]). 3 | 4 | Use `fs.createReadStream()` to pipe the given file to `process.stdout`. 5 | 6 | `fs.createReadStream()` takes a file as an argument and returns a readable 7 | stream that you can call `.pipe()` on. Here's a readable stream that pipes its 8 | data to `process.stderr`: 9 | 10 | var fs = require('fs'); 11 | fs.createReadStream('data.txt').pipe(process.stderr); 12 | 13 | Your program is basically the same idea, but instead of `'data.txt'`, the 14 | filename comes from `process.argv[2]` and you should pipe to stdout, not stderr. 15 | */ 16 | 17 | const fs = require('fs'); 18 | 19 | const file = process.argv[2]; 20 | 21 | fs.createReadStream(file) 22 | .pipe(process.stdout); 23 | -------------------------------------------------------------------------------- /stream-adventure/crypt.js: -------------------------------------------------------------------------------- 1 | /* 2 | Your program will be given a passphrase on `process.argv[2]` and 'aes256' 3 | encrypted data will be written to stdin. 4 | 5 | Simply decrypt the data and stream the result to process.stdout. 6 | 7 | You can use the `crypto.createDecipher()` api from node core to solve this 8 | challenge. Here's an example: 9 | 10 | var crypto = require('crypto'); 11 | var stream = crypto.createDecipher('RC4', 'robots'); 12 | stream.pipe(process.stdout); 13 | stream.write(Buffer([ 135, 197, 164, 92, 129, 90, 215, 63, 92 ])); 14 | stream.end(); 15 | 16 | Instead of calling `.write()` yourself, just pipe stdin into your decrypter. 17 | */ 18 | const crypt = require('crypto'); 19 | 20 | process.stdin 21 | .pipe(crypt.createDecipher('aes256', process.argv[2])) 22 | .pipe(process.stdout); 23 | -------------------------------------------------------------------------------- /net/socket-client.js: -------------------------------------------------------------------------------- 1 | const net = require('net'); 2 | 3 | let client; 4 | 5 | function tryConnection() { 6 | try { 7 | client = net.createConnection({ port: 1337 }, () => { 8 | client.write('Hello from client'); 9 | }); 10 | 11 | client.on('error', (e) => { 12 | console.log('err:', e); 13 | setTimeout(() => { 14 | tryConnection(); 15 | }, 1000); 16 | }); 17 | 18 | client.on('data', (data) => { 19 | console.log('Data from server => ', String(data)); 20 | }); 21 | 22 | client.on('end', () => { 23 | console.log('Client disconnected from server'); 24 | tryConnection(); 25 | }); 26 | } catch (ee) { 27 | console.log('exception: ', ee); 28 | setTimeout(() => { 29 | tryConnection(); 30 | }, 1000); 31 | } 32 | } 33 | 34 | tryConnection(); 35 | -------------------------------------------------------------------------------- /error-handling/unhandled-error-event.js: -------------------------------------------------------------------------------- 1 | const { EventEmitter } = require('events'); 2 | 3 | const myEventEmiter = new EventEmitter(); 4 | 5 | process.on('uncaughtException', (err) => { 6 | console.log('uncaughtException - Catch event error:', err); 7 | }); 8 | 9 | // need process.on('uncaughtException', ... if not ".on('error'" in emiter 10 | // myEventEmiter.on('error', (err) => { 11 | // console.log('myEventEmiter - Catch event error:', err); 12 | // }); 13 | 14 | setTimeout(() => { 15 | /* 16 | If no: 17 | * process.on('uncaughtException', (err) => {}) 18 | or 19 | * myEventEmiter.on('error', (err) => {} 20 | this will not work 21 | */ 22 | console.log('Final message'); 23 | }, 1000); 24 | 25 | // if next line goes before "setTimeout", that is not going to be executed 26 | myEventEmiter.emit('error', new Error('unhandled error event')); 27 | -------------------------------------------------------------------------------- /module-system/parent.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | // export one element 4 | exports.name = 'Process.js'; 5 | // if module.exports = ... later, "name" will not exist quen require this module 6 | 7 | const children = require('./children-module'); 8 | 9 | assert(children.doNothing); 10 | assert(!children.notExists); 11 | 12 | // not need for json extension 13 | const { prop1, prop2 } = require('./data'); 14 | 15 | assert(prop1); 16 | assert(prop2); 17 | 18 | const childrenExportsNothing = require('./children-exports-nothing'); 19 | 20 | assert(!childrenExportsNothing.notExists); 21 | 22 | const functions = Object.keys(process).filter((key) => typeof process[key] === 'function'); 23 | 24 | functions.forEach((key) => { 25 | console.log(key, ' => '); 26 | }); 27 | 28 | exports.version = '1.0'; 29 | console.log('module', module); 30 | 31 | children.assertParent(); 32 | -------------------------------------------------------------------------------- /buffers/sized-buffer.js: -------------------------------------------------------------------------------- 1 | // create an buffer with a delimtter size. Fille with 0 by defautl 2 | const buffer = Buffer.alloc(128); 3 | 4 | function print() { 5 | console.log(buffer); 6 | console.log(buffer.length); 7 | console.log(String(buffer)); 8 | } 9 | 10 | function printContent() { 11 | // print all 12 | // buffer.forEach((value, pos) => { 13 | // console.log(`${pos}: ${String.fromCharCode(value)}`); 14 | // }); 15 | 16 | // print filled values (not nullish) 17 | buffer 18 | .filter((value) => !!value) 19 | .forEach((value, pos) => { 20 | console.log(`${pos}: "${String.fromCharCode(value)}"`); 21 | }); 22 | } 23 | 24 | print(); 25 | printContent(); 26 | 27 | buffer.write('hello'); 28 | buffer.write(' there', 5); 29 | print(); 30 | printContent(); 31 | 32 | // fille the buffer size with... 33 | buffer.fill('1'); 34 | print(); 35 | printContent(); 36 | -------------------------------------------------------------------------------- /events/server-request-wit-events/date-event-handler-without-class.js: -------------------------------------------------------------------------------- 1 | const { EventEmitter } = require('events'); 2 | 3 | const CALCULATE_DATE = 'calculateDate'; 4 | const eventEmitter = new EventEmitter(); 5 | 6 | function calculateDate(data) { 7 | setTimeout(() => { 8 | if (Math.floor(Math.random() * (2 - 1 + 1)) % 2 === 0) { 9 | eventEmitter.emit(data.errorEventName, new Error('Random error')); 10 | } else { 11 | eventEmitter.emit(data.eventName, { id: data.id, date: new Date() }); 12 | } 13 | }, 1000); 14 | } 15 | 16 | function getEvents(id) { 17 | const eventName = `${id}`; 18 | return { 19 | eventName, 20 | errorEventName: `${eventName}_error`, 21 | }; 22 | } 23 | 24 | eventEmitter.on(CALCULATE_DATE, calculateDate); 25 | 26 | module.exports = { 27 | eventEmitter, 28 | getEvents, 29 | events: { 30 | CALCULATE_DATE, 31 | }, 32 | }; 33 | -------------------------------------------------------------------------------- /events/server-request-wit-events/index-without-class.js: -------------------------------------------------------------------------------- 1 | const http = require('http'); 2 | const { events, eventEmitter, getEvents } = require('./date-event-handler-without-class'); 3 | 4 | // declare server variables 5 | const hostname = '127.0.0.1'; 6 | const port = 8080; 7 | 8 | const server = http.createServer((req, res) => { 9 | res.statusCode = 200; 10 | res.setHeader('Content-Type', 'text/plain'); 11 | 12 | const id = String(Date.now()); 13 | const { eventName, errorEventName } = getEvents(id); 14 | 15 | eventEmitter.emit(events.CALCULATE_DATE, { eventName, errorEventName, id }); 16 | 17 | eventEmitter.once(eventName, (data) => { 18 | res.end(JSON.stringify(data)); 19 | }); 20 | 21 | eventEmitter.once(errorEventName, (err) => { 22 | res.end(`Error! ${err}`); 23 | }); 24 | }); 25 | 26 | server.listen(port, hostname, () => { 27 | console.log(`Server running at http://${hostname}:${port}/`); 28 | }); 29 | -------------------------------------------------------------------------------- /streams/zip-decrypt.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const zlib = require('zlib'); 3 | const crypto = require('crypto'); 4 | const { Transform, Readable } = require('readable-stream'); 5 | 6 | 7 | // class MyTransform extends Transform { 8 | // _transform (chunk, enc, cb) { 9 | // // console.log('.'); 10 | // process.stdout.write('.'); 11 | // cb(null, chunk); } 12 | // } 13 | // const processStream = new MyTransform(); 14 | 15 | 16 | const processStream = Transform({ 17 | transform: (chunk, encoding, callback) => { 18 | // console.log('.'); 19 | process.stdout.write('.'); 20 | callback(null, chunk); 21 | }, 22 | }); 23 | const a = 'a'; 24 | 25 | 26 | fs.createReadStream('./zipped-encripted.gz') 27 | .pipe(crypto.createDecipher('aes192', 'passsword')) 28 | .pipe(processStream) 29 | .pipe(zlib.createUnzip()) 30 | .pipe(fs.createWriteStream('./unzipped-encripted.txt')) 31 | .on('end', () => console.log('Done')); 32 | -------------------------------------------------------------------------------- /events/server-request-wit-events/index-with-class.js: -------------------------------------------------------------------------------- 1 | const http = require('http'); 2 | const dateEventHandler = require('./date-event-handler-with-class'); 3 | 4 | // declare server variables 5 | const hostname = '127.0.0.1'; 6 | const port = 8080; 7 | 8 | const server = http.createServer((req, res) => { 9 | res.statusCode = 200; 10 | res.setHeader('Content-Type', 'text/plain'); 11 | 12 | const id = String(Date.now()); 13 | 14 | const { eventName, errorEventName } = dateEventHandler.getEvents(id); 15 | 16 | dateEventHandler.emit(dateEventHandler.events.CALCULATE_DATE, { id, eventName, errorEventName }); 17 | 18 | dateEventHandler.once(eventName, (data) => { 19 | res.end(JSON.stringify(data)); 20 | }); 21 | 22 | dateEventHandler.once(errorEventName, (err) => { 23 | res.end(`Error! ${err}`); 24 | }); 25 | }); 26 | 27 | server.listen(port, hostname, () => { 28 | console.log(`Server running at http://${hostname}:${port}/`); 29 | }); 30 | -------------------------------------------------------------------------------- /file-system/concat/concat-files.test.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const { expect } = require('chai'); 3 | const { concatFiles } = require('./concat-files'); 4 | 5 | describe('concat-files', () => { 6 | const content1 = 'File1.text content\n'; 7 | const content2 = 'File2.text content\n'; 8 | 9 | beforeAll(async () => { 10 | await fs.promises.writeFile('./file1.txt', content1); 11 | await fs.promises.writeFile('./file2.txt', content2); 12 | 13 | await concatFiles(['./file1.txt', './file2.txt'], './dest.txt'); 14 | }); 15 | afterAll(async () => { 16 | await fs.promises.unlink('./file1.txt'); 17 | await fs.promises.unlink('./file2.txt'); 18 | await fs.promises.unlink('./dest.txt'); 19 | }); 20 | 21 | it('should concat two files content', async () => { 22 | const content = await fs.promises.readFile('./dest.txt'); 23 | 24 | expect(content.includes(content1)).to.eql(true); 25 | expect(content.includes(content2)).to.eql(true); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /streams/stream-codify-text.js: -------------------------------------------------------------------------------- 1 | const { Transform } = require('stream'); 2 | 3 | const MAP = { 4 | i: '1', 5 | I: '1_', 6 | e: '€', 7 | E: '€_', 8 | o: '0', 9 | O: '0_', 10 | A: '4', 11 | b: '6', 12 | B: '8', 13 | l: '|', 14 | L: '|_', 15 | g: '9', 16 | s: '5', 17 | S: '5_', 18 | c: '[', 19 | C: '[_', 20 | z: '2', 21 | Z: '2_', 22 | }; 23 | 24 | const encoder = new Transform({ 25 | transform(chunk, encoding, cb) { 26 | const result = String(chunk).split('').map((letter) => MAP[letter] || letter).join(''); 27 | this.push(result); 28 | cb(); 29 | }, 30 | }); 31 | 32 | // # Piping 33 | process.stdin 34 | .pipe(encoder) 35 | .pipe(process.stdout); 36 | 37 | 38 | // # Events 39 | // process.stdin.on('data', (data) => { 40 | // encoder.push(data); 41 | // }); 42 | 43 | // encoder.on('data', (data) => { 44 | // const result = String(data).split('').map((letter) => MAP[letter] || letter).join(''); 45 | // process.stdout.write(result); 46 | // }); 47 | -------------------------------------------------------------------------------- /stream-adventure/websockets.js: -------------------------------------------------------------------------------- 1 | /* 2 | In this adventure, write some browser code that uses the websocket-stream module 3 | to print the string "hello\n". 4 | 5 | Your solution file will be compiled with browserify and the verify script will 6 | prompt you to open `http://localhost:8099` in a browser to verify your solution. 7 | 8 | To open a stream with websocket-stream on localhost:8099, just write: 9 | 10 | var ws = require('websocket-stream'); 11 | var stream = ws('ws://localhost:8099'); 12 | 13 | Then write the string "hello\n" to the stream. 14 | 15 | The readme for websocket-stream has more info if you're curious about how to 16 | write the server side code: https://github.com/maxogden/websocket-stream 17 | 18 | Make sure to `npm install websocket-stream` in the directory where your solution 19 | file lives. 20 | */ 21 | 22 | const websocketStream = require('websocket-stream'); 23 | 24 | const wsStream = websocketStream('ws://localhost:8099'); 25 | 26 | 27 | wsStream.write('hello\n'); 28 | -------------------------------------------------------------------------------- /prototype/prototype-data.test.js: -------------------------------------------------------------------------------- 1 | const { expect } = require('chai'); 2 | const { Aritmetic } = require('./prototype-data'); 3 | 4 | describe('prototype-data', () => { 5 | describe('calling new should create an object', () => { 6 | const aritmetic = new Aritmetic(); 7 | 8 | it('with own "sum" method', () => { 9 | const isOwnProperty = Object.prototype.hasOwnProperty.call(aritmetic, 'sum'); 10 | expect(isOwnProperty).to.eql(true); 11 | expect(aritmetic.sum).to.be.a('function'); 12 | }); 13 | 14 | it('with from parent "subtract" method', () => { 15 | const isOwnProperty = Object.prototype.hasOwnProperty.call(aritmetic, 'subtract'); 16 | expect(isOwnProperty).to.eql(false); 17 | expect(aritmetic.subtract).to.be.a('function'); 18 | }); 19 | 20 | it('with not own "plus" method', () => { 21 | const isOwnProperty = Object.prototype.hasOwnProperty.call(aritmetic, 'plus'); 22 | expect(isOwnProperty).to.eql(false); 23 | expect(aritmetic.plus).to.be.a('function'); 24 | }); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /streams/zip-encrypt.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const zlib = require('zlib'); 3 | const crypto = require('crypto'); 4 | const { Transform, Readable } = require('readable-stream'); 5 | 6 | const processStream = new Transform({ 7 | transform(chunk, encoding, callback) { 8 | // console.log('.'); 9 | process.stdout.write('.'); 10 | callback(null, chunk); 11 | }, 12 | }); 13 | 14 | const message = ` 15 | Hello There! 16 | How are you? 17 | 18 | See you next time! 19 | `; 20 | new Readable({ 21 | read(size) { 22 | this.index = this.index || 0; 23 | // const top = size || 10; 24 | const data = message.substring(this.index, this.index + 10) || null; 25 | console.log(`Read from ${this.index} to ${this.index + 10} - ${data}`); 26 | this.push(data); 27 | this.index += 10; 28 | }, 29 | }) 30 | // fs.createReadStream(__filename) 31 | .pipe(zlib.createGzip()) 32 | .pipe(processStream) 33 | .pipe(crypto.createCipher('aes192', 'passsword')) 34 | .pipe(fs.createWriteStream('./zipped-encripted.gz')) 35 | .on('end', () => console.log('Done')); 36 | -------------------------------------------------------------------------------- /events/server-request-wit-events/date-event-handler-with-class.js: -------------------------------------------------------------------------------- 1 | const { EventEmitter } = require('events'); 2 | 3 | const CALCULATE_DATE = 'calculateDate'; 4 | 5 | class DateEventHandler extends EventEmitter { 6 | constructor() { 7 | super(); 8 | 9 | this.preffix = String(Date.now); 10 | this.events = { 11 | CALCULATE_DATE, 12 | }; 13 | } 14 | 15 | calculateDate(data) { 16 | setTimeout(() => { 17 | if (Math.floor(Math.random() * (2 - 1 + 1)) % 2 === 0) { 18 | this.emit(data.errorEventName, new Error('Random error')); 19 | } else { 20 | this.emit(data.eventName, { id: data.id, date: new Date() }); 21 | } 22 | }, 1000); 23 | } 24 | 25 | getEvents(id) { 26 | const eventName = `${this.preffix}_${id}`; 27 | return { 28 | eventName, 29 | errorEventName: `${eventName}_error`, 30 | }; 31 | } 32 | } 33 | 34 | const dateEventHandler = new DateEventHandler(); 35 | 36 | dateEventHandler.on(CALCULATE_DATE, (id) => { 37 | dateEventHandler.calculateDate(id); 38 | }); 39 | 40 | module.exports = dateEventHandler; 41 | -------------------------------------------------------------------------------- /streams/readable-to-output.js: -------------------------------------------------------------------------------- 1 | const { Readable } = require('readable-stream'); 2 | 3 | const inStream = new Readable({ 4 | read(size) { 5 | console.log('\nread method:', size); 6 | this.currentCharCode += 1; 7 | this.push(String.fromCharCode(this.currentCharCode)); 8 | 9 | if (this.currentCharCode > 90) { 10 | this.push(null); 11 | } 12 | }, 13 | }); 14 | 15 | inStream.currentCharCode = 65; 16 | 17 | // choose "readable" or "data" event 18 | // inStream.on('readable', function onReadable() { 19 | // let data = this.read(); 20 | // while (data) { 21 | // console.log('---------------------------------'); 22 | // console.log(String(data)); 23 | // console.log('---------------------------------'); 24 | // data = this.read(); 25 | // } 26 | // }); 27 | 28 | inStream.on('data', (chunk) => { 29 | console.log('---------------------------------'); 30 | console.log(String(chunk)); 31 | console.log('---------------------------------'); 32 | }); 33 | 34 | 35 | inStream.on('end', () => { 36 | console.log('Stream Closed...'); 37 | }); 38 | 39 | // inStream.pipe(process.stdout); 40 | -------------------------------------------------------------------------------- /debug/console.js: -------------------------------------------------------------------------------- 1 | console.profile('MyLabel'); 2 | 3 | console.assert(true, 'nothing'); 4 | console.assert(false, ' ups false'); 5 | 6 | console.count(); 7 | console.count('default'); 8 | console.count('abc'); 9 | console.count('xyz'); 10 | console.count('abc'); 11 | console.count(); 12 | console.countReset('abc'); 13 | console.count('abc'); 14 | 15 | console.dir({ data: 'data' }); 16 | 17 | console.group('test'); 18 | console.log('1 test'); 19 | console.log('2 test'); 20 | console.log('3 test'); 21 | console.groupEnd(); 22 | console.log('4 test'); 23 | 24 | 25 | console.time('process'); 26 | for (let i = 1; i < 1000000; i += 1) { /* do nothing */ } 27 | console.timeLog('process', 'End 1º loop'); 28 | for (let i = 1; i < 1000000; i += 1) { /* do nothing */ } 29 | console.timeEnd('process'); 30 | 31 | 32 | console.trace('Mesage!'); 33 | 34 | /* 35 | This method does not display anything unless used in the inspector, 36 | adds an event with the label 'label' to the Timeline panel of the inspector. 37 | */ 38 | console.timeStamp('timestamp'); 39 | 40 | 41 | // Adds the profile 'MyLabel' to the Profiles panel of the inspector. 42 | console.profileEnd('MyLabel'); 43 | -------------------------------------------------------------------------------- /module-system/children-module.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | // this will no exists when the module will be required 4 | exports.notExists = 'notExists'; 5 | 6 | const { name, version } = require('./parent'); 7 | 8 | // export all in one places 9 | module.exports = { 10 | doNothing: function doNothing() { /* do nothing */ }, 11 | assertParent: function assertParent() { 12 | // eslint-disable-next-line global-require 13 | const { version: versionExported } = require('./parent'); 14 | // at this point version is exported in process.js 15 | assert(versionExported); 16 | console.log('Now "version" from parent exists!'); 17 | }, 18 | }; 19 | 20 | 21 | // at this point name is already exported in process.js 22 | assert(name); 23 | console.log('Now "name" from parent exists!'); 24 | 25 | // at this point version is not already exported in process.js 26 | assert(!version); 27 | console.log('Now "version" from parent does not exist!'); 28 | 29 | function print() { 30 | console.log('\n--- children --- '); 31 | console.log('module', module); 32 | console.log('--- --- ---\n'); 33 | } 34 | 35 | print(); 36 | 37 | setImmediate(print); 38 | -------------------------------------------------------------------------------- /file-system/print-content/print-content.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | 3 | function executeNext(file) { 4 | return fs.promises.readFile(file) 5 | .then((data) => data.toString('utf8')); 6 | } 7 | 8 | /* 9 | Receive 3 files and print its content ordered. 10 | All filereaders shoul be initializated at the same time. 11 | Printing should be in order 12 | */ 13 | function printNextTick(file1, file2, file3) { 14 | return new Promise((resolve) => { 15 | process.nextTick(() => Promise.all([ 16 | executeNext(file1), 17 | executeNext(file2), 18 | executeNext(file3), 19 | ]) 20 | .then(([data1, data2, data3]) => { 21 | console.log(data1); 22 | console.log(data2); 23 | console.log(data3); 24 | resolve(); 25 | })); 26 | }); 27 | } 28 | 29 | function print(file1, file2, file3) { 30 | return Promise.all([ 31 | executeNext(file1), 32 | executeNext(file2), 33 | executeNext(file3), 34 | ]) 35 | .then(([data1, data2, data3]) => { 36 | console.log(data1); 37 | console.log(data2); 38 | console.log(data3); 39 | }); 40 | } 41 | 42 | module.exports = { printNextTick, print }; 43 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-certification", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test:unit": "LOG=1 jest", 8 | "test": "jest ." 9 | }, 10 | "author": "acereijo ", 11 | "license": "ISC", 12 | "dependencies": { 13 | "@workshoppers/scope-chains-closures": "^1.0.5", 14 | "async": "^3.2.0", 15 | "async-you": "^2.0.0", 16 | "chai": "^4.2.0", 17 | "concat-files": "^0.1.1", 18 | "concat-stream": "^2.0.0", 19 | "csv": "^5.3.2", 20 | "csv-parser": "^2.3.2", 21 | "csv2": "^0.1.1", 22 | "debug": "^4.1.1", 23 | "duplexer2": "^0.1.4", 24 | "eslint-config-airbnb-base": "^14.1.0", 25 | "eslint-plugin-import": "^2.20.2", 26 | "express": "^4.17.1", 27 | "jest": "^25.5.4", 28 | "readable-stream": "^3.6.0", 29 | "sinon": "^9.0.2", 30 | "sinon-chai": "^3.5.0", 31 | "split": "^1.0.1", 32 | "stream-adventure": "^4.1.1", 33 | "stream-combiner": "^0.2.2", 34 | "tar": "^6.0.2", 35 | "through": "^2.3.8", 36 | "through2": "^3.0.1", 37 | "trumpet": "^1.7.2", 38 | "websocket-stream": "^5.5.2" 39 | }, 40 | "devDependencies": { 41 | "eslint": "^6.8.0" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /stream-adventure/http_client.js: -------------------------------------------------------------------------------- 1 | /* 2 | Send an HTTP POST request to http://localhost:8099 and pipe process.stdin into 3 | it. Pipe the response stream to process.stdout. 4 | 5 | Here's an example of how to use the `request` module to send a GET request, 6 | piping the result to stdout: 7 | 8 | var request = require('request'); 9 | request('http://beep.boop:80/').pipe(process.stdout); 10 | 11 | To make a POST request, just call `request.post()` instead of `request()`: 12 | 13 | var request = require('request'); 14 | var r = request.post('http://beep.boop:80/'); 15 | 16 | The `r` object that you get back from `request.post()` is a readable+writable 17 | stream so you can pipe a readable stream into it (`src.pipe(r)`) and you can 18 | pipe it to a writable stream (`r.pipe(dst)`). 19 | 20 | You can even chain both steps together: src.pipe(r).pipe(dst); 21 | 22 | Hint: for your code, src will be process.stdin and dst will be process.stdout. 23 | 24 | Make sure to do the following before verifying your solution: 25 | - `npm install request` in the directory where your solution file lives. 26 | - free port 8099 for the testing server by temporarily closing 27 | other applications that use it, i.e. installing a module like node-portfinder. 28 | */ 29 | const request = require('request'); 30 | 31 | const postRequest = request.post('http://localhost:8099'); 32 | process.stdin 33 | .pipe(postRequest) 34 | .pipe(process.stdout); 35 | -------------------------------------------------------------------------------- /events/event-evemitter/event-emitter-class.js: -------------------------------------------------------------------------------- 1 | class EventEmitter { 2 | constructor() { 3 | this.listeners = {}; 4 | } 5 | 6 | addListener(eventName, fn) { 7 | if (!this.listeners[eventName]) { 8 | this.listeners[eventName] = []; 9 | } 10 | this.listeners[eventName].push(fn); 11 | 12 | return this; 13 | } 14 | 15 | on(eventName, fn) { 16 | return this.addListener(eventName, fn); 17 | } 18 | 19 | removeListener(eventName, fn) { 20 | const pos = (this.listeners[eventName] || []).indexOf(fn); 21 | 22 | if (pos >= 0) { 23 | this.listeners[eventName].splice(pos, 1); 24 | } 25 | 26 | return this; 27 | } 28 | 29 | off(eventName, fn) { 30 | return this.removeListener(eventName, fn); 31 | } 32 | 33 | once(eventName, fn) { 34 | const wrap = (...args) => { 35 | fn(...args); 36 | this.off(eventName, wrap); 37 | }; 38 | 39 | return this.on(eventName, wrap); 40 | } 41 | 42 | emit(eventName, ...args) { 43 | const listeners = (this.listeners[eventName] || []); 44 | 45 | if (listeners.length === 0) { 46 | return false; 47 | } 48 | 49 | listeners.forEach((fn) => { 50 | fn(...args); 51 | }); 52 | 53 | return true; 54 | } 55 | 56 | listenerCount(eventName) { 57 | return (this.listeners[eventName] || []).length; 58 | } 59 | 60 | rawListeners(eventName) { 61 | return this.listeners[eventName]; 62 | } 63 | } 64 | 65 | module.exports = EventEmitter; 66 | -------------------------------------------------------------------------------- /stream-adventure/concat.js: -------------------------------------------------------------------------------- 1 | /* 2 | Create a new file called concat.js. 3 | 4 | You will be given text on process.stdin. Buffer the text and reverse it using 5 | the `concat-stream` module before writing it to stdout. 6 | 7 | `concat-stream` is a write stream that you can pass a callback to get the 8 | complete contents of a stream as a single buffer. Here's an example that uses 9 | concat to buffer POST content in order to JSON.parse() the submitted data: 10 | 11 | var concat = require('concat-stream'); 12 | var http = require('http'); 13 | 14 | var server = http.createServer(function (req, res) { 15 | if (req.method === 'POST') { 16 | req.pipe(concat(function (body) { 17 | var obj = JSON.parse(body); 18 | res.end(Object.keys(obj).join('\n')); 19 | })); 20 | } 21 | else res.end(); 22 | }); 23 | server.listen(5000); 24 | 25 | In your adventure you'll only need to buffer input with `concat()` from 26 | process.stdin. 27 | 28 | Make sure to `npm install concat-stream` in the directory where your solution 29 | file is located. 30 | 31 | To verify your solution run: 32 | stream-adventure verify concat.js 33 | */ 34 | const concat = require('concat-stream'); 35 | 36 | function reverseText(buffer) { 37 | const resevedData = new String(buffer) 38 | .split('') 39 | .reverse() 40 | .join(''); 41 | 42 | console.log(resevedData); 43 | } 44 | process.stdin 45 | .pipe(concat(reverseText)); 46 | -------------------------------------------------------------------------------- /control-flow/async-parallel-for-loop-limit-concurency.js: -------------------------------------------------------------------------------- 1 | /* 2 | want to perform some operations in parallel, but keep the number 3 | of running I/O operations under a set limit 4 | */ 5 | 6 | function doAsyncTask(numericData, cb) { 7 | console.log(`to run doAsyncTask for ${numericData} - takes 1sec`); 8 | // run in random time 9 | setTimeout(() => cb(numericData * 2), Math.ceil(Math.random() * 1000)); 10 | } 11 | const actions = [1, 2, 3, 4, 5]; 12 | const results = []; 13 | const limit = 2; 14 | let running = 0; 15 | 16 | 17 | function run(element) { 18 | if (element) { 19 | running += 1; 20 | 21 | doAsyncTask(element, (result) => { 22 | results.push(result); 23 | running -= 1; 24 | 25 | if (running < limit) { 26 | run(actions.shift()); 27 | } 28 | }); 29 | 30 | if (running < limit) { 31 | run(actions.shift()); 32 | } 33 | } else if (!running && !actions.length) { 34 | console.log('End: ', results); 35 | } 36 | } 37 | 38 | run(actions.shift()); 39 | 40 | /* Other way */ 41 | /* 42 | function runWhileCallback(result) { 43 | results.push(result); 44 | running -= 1; 45 | 46 | if (actions.length) { 47 | runWhile(); 48 | } else if (running === 0) { 49 | console.log('End: ', results); 50 | } 51 | } 52 | function runWhile() { 53 | while (actions.length && running < limit) { 54 | const item = actions.shift(); 55 | 56 | doAsyncTask(item, runWhileCallback); 57 | 58 | running += 1; 59 | } 60 | } 61 | 62 | runWhile(); 63 | */ 64 | -------------------------------------------------------------------------------- /events/event-evemitter/event-emitter-class-map.js: -------------------------------------------------------------------------------- 1 | class EventEmitter { 2 | constructor() { 3 | this.listeners = new Map(); 4 | } 5 | 6 | addListener(eventName, fn) { 7 | if (!this.listeners.has(eventName)) { 8 | this.listeners.set(eventName, []); 9 | } 10 | this.listeners.get(eventName).push(fn); 11 | 12 | return this; 13 | } 14 | 15 | on(eventName, fn) { 16 | return this.addListener(eventName, fn); 17 | } 18 | 19 | removeListener(eventName, fn) { 20 | const pos = (this.listeners.get(eventName) || []).indexOf(fn); 21 | 22 | if (pos >= 0) { 23 | this.listeners.get(eventName).splice(pos, 1); 24 | } 25 | 26 | return this; 27 | } 28 | 29 | off(eventName, fn) { 30 | return this.removeListener(eventName, fn); 31 | } 32 | 33 | once(eventName, fn) { 34 | const wrap = (...args) => { 35 | fn(...args); 36 | this.off(eventName, wrap); 37 | }; 38 | 39 | return this.on(eventName, wrap); 40 | } 41 | 42 | emit(eventName, ...args) { 43 | const listeners = (this.listeners.get(eventName) || []); 44 | 45 | if (listeners.length === 0) { 46 | return false; 47 | } 48 | 49 | listeners.forEach((fn) => { 50 | fn(...args); 51 | }); 52 | 53 | return true; 54 | } 55 | 56 | listenerCount(eventName) { 57 | return (this.listeners.get(eventName) || []).length; 58 | } 59 | 60 | rawListeners(eventName) { 61 | return this.listeners.get(eventName); 62 | } 63 | } 64 | 65 | module.exports = EventEmitter; 66 | -------------------------------------------------------------------------------- /stream-adventure/html_stream.js: -------------------------------------------------------------------------------- 1 | /* 2 | Your program will get some html written to stdin. Convert all the inner html to 3 | upper-case for elements with a class name of "loud", 4 | and pipe all the html to stdout. 5 | 6 | You can use `trumpet` and `through2` to solve this adventure. 7 | 8 | With `trumpet` you can create a transform stream from a css selector: 9 | 10 | var trumpet = require('trumpet'); 11 | var fs = require('fs'); 12 | var tr = trumpet(); 13 | fs.createReadStream('input.html').pipe(tr); 14 | 15 | var stream = tr.select('.beep').createStream(); 16 | 17 | Now `stream` outputs all the inner html content at `'.beep'` and the data you 18 | write to `stream` will appear as the new inner html content. 19 | 20 | Make sure to `npm install trumpet through2` in the directory where your solution 21 | file lives. 22 | */ 23 | 24 | const trumpet = require('trumpet'); 25 | const Through2 = require('through2'); 26 | 27 | function write(buffer, encoding, next) { 28 | const upperData = new String(buffer).toUpperCase(); 29 | this.push(new Buffer(upperData)); 30 | next(); 31 | } 32 | function end(done) { 33 | done(); 34 | } 35 | const toUpperCaseTransform = new Through2(write, end); 36 | const tr = trumpet(); 37 | 38 | const htmlStream = tr.selectAll('.loud').createStream(); 39 | 40 | htmlStream 41 | .pipe(toUpperCaseTransform) 42 | .pipe(htmlStream); 43 | 44 | process.stdin 45 | .pipe(tr) 46 | .pipe(process.stdout); 47 | 48 | 49 | // 2 solution 50 | /* 51 | tr.selectAll('.loud', (data) => { 52 | const stream = data.createStream(); 53 | 54 | stream 55 | .pipe(toUpperCaseTransform) 56 | .pipe(stream); 57 | }); 58 | 59 | process.stdin 60 | .pipe(tr) 61 | .pipe(process.stdout); 62 | */ 63 | -------------------------------------------------------------------------------- /events/event-evemitter/event-emitter-obj.js: -------------------------------------------------------------------------------- 1 | 2 | function eventEmiterBuilder() { 3 | const listeners = {}; 4 | 5 | return (function build(obj) { 6 | return { 7 | ...obj, 8 | addListener(eventName, fn) { 9 | if (!listeners[eventName]) { 10 | listeners[eventName] = []; 11 | } 12 | listeners[eventName].push(fn); 13 | 14 | return this; 15 | }, 16 | 17 | on(eventName, fn) { 18 | return this.addListener(eventName, fn); 19 | }, 20 | 21 | removeListener(eventName, fn) { 22 | const pos = (listeners[eventName] || []).indexOf(fn); 23 | 24 | if (pos >= 0) { 25 | listeners[eventName].splice(pos, 1); 26 | } 27 | 28 | return this; 29 | }, 30 | 31 | off(eventName, fn) { 32 | return this.removeListener(eventName, fn); 33 | }, 34 | 35 | once(eventName, fn) { 36 | const wrap = (...args) => { 37 | fn(...args); 38 | this.off(eventName, wrap); 39 | }; 40 | 41 | return this.on(eventName, wrap); 42 | }, 43 | 44 | emit(eventName, ...args) { 45 | const listenersForEvent = (listeners[eventName] || []); 46 | 47 | if (listenersForEvent.length === 0) { 48 | return false; 49 | } 50 | 51 | listenersForEvent.forEach((fn) => { 52 | fn(...args); 53 | }); 54 | 55 | return true; 56 | }, 57 | 58 | listenerCount(eventName) { 59 | return (listeners[eventName] || []).length; 60 | }, 61 | 62 | rawListeners(eventName) { 63 | return listeners[eventName]; 64 | }, 65 | }; 66 | }({ listeners })); 67 | } 68 | 69 | module.exports = { eventEmiterBuilder }; 70 | -------------------------------------------------------------------------------- /file-system/print-content/print-content-stream.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | 3 | async function read(file, resolve, reject) { 4 | const stat = await fs.promises.stat(file); 5 | const buffer = Buffer.alloc(stat.size); 6 | let length = 0; 7 | 8 | fs.createReadStream(file, { highWaterMark: 5 }) 9 | .on('data', (chunk) => { 10 | buffer.write(chunk.toString(), length); 11 | length += chunk.length; 12 | // could write to any place when length is too high 13 | }) 14 | .on('end', () => { 15 | resolve(buffer.toString('utf8')); 16 | }) 17 | .on('error', (err) => { 18 | reject(err); 19 | }); 20 | } 21 | function executeNext(file) { 22 | return new Promise((resolve, reject) => { 23 | read(file, resolve, reject); 24 | }); 25 | } 26 | 27 | /* 28 | Receive 3 files and print its content ordered. 29 | All filereaders shoul be initializated at the same time. 30 | Printing should be in order 31 | */ 32 | function printNextTick(file1, file2, file3) { 33 | return new Promise((resolve) => { 34 | process.nextTick(() => Promise.all([ 35 | executeNext(file1), 36 | executeNext(file2), 37 | executeNext(file3), 38 | ]) 39 | .then(([data1, data2, data3]) => { 40 | console.log(data1); 41 | console.log(data2); 42 | console.log(data3); 43 | resolve(); 44 | })); 45 | }); 46 | } 47 | 48 | function print(file1, file2, file3) { 49 | return Promise.all([ 50 | executeNext(file1), 51 | executeNext(file2), 52 | executeNext(file3), 53 | ]) 54 | .then(([data1, data2, data3]) => { 55 | console.log(data1); 56 | console.log(data2); 57 | console.log(data3); 58 | }); 59 | } 60 | 61 | module.exports = { printNextTick, print }; 62 | -------------------------------------------------------------------------------- /control-flow/async-parallel-for-loop-limit-concurency-waranty-result.js: -------------------------------------------------------------------------------- 1 | /* 2 | want to perform some operations in parallel, but keep the number 3 | of running I/O operations under a set limit 4 | */ 5 | 6 | function doAsyncTask(numericData, cb) { 7 | console.log(`to run doAsyncTask for ${numericData} - takes 1sec`); 8 | // run in random time 9 | setTimeout(() => cb(numericData * 2), Math.ceil(Math.random() * 1000)); 10 | } 11 | const actions = [1, 2, 3, 4, 5]; 12 | const results = []; 13 | const limit = 2; 14 | let running = 0; 15 | let position = 0; 16 | 17 | function run(element) { 18 | if (element) { 19 | running += 1; 20 | const pos = position; 21 | 22 | doAsyncTask(element, (result) => { 23 | results[pos] = result; 24 | running -= 1; 25 | 26 | if (running < limit) { 27 | position += 1; 28 | run(actions.shift()); 29 | } 30 | }); 31 | 32 | if (running < limit) { 33 | position += 1; 34 | run(actions.shift()); 35 | } 36 | } else if (!running && !actions.length) { 37 | console.log('End: ', results); 38 | } 39 | } 40 | 41 | run(actions.shift()); 42 | 43 | 44 | /* Other way */ 45 | 46 | /* 47 | function buildRunWhileCallback(pos) { 48 | return (result) => { 49 | results[pos] = result; 50 | running -= 1; 51 | 52 | if (actions.length) { 53 | runWhile(); 54 | } else if (running === 0) { 55 | console.log('End: ', results); 56 | } 57 | }; 58 | } 59 | function runWhile() { 60 | while (actions.length && running < limit) { 61 | const pos = position; 62 | position += 1; 63 | const item = actions.shift(); 64 | 65 | doAsyncTask(item, buildRunWhileCallback(pos)); 66 | 67 | running += 1; 68 | } 69 | } 70 | 71 | runWhile(); 72 | */ 73 | -------------------------------------------------------------------------------- /events/event-evemitter/event-emitter-proto.js: -------------------------------------------------------------------------------- 1 | function EventEmitter() { 2 | this.listeners = {}; 3 | } 4 | EventEmitter.prototype.addListener = function addListener(eventName, fn) { 5 | if (!this.listeners[eventName]) { 6 | this.listeners[eventName] = []; 7 | } 8 | this.listeners[eventName].push(fn); 9 | 10 | return this; 11 | }; 12 | 13 | EventEmitter.prototype.on = function on(eventName, fn) { 14 | return this.addListener(eventName, fn); 15 | }; 16 | 17 | EventEmitter.prototype.removeListener = function removeListener(eventName, fn) { 18 | const pos = (this.listeners[eventName] || []).indexOf(fn); 19 | 20 | if (pos >= 0) { 21 | this.listeners[eventName].splice(pos, 1); 22 | } 23 | 24 | return this; 25 | }; 26 | 27 | EventEmitter.prototype.off = function off(eventName, fn) { 28 | return this.removeListener(eventName, fn); 29 | }; 30 | 31 | EventEmitter.prototype.once = function once(eventName, fn) { 32 | const wrap = (...args) => { 33 | fn(...args); 34 | this.off(eventName, wrap); 35 | }; 36 | 37 | return this.on(eventName, wrap); 38 | }; 39 | 40 | EventEmitter.prototype.emit = function emit(eventName, ...args) { 41 | const listenersForEvent = (this.listeners[eventName] || []); 42 | 43 | if (listenersForEvent.length === 0) { 44 | return false; 45 | } 46 | 47 | listenersForEvent.forEach((fn) => { 48 | fn(...args); 49 | }); 50 | 51 | return true; 52 | }; 53 | 54 | EventEmitter.prototype.listenerCount = function listenerCount(eventName) { 55 | return (this.listeners[eventName] || []).length; 56 | }; 57 | 58 | EventEmitter.prototype.rawListeners = function rawListeners(eventName) { 59 | return this.listeners[eventName]; 60 | }; 61 | 62 | function eventEmiterBuilder() { 63 | return new EventEmitter(); 64 | } 65 | 66 | module.exports = { eventEmiterBuilder }; 67 | -------------------------------------------------------------------------------- /scope-chains-closures/garbage-collector.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Your Mission 4 | 5 | In this challenge, you will be required to use Chrome DevTools for detecting 6 | Garbage Collection events. Follow these steps to get a feel for what happens 7 | when Chrome performs its Mark & Sweep algorithm: 8 | 9 | 1) Fire up a new tab in Chrome 10 | 2) Open the DevTools > Timeline tab 11 | 3) Ensure the settings are like so: http://i.imgur.com/RMovIw4.png 12 | a) Frames View is unselected (allows seeing memory graphs) 13 | b) Flame Chart View is selected (allows seeing where execution time is spent) 14 | c) Only "Memory" is selected from the options 15 | 4) Click the solid gray record button to begin capturing data 16 | 5) Visit http://www.stackoverflow.com (or your favourite website) 17 | 6) Click the now-red record button to stop capturing data 18 | 7) You should now see something similar to: http://i.imgur.com/ZCNMrI1.png 19 | 8) The part we're interested in is when memory suddenly drops: 20 | http://i.imgur.com/FyMyRVI.png 21 | 9) Click this drop in memory to select it 22 | 10) Now look for the yellow event called "GC Event": http://i.imgur.com/3ieSxIZ.png 23 | 11) Clicking this event will reveal information about total memory garbage 24 | collected, and how long it took. 25 | 26 | One particularly interesting thing of note here is the length of time Garbage 27 | Collection can take: Often well beyond the 16ms maximum required to keep it 28 | within a single frame (at 60fps). While garbage collection occurs, it blocks the 29 | main thread, which means other Javascript cannot be executed until the event 30 | completes. Be conscious of how janky your application may become due to 31 | extensive Garbage Collection events! 32 | 33 | Note: If you'd like to get that lovely [COMPLETED] label for this lesson, 34 | Run @workshoppers/scope-chains-closures verify 35 | */ 36 | -------------------------------------------------------------------------------- /stream-adventure/duplexer.js: -------------------------------------------------------------------------------- 1 | /* 2 | Write a program that exports a function that spawns a process from a `cmd` 3 | string and an `args` array and returns a single duplex stream joining together 4 | the stdin and stdout of the spawned process: 5 | 6 | var spawn = require('child_process').spawn; 7 | 8 | module.exports = function (cmd, args) { 9 | // spawn the process and return a single stream 10 | // joining together the stdin and stdout here 11 | }; 12 | 13 | There is a very handy module you can use here: duplexer2. The duplexer2 module 14 | exports a single function `duplexer2(writable, readable)` that joins together a 15 | writable stream and a readable stream into a single, readable/writable duplex 16 | stream. 17 | 18 | If you use duplexer2, make sure to `npm install duplexer2` in the directory where 19 | your solution file is located. 20 | 21 | Keep in mind that the main and child processes will have different stream interface. 22 | 23 | process.stdin is a Readable stream 24 | process.stdout is a Writable stream 25 | 26 | For process you're inside the process to stdin is readable to you. 27 | For child process you're outside so that process's stdin is writable to you. 28 | 29 | childProc.stdin is a Writable stream 30 | childProc.stdout is a Readable stream 31 | 32 | Also, have a look at the duplexer2 documentation and notice that singnature 33 | of the exported function is `duplexer2([options], writable, readable)` 34 | which means that you might need to pass an options argument. 35 | 36 | Create a new file called duplexer.js which will hold your solution. 37 | 38 | To verify your solution run: 39 | `stream-adventure verify duplexer.js` 40 | */ 41 | 42 | const { spawn } = require('child_process'); 43 | const duplexer2 = require('duplexer2'); 44 | 45 | module.exports = (cmd, args) => { 46 | const ps = spawn(cmd, args); 47 | return duplexer2(ps.stdin, ps.stdout); 48 | }; 49 | -------------------------------------------------------------------------------- /streams/csv/custom-csv-parser.js: -------------------------------------------------------------------------------- 1 | /* 2 | Transforma a CSV file in a json file with a list of objects. Example: 3 | a:b:c 4 | 1:2:3 5 | 4:5:6 6 | to 7 | { 8 | data: 9 | [ 10 | {a:1, b:2, c:3}, 11 | {a:4, b:5, c:6}, 12 | ] 13 | } 14 | 15 | Steps: 16 | * read csv file 17 | * create object properties 18 | * fill list of element with object properties + object values 19 | * print data in file 20 | */ 21 | const fs = require('fs'); 22 | const { Transform, Writable } = require('readable-stream'); // better than just require('streams') 23 | 24 | let HEADERS; 25 | const readableFileStream = fs.createReadStream(`${__dirname}/example.csv`); 26 | const writableFileStream = fs.createWriteStream(`${__dirname}/result.json`); 27 | writableFileStream.write('{\n"data": [\n'); 28 | 29 | const breakLineTransformer = new Transform({ 30 | transform(chunk, enconding, cb) { 31 | chunk.toString().split('\n').forEach((line) => this.push(line)); 32 | cb(); 33 | }, 34 | }); 35 | 36 | function buildObjectData(values) { 37 | return HEADERS.reduce((acc, header, pos) => ({ ...acc, [header]: values[pos] }), {}); 38 | } 39 | 40 | const buildDataStream = new Transform({ 41 | transform(chunk, enconding, cb) { 42 | if (!HEADERS) { 43 | HEADERS = chunk.toString().split(':'); 44 | } else { 45 | const objectData = buildObjectData(chunk.toString().split(':')); 46 | this.push(JSON.stringify(objectData)); 47 | } 48 | cb(); 49 | }, 50 | }); 51 | 52 | const printerStream = new Writable({ 53 | write(chunk, enconding, cb) { 54 | console.log('=> ', chunk.toString()); 55 | writableFileStream.write(`${chunk.toString()},\n`); 56 | cb(); 57 | }, 58 | final(cb) { 59 | writableFileStream.write(']\n}'); 60 | writableFileStream.end(); 61 | cb(); 62 | }, 63 | }); 64 | 65 | readableFileStream 66 | .pipe(breakLineTransformer) 67 | .pipe(buildDataStream) 68 | .pipe(printerStream); 69 | -------------------------------------------------------------------------------- /file-system/watcher/watcher.test.js: -------------------------------------------------------------------------------- 1 | const { expect } = require('chai'); 2 | const fs = require('fs'); 3 | const { watchFolder } = require('./watcher'); 4 | 5 | describe('watcher', () => { 6 | describe('watchFolder', () => { 7 | const folderToWatch = __dirname; 8 | const fileToAdd = 'fileToAdd.txt'; 9 | let watcherListener; 10 | 11 | beforeAll(() => { 12 | watcherListener = watchFolder(folderToWatch); 13 | }); 14 | 15 | it('should notifiy when a file is added with its name', (done) => { 16 | const listen = (filename) => { 17 | expect(filename).to.eql(fileToAdd); 18 | done(); 19 | }; 20 | 21 | watcherListener.once('change', listen); 22 | 23 | fs.writeFileSync(`${__dirname}/${fileToAdd}`, 'Content'); 24 | }); 25 | 26 | it('should notifiy when a file is edited', (done) => { 27 | const listen = (filename) => { 28 | expect(filename).to.eql(fileToAdd); 29 | done(); 30 | }; 31 | 32 | watcherListener.once('change', listen); 33 | 34 | fs.appendFileSync(`${__dirname}/${fileToAdd}`, 'More data'); 35 | }); 36 | 37 | it('should notify when a file is removed with its name', (done) => { 38 | const listen = (filename) => { 39 | expect(filename).to.eql(fileToAdd); 40 | done(); 41 | }; 42 | 43 | watcherListener.once('change', listen); 44 | 45 | fs.unlinkSync(`${__dirname}/${fileToAdd}`); 46 | }); 47 | 48 | it('after call stop watching, should not receive events', async (done) => { 49 | watcherListener.emit('stop'); 50 | 51 | // await listener receives "stop" event 52 | await new Promise((resolve) => setTimeout(() => resolve(), 500)); 53 | 54 | watcherListener.once('change', () => { done(new Error()); }); 55 | 56 | fs.writeFileSync(`${__dirname}/${fileToAdd}`, 'Content'); 57 | fs.unlinkSync(`${__dirname}/${fileToAdd}`); 58 | 59 | setTimeout(done, 500); 60 | }); 61 | }); 62 | }); 63 | -------------------------------------------------------------------------------- /stream-adventure/secretz.js: -------------------------------------------------------------------------------- 1 | /* 2 | An encrypted, gzipped tar file will be piped in on process.stdin. To beat this 3 | challenge, for each file in the tar input, print a hex-encoded md5 hash of the 4 | file contents followed by a single space followed by the filename, then a 5 | newline. 6 | 7 | You will receive the cipher name as process.argv[2] and the cipher passphrase as 8 | process.argv[3]. You can pass these arguments directly through to 9 | `crypto.createDecipher()`. 10 | 11 | The built-in zlib library you get when you `require('zlib')` has a 12 | `zlib.createGunzip()` that returns a stream for gunzipping. 13 | 14 | The `tar` module from npm has a `tar.Parse()` function that emits `'entry'` 15 | events for each file in the tar input. Each `entry` object is a readable stream 16 | of the file contents from the archive and: 17 | 18 | `entry.type` is the kind of file ('File', 'Directory', etc) 19 | `entry.path` is the file path 20 | 21 | Using the tar module looks like: 22 | 23 | var tar = require('tar'); 24 | var parser = tar.Parse(); 25 | parser.on('entry', function (e) { 26 | console.dir(e); 27 | }); 28 | var fs = require('fs'); 29 | fs.createReadStream('file.tar').pipe(parser); 30 | 31 | Use `crypto.createHash('md5', { encoding: 'hex' })` to generate a stream that 32 | outputs a hex md5 hash for the content written to it. 33 | 34 | Make sure to ` 35 | */ 36 | 37 | const crypto = require('crypto'); 38 | const zlib = require('zlib'); 39 | const tar = require('tar'); 40 | const concat = require('concat-stream'); 41 | 42 | const cipherName = process.argv[2]; 43 | const passphrase = process.argv[3]; 44 | 45 | const parser = new tar.Parse(); 46 | parser.on('entry', (entry) => { 47 | if (entry.type !== 'File') { 48 | return entry.resume(); 49 | } 50 | 51 | entry 52 | .pipe(crypto.createHash('md5', { encoding: 'hex' })) 53 | .pipe(concat((hasData) => { 54 | console.log(`${hasData} ${entry.path}`); 55 | })); 56 | }); 57 | 58 | process.stdin 59 | .pipe(crypto.createDecipher(cipherName, passphrase)) 60 | .pipe(zlib.createGunzip()) 61 | .pipe(parser); 62 | -------------------------------------------------------------------------------- /stream-adventure/transform.js: -------------------------------------------------------------------------------- 1 | /* 2 | Convert data from `process.stdin` to upper-case data on `process.stdout` 3 | using the `through2` module. 4 | 5 | To get the `through2` module you'll need to do: 6 | 7 | npm install through2 8 | 9 | A transform stream takes input data and applies an operation to the data to 10 | produce the output data. 11 | 12 | Create a through stream with a `write` and `end` function: 13 | 14 | var through = require('through2'); 15 | var stream = through(write, end); 16 | 17 | The `write` function is called for every buffer of available input: 18 | 19 | function write (buffer, encoding, next) { 20 | // ... 21 | } 22 | 23 | and the `end` function is called when there is no more data: 24 | 25 | function end () { 26 | // ... 27 | } 28 | 29 | Inside the write function, call `this.push()` to produce output data and call 30 | `next()` when you're ready to receive the next chunk: 31 | 32 | function write (buffer, encoding, next) { 33 | this.push('I got some data: ' + buffer + '\n'); 34 | next(); 35 | } 36 | 37 | and call `done()` to finish the output: 38 | 39 | function end (done) { 40 | done(); 41 | } 42 | 43 | `write` and `end` are both optional. 44 | 45 | If `write` is not specified, the default implementation passes the input data to 46 | the output unmodified. 47 | 48 | If `end` is not specified, the default implementation calls `this.push(null)` 49 | to close the output side when the input side ends. 50 | 51 | Make sure to pipe `process.stdin` into your transform stream 52 | and pipe your transform stream into `process.stdout`, like this: 53 | 54 | process.stdin.pipe(stream).pipe(process.stdout); 55 | 56 | To convert a buffer to a string, call `buffer.toString()`. 57 | */ 58 | const Through2 = require('through2'); 59 | 60 | function write(buffer, encoding, next) { 61 | const upperData = new String(buffer).toUpperCase(); 62 | this.push(new Buffer(upperData)); 63 | next(); 64 | } 65 | 66 | function end(done) { 67 | done(); 68 | } 69 | const toUpperCaseTransform = new Through2(write, end); 70 | 71 | process.stdin.pipe(toUpperCaseTransform).pipe(process.stdout); 72 | -------------------------------------------------------------------------------- /stream-adventure/lines.js: -------------------------------------------------------------------------------- 1 | /* 2 | Instead of transforming every line as in the previous "TRANSFORM" example, 3 | for this challenge, convert even-numbered lines to upper-case and odd-numbered 4 | lines to lower-case. Consider the first line to be odd-numbered. For example 5 | given this input: 6 | 7 | One 8 | Two 9 | Three 10 | Four 11 | 12 | Your program should output: 13 | 14 | one 15 | TWO 16 | three 17 | FOUR 18 | 19 | Even though it's not obligatory, you can use the `split` module 20 | to split input by newlines. For example: 21 | 22 | var split = require('split'); 23 | var through2 = require('through2'); 24 | process.stdin 25 | .pipe(split()) 26 | .pipe(through2(function (line, _, next) { 27 | console.dir(line.toString()); 28 | next(); 29 | })) 30 | ; 31 | 32 | `split` will buffer chunks on newlines before you get them. With example 33 | above, we will get separate events for each line even though all the data 34 | probably arrives on the same chunk: 35 | 36 | $ echo -e 'one\ntwo\nthree' | node split.js 37 | 'one' 38 | 'two' 39 | 'three' 40 | 41 | Your own program could use `split` in this way, and you should transform the 42 | input and pipe the output through to `process.stdout`. Keep in mind that, 43 | if you decide to use this technique, `split2` might be actually needed, 44 | depending on the versions of the other dependencies. 45 | 46 | You are free to solve the challenge without `split` module. In this case, 47 | you would have to add a new line after each line to have a passing match. 48 | 49 | Make sure to `npm install split through2` in the directory where your solution 50 | file lives. 51 | */ 52 | 53 | const through2 = require('through2'); 54 | const split = require('split'); 55 | 56 | let shouldUpper = false; 57 | 58 | function write(buffer, encoding, next) { 59 | const trasnformedData = (shouldUpper) ? new String(buffer).toUpperCase() 60 | : new String(buffer).toLowerCase(); 61 | 62 | const finalBuffer = new Buffer(`\n${trasnformedData}`); 63 | 64 | this.push(finalBuffer); 65 | shouldUpper = !shouldUpper; 66 | 67 | next(); 68 | } 69 | 70 | function end(done) { 71 | done(); 72 | } 73 | 74 | const toUpperCaseTransform = new through2(write, end); 75 | 76 | process.stdin 77 | .pipe(split()) 78 | .pipe(toUpperCaseTransform) 79 | .pipe(process.stdout); 80 | -------------------------------------------------------------------------------- /stream-adventure/duplexer_redux.js: -------------------------------------------------------------------------------- 1 | /* 2 | In this example, you will be given a readable stream, `counter`, as the first 3 | argument to your exported function: 4 | 5 | module.exports = function (counter) { 6 | // return a duplex stream to count countries on the writable side 7 | // and pass through `counter` on the readable side 8 | }; 9 | 10 | Return a duplex stream with the `counter` as the readable side. You will be 11 | written objects with a 2-character `country` field as input, such as these: 12 | 13 | {"short":"OH","name":"Ohio","country":"US"} 14 | {"name":"West Lothian","country":"GB","region":"Scotland"} 15 | {"short":"NSW","name":"New South Wales","country":"AU"} 16 | 17 | Create an object to track the number of occurrences of each unique country code. 18 | 19 | For example: 20 | 21 | {"US": 2, "GB": 3, "CN": 1} 22 | 23 | Once the input ends, call `counter.setCounts()` with your counts object. 24 | 25 | The `duplexer2` module will again be very handy in this example. 26 | 27 | If you use duplexer, make sure to `npm install duplexer2` in the directory where 28 | your solution file is located. 29 | 30 | Keep in mind that you will have to work with objects, not buffers. 31 | Consult the documentation for further details: 32 | https://nodejs.org/api/stream.html#stream_object_mode 33 | When you switch on the object mode, remember to do the same for all 34 | additional dependencies that you work with (i.e. through2) 35 | 36 | Create a new file called duplexer-redux.js which will hold your solution. 37 | 38 | To verify your solution run: 39 | `stream-adventure verify duplexer-redux.js` 40 | */ 41 | 42 | const duplexer2 = require('duplexer2'); 43 | const { Writable } = require('stream'); 44 | const through2 = require('through2').obj; 45 | 46 | 47 | module.exports = function (counter) { 48 | // return a duplex stream to count countries on the writable side 49 | // and pass through `counter` on the readable side 50 | 51 | const counts = {}; 52 | 53 | function write(input, encoding, next) { 54 | const counter = counts[input.country] || 0; 55 | counts[input.country] = counter + 1; 56 | 57 | next(); 58 | } 59 | 60 | function end(done) { 61 | counter.setCounts(counts); 62 | done(); 63 | } 64 | const countableStream = through2(write, end); 65 | 66 | 67 | return duplexer2({ objectMode: true }, countableStream, counter); 68 | }; 69 | -------------------------------------------------------------------------------- /streams/stream-object-string-transform.js: -------------------------------------------------------------------------------- 1 | /* 2 | This is an example sentence. Is all ok? 3 | 4 | { 5 | wordsCount: 8, 6 | words: ['This', 'is', 'an', 'example', sentence', 'Is', 'all', 'ok'], 7 | repitedWords: true, 8 | symbolsCount: 2, 9 | symbols: ['.', '?'], 10 | repitedSymbols: false, 11 | } 12 | */ 13 | const { Transform } = require('stream'); 14 | 15 | const SYMBOLS = ['.', ',', '?', '¡', '¿', '!', ':', ';']; 16 | function isSymbol(char) { 17 | return SYMBOLS.includes(char); 18 | } 19 | 20 | 21 | function createList(chunk) { 22 | const listOfElements = []; 23 | let temp = []; 24 | String(chunk).split('').forEach((character) => { 25 | if ((character === ' ' || isSymbol(character)) && temp.length) { 26 | listOfElements.push(temp.join('').toLowerCase().trim()); 27 | temp = []; 28 | } 29 | 30 | if (isSymbol(character)) { 31 | listOfElements.push(character); 32 | } else { 33 | temp.push(character); 34 | } 35 | }); 36 | return listOfElements; 37 | } 38 | 39 | const splitText = new Transform({ 40 | writableObjectMode: false, 41 | readableObjectMode: true, 42 | 43 | transform(chunk, encoding, cb) { 44 | const listOfElements = createList(chunk); 45 | 46 | const words = listOfElements.filter((element) => !isSymbol(element)); 47 | const symbols = listOfElements.filter((element) => isSymbol(element)); 48 | 49 | this.push({ 50 | words, 51 | symbols, 52 | }); 53 | 54 | cb(); 55 | }, 56 | }); 57 | 58 | const countElements = new Transform({ 59 | objectMode: true, 60 | transform(chunk, encoding, cb) { 61 | const { words, symbols } = chunk; 62 | 63 | this.push({ 64 | words, 65 | symbols, 66 | wordsCount: words.length, 67 | symbolsCount: symbols.length, 68 | }); 69 | 70 | cb(); 71 | }, 72 | }); 73 | 74 | const repitedElements = new Transform({ 75 | objectMode: true, 76 | transform(chunk, encoding, cb) { 77 | const { words, symbols } = chunk; 78 | const repitedWords = words.length - new Set(words).size; 79 | const repitedSymbols = symbols.length - new Set(symbols).size; 80 | 81 | this.push({ ...chunk, repitedWords, repitedSymbols }); 82 | 83 | cb(); 84 | }, 85 | }); 86 | 87 | const toString = new Transform({ 88 | readableObjectMode: false, 89 | writableObjectMode: true, 90 | transform(chunk, encoding, cb) { 91 | this.push(JSON.stringify(chunk, null, 2)); 92 | cb(); 93 | }, 94 | }); 95 | 96 | process.stdin 97 | .pipe(splitText) 98 | .pipe(countElements) 99 | .pipe(repitedElements) 100 | .pipe(toString) 101 | .pipe(process.stdout); 102 | -------------------------------------------------------------------------------- /stream-adventure/http_server.js: -------------------------------------------------------------------------------- 1 | /* 2 | In this challenge, write an http server that uses a through stream to write back 3 | the request stream as upper-cased response data for POST requests. 4 | 5 | Streams aren't just for text files and stdin/stdout. Did you know that http 6 | request and response objects from node core's `http.createServer()` handler are 7 | also streams? 8 | 9 | For example, we can stream a file to the response object: 10 | 11 | var http = require('http'); 12 | var fs = require('fs'); 13 | var server = http.createServer(function (req, res) { 14 | fs.createReadStream('file.txt').pipe(res); 15 | }); 16 | server.listen(process.argv[2]); 17 | 18 | This is great because our server can respond immediately without buffering 19 | everything in memory first. 20 | 21 | We can also stream a request to populate a file with data: 22 | 23 | var http = require('http'); 24 | var fs = require('fs'); 25 | var server = http.createServer(function (req, res) { 26 | if (req.method === 'POST') { 27 | req.pipe(fs.createWriteStream('post.txt')); 28 | } 29 | res.end('beep boop\n'); 30 | }); 31 | server.listen(process.argv[2]); 32 | 33 | You can test this post server with curl: 34 | 35 | $ node server.js 8000 & 36 | $ echo hack the planet | curl -d@- http://localhost:8000 37 | beep boop 38 | $ cat post.txt 39 | hack the planet 40 | 41 | Your http server should listen on the port given at process.argv[2] and convert 42 | the POST request written to it to upper-case using the same approach as the 43 | TRANSFORM example. 44 | 45 | As a refresher, here's an example with the default through2 callbacks explicitly 46 | defined: 47 | 48 | var through = require('through2'); 49 | process.stdin.pipe(through(write, end)).pipe(process.stdout); 50 | 51 | function write (buf, _, next) { 52 | this.push(buf); 53 | next(); 54 | } 55 | function end (done) { done(); } 56 | 57 | Do that, but send upper-case data in your http server in response to POST data. 58 | 59 | Make sure to `npm install through2` in the directory where your solution file 60 | lives. 61 | */ 62 | 63 | const http = require('http'); 64 | const Through2 = require('through2'); 65 | 66 | function write(buffer, encoding, next) { 67 | const upperData = String(buffer).toUpperCase(); 68 | this.push(Buffer.from(upperData)); 69 | next(); 70 | } 71 | 72 | function end(done) { 73 | done(); 74 | } 75 | 76 | const toUpperCase = new Through2(write, end); 77 | 78 | const server = http.createServer((req, res) => { 79 | if (req.method === 'POST') { 80 | return req.pipe(toUpperCase).pipe(res); 81 | } 82 | 83 | res.end(); 84 | }); 85 | 86 | server.listen(Number(process.argv[2])); 87 | -------------------------------------------------------------------------------- /streams/stream-object-string-transform-by-char.js: -------------------------------------------------------------------------------- 1 | /* 2 | This is an example sentence. Is all ok?\n 3 | 4 | { 5 | wordsCount: 8, 6 | words: ['This', 'is', 'an', 'example', sentence', 'Is', 'all', 'ok'], 7 | repitedWords: true, 8 | symbolsCount: 2, 9 | symbols: ['.', '?'], 10 | repitedSymbols: false, 11 | } 12 | */ 13 | const { Transform, Duplex } = require('stream'); 14 | 15 | const SYMBOLS = ['.', ',', '?', '¡', '¿', '!', ':', ';']; 16 | function isSymbol(char) { 17 | return SYMBOLS.includes(char); 18 | } 19 | 20 | const splitChars = new Transform({ 21 | transform(chunk, encoding, cb) { 22 | String(chunk).split('').forEach((char) => this.push(char)); 23 | cb(); 24 | }, 25 | }); 26 | 27 | const splitText = new Transform({ 28 | writableObjectMode: false, 29 | readableObjectMode: false, 30 | 31 | transform(chunk, encoding, cb) { 32 | if (!this.temp) { 33 | this.temp = []; 34 | } 35 | 36 | const stringChunk = String(chunk); 37 | 38 | if ((stringChunk.match(/\n/) || stringChunk === ' ' || isSymbol(stringChunk)) && this.temp.length) { 39 | this.push(this.temp.join('').toLowerCase().trim()); 40 | this.temp = []; 41 | } 42 | 43 | if (isSymbol(stringChunk) || stringChunk.match(/\n/)) { 44 | this.push(stringChunk); 45 | } else if (stringChunk !== ' ') { 46 | this.temp.push(stringChunk); 47 | } 48 | 49 | cb(); 50 | }, 51 | }); 52 | 53 | const createObjectData = new Duplex({ 54 | writableObjectMode: true, 55 | readableObjectMode: true, 56 | 57 | read() {}, 58 | write(chunk, enconding, cb) { 59 | if (!this.data) { 60 | this.data = { 61 | symbols: [], 62 | words: [], 63 | }; 64 | } 65 | const stringChunk = String(chunk); 66 | 67 | if (stringChunk.match(/\n/)) { 68 | this.push(this.data); 69 | this.data = null; 70 | } else if (isSymbol(stringChunk)) { 71 | this.data.symbols.push(stringChunk); 72 | } else { 73 | this.data.words.push(stringChunk); 74 | } 75 | 76 | cb(); 77 | }, 78 | 79 | }); 80 | 81 | const countElements = new Transform({ 82 | objectMode: true, 83 | 84 | transform(chunk, encoding, cb) { 85 | const { words, symbols } = chunk; 86 | 87 | this.push({ 88 | words, 89 | symbols, 90 | wordsCount: words.length, 91 | symbolsCount: symbols.length, 92 | }); 93 | 94 | cb(); 95 | }, 96 | }); 97 | 98 | const repitedElements = new Transform({ 99 | objectMode: true, 100 | 101 | transform(chunk, encoding, cb) { 102 | const { words, symbols } = chunk; 103 | const repitedWords = words.length - new Set(words).size; 104 | const repitedSymbols = symbols.length - new Set(symbols).size; 105 | 106 | this.push({ ...chunk, repitedWords, repitedSymbols }); 107 | 108 | cb(); 109 | }, 110 | }); 111 | 112 | const toString = new Transform({ 113 | readableObjectMode: false, 114 | writableObjectMode: true, 115 | 116 | transform(chunk, encoding, cb) { 117 | if (chunk === null) { 118 | this.push(null); 119 | } 120 | this.push(JSON.stringify(chunk, null, 2)); 121 | cb(); 122 | }, 123 | }); 124 | 125 | process.stdin 126 | .pipe(splitChars) 127 | .pipe(splitText) 128 | .pipe(createObjectData) 129 | .pipe(countElements) 130 | .pipe(repitedElements) 131 | .pipe(toString) 132 | .pipe(process.stdout); 133 | -------------------------------------------------------------------------------- /stream-adventure/combiner.js: -------------------------------------------------------------------------------- 1 | /* 2 | Create a module in a new file named combiner.js, it should return a readable/writable stream using the 3 | `stream-combiner` module. 4 | 5 | You can use this code to start with: 6 | 7 | var combine = require('stream-combiner') 8 | 9 | module.exports = function () { 10 | return combine( 11 | // read newline-separated json, 12 | // group books into genres, 13 | // then gzip the output 14 | ) 15 | } 16 | 17 | Your stream will be written a newline-separated JSON list of science fiction 18 | genres and books. All the books after a `"type":"genre"` row belong in that 19 | genre until the next `"type":"genre"` comes along in the output. 20 | 21 | {"type":"genre","name":"cyberpunk"} 22 | {"type":"book","name":"Neuromancer"} 23 | {"type":"book","name":"Snow Crash"} 24 | {"type":"genre","name":"space opera"} 25 | {"type":"book","name":"A Deepness in the Sky"} 26 | {"type":"book","name":"Void"} 27 | 28 | Your program should generate a newline-separated list of JSON lines of genres, 29 | each with a `"books"` array containing all the books in that genre. The input 30 | above would yield the output: 31 | 32 | {"name":"cyberpunk","books":["Neuromancer","Snow Crash"]} 33 | {"name":"space opera","books":["A Deepness in the Sky","Void"]} 34 | 35 | Your stream should take this list of JSON lines and gzip it with 36 | `zlib.createGzip()`. 37 | 38 | * HINTS * 39 | 40 | The `stream-combiner` module creates a pipeline from a list of streams, 41 | returning a single stream that exposes the first stream as the writable side and 42 | the last stream as the readable side like the `duplexer` module, but with an 43 | arbitrary number of streams in between. Unlike the `duplexer` module, each 44 | stream is piped to the next. For example: 45 | 46 | var combine = require('stream-combiner'); 47 | var stream = combine(a, b, c, d); 48 | 49 | will internally do `a.pipe(b).pipe(c).pipe(d)` but the `stream` returned by 50 | `combine()` has its writable side hooked into `a` and its readable side hooked 51 | into `d`. 52 | 53 | Your module should return the combined stream that will be fed input into the 54 | front 'end' of the stream, reads the associated JSON, processes the input book 55 | data by grouping it by genre and produces a gzipped result stream from which 56 | the result may be read. 57 | 58 | As in the previous LINES adventure, the `split` module is very handy here. You 59 | can put a split stream directly into the stream-combiner pipeline. 60 | Note that split can send empty lines too. 61 | 62 | If you end up using `split` and `stream-combiner`, make sure to install them 63 | into the directory where your solution file resides by doing: 64 | 65 | npm install stream-combiner split 66 | 67 | To verify your solution run: 68 | stream-adventure verify combiner.js 69 | */ 70 | const streamCombiner = require('stream-combiner'); 71 | const split = require('split'); 72 | const zlib = require('zlib'); 73 | const fs = require('fs'); 74 | const through2 = require('through2'); 75 | 76 | let books = null; 77 | 78 | function maybeWriteBook(stream) { 79 | if (books !== null) { 80 | stream.push(`${JSON.stringify(books)}\n`); 81 | } 82 | books = null; 83 | } 84 | 85 | function write(input, encoding, next) { 86 | if (input && input.length) { 87 | const jsonInput = JSON.parse(input); 88 | 89 | if (jsonInput.type === 'genre') { 90 | maybeWriteBook(this); 91 | 92 | books = { name: jsonInput.name, books: [] }; 93 | } else if (jsonInput.type === 'book') { 94 | books.books.push(jsonInput.name); 95 | } 96 | } 97 | 98 | next(); 99 | } 100 | 101 | function end(done) { 102 | maybeWriteBook(this); 103 | 104 | done(); 105 | } 106 | 107 | 108 | const booksStream = through2(write, end); 109 | 110 | 111 | module.exports = function () { 112 | return streamCombiner( 113 | // read newline-separated json, 114 | split(), 115 | // group books into genres, 116 | booksStream, 117 | // then gzip the output 118 | zlib.createGzip(), 119 | ); 120 | }; 121 | -------------------------------------------------------------------------------- /events/event-evemitter/event-emitter-class.test.js: -------------------------------------------------------------------------------- 1 | const chai = require('chai'); 2 | const sinon = require('sinon'); 3 | const sinonChai = require('sinon-chai'); 4 | const EventEmiter = require('./event-emitter-class'); 5 | 6 | const { expect } = chai; 7 | chai.use(sinonChai); 8 | describe('event-emiter', () => { 9 | describe('addListener', () => { 10 | const eventEmiter = new EventEmiter(); 11 | const fn = () => {}; 12 | const event = 'testEvent'; 13 | 14 | beforeAll(() => { 15 | eventEmiter.addListener(event, fn); 16 | }); 17 | 18 | it('should have one element added for event', () => { 19 | expect(eventEmiter.listeners[event]).to.have.lengthOf(1); 20 | }); 21 | }); 22 | 23 | describe('on', () => { 24 | const eventEmiter = new EventEmiter(); 25 | const fn = () => {}; 26 | const fn2 = () => {}; 27 | const event = 'testEvent'; 28 | const event2 = 'testEvent2'; 29 | 30 | beforeAll(() => { 31 | eventEmiter.on(event, fn) 32 | .on(event, fn2) 33 | .on(event2, fn2); 34 | }); 35 | 36 | it('should have two element added for event', () => { 37 | expect(eventEmiter.listeners[event]).to.have.lengthOf(2); 38 | }); 39 | 40 | it('should have one element added for an event', () => { 41 | expect(eventEmiter.listeners[event2]).to.have.lengthOf(1); 42 | }); 43 | }); 44 | 45 | describe('removeListener', () => { 46 | const eventEmiter = new EventEmiter(); 47 | const fn = () => {}; 48 | const event = 'testEvent'; 49 | 50 | beforeAll(() => { 51 | eventEmiter.addListener(event, fn); 52 | }); 53 | 54 | it('should have one element added for event and when remove should have 0', () => { 55 | expect(eventEmiter.listeners[event]).to.have.lengthOf(1); 56 | eventEmiter.removeListener(event, fn); 57 | expect(eventEmiter.listeners[event]).to.have.lengthOf(0); 58 | }); 59 | }); 60 | 61 | describe('off', () => { 62 | const eventEmiter = new EventEmiter(); 63 | const fn = () => {}; 64 | const fn2 = () => {}; 65 | const event = 'testEvent'; 66 | const event2 = 'testEvent2'; 67 | 68 | beforeAll(() => { 69 | eventEmiter.addListener(event, fn); 70 | eventEmiter.addListener(event, fn2); 71 | eventEmiter.addListener(event2, fn2); 72 | }); 73 | 74 | it('should have two element added for event and when remove should have one and keep other events', () => { 75 | expect(eventEmiter.listeners[event]).to.have.lengthOf(2); 76 | eventEmiter.off(event, fn); 77 | expect(eventEmiter.listeners[event]).to.have.lengthOf(1); 78 | expect(eventEmiter.listeners[event2]).to.have.lengthOf(1); 79 | }); 80 | }); 81 | 82 | describe('once', () => { 83 | const eventEmiter = new EventEmiter(); 84 | const fn = () => {}; 85 | const event = 'testEvent'; 86 | 87 | beforeAll(() => { 88 | eventEmiter.once(event, fn); 89 | }); 90 | 91 | it('should have added the function and be removed after the event is emited', () => { 92 | expect(eventEmiter.listeners[event]).to.have.lengthOf(1); 93 | eventEmiter.emit(event, 'data'); 94 | expect(eventEmiter.listeners[event]).to.have.lengthOf(0); 95 | }); 96 | }); 97 | 98 | describe('emit', () => { 99 | describe('for a event with listeners', () => { 100 | const eventEmiter = new EventEmiter(); 101 | const fn = sinon.spy(); 102 | const event = 'testEvent'; 103 | let result; 104 | 105 | beforeAll(() => { 106 | eventEmiter.on(event, fn); 107 | result = eventEmiter.emit(event, 'data'); 108 | }); 109 | 110 | it('should call functions', () => { 111 | expect(fn).to.have.been.calledWith('data'); 112 | }); 113 | 114 | it('should return true', () => { 115 | expect(result).to.eql(true); 116 | }); 117 | }); 118 | 119 | describe('for a event without listeners', () => { 120 | const eventEmiter = new EventEmiter(); 121 | const event = 'testEvent'; 122 | let result; 123 | 124 | beforeAll(() => { 125 | result = eventEmiter.emit(event, 'data'); 126 | }); 127 | 128 | it('should return false', () => { 129 | expect(result).to.eql(false); 130 | }); 131 | }); 132 | }); 133 | 134 | describe('listenerCount', () => { 135 | const eventEmiter = new EventEmiter(); 136 | const fn = () => {}; 137 | const fn2 = () => {}; 138 | const event = 'testEvent'; 139 | 140 | beforeAll(() => { 141 | eventEmiter.addListener(event, fn); 142 | eventEmiter.on(event, fn2); 143 | }); 144 | 145 | it('should return the correct number of listeners', () => { 146 | expect(eventEmiter.listenerCount(event)).to.eql(2); 147 | expect(eventEmiter.listenerCount('other')).to.eql(0); 148 | }); 149 | }); 150 | 151 | describe('rawListeners', () => { 152 | const eventEmiter = new EventEmiter(); 153 | const fn = () => {}; 154 | const fn2 = () => {}; 155 | const event = 'testEvent'; 156 | const event2 = 'testEvent'; 157 | 158 | beforeAll(() => { 159 | eventEmiter.addListener(event, fn); 160 | eventEmiter.on(event2, fn2); 161 | }); 162 | 163 | it('should return all the listeners', () => { 164 | expect(eventEmiter.rawListeners(event)).to.eql(eventEmiter.listeners[event]); 165 | expect(eventEmiter.rawListeners(event2)).to.eql(eventEmiter.listeners[event2]); 166 | }); 167 | }); 168 | }); 169 | -------------------------------------------------------------------------------- /events/event-evemitter/event-emitter-obj.test.js: -------------------------------------------------------------------------------- 1 | const chai = require('chai'); 2 | const sinon = require('sinon'); 3 | const sinonChai = require('sinon-chai'); 4 | const { eventEmiterBuilder } = require('./event-emitter-obj'); 5 | 6 | const { expect } = chai; 7 | chai.use(sinonChai); 8 | describe('event-emiter', () => { 9 | describe('addListener', () => { 10 | const eventEmiter = eventEmiterBuilder(); 11 | const fn = () => {}; 12 | const event = 'testEvent'; 13 | 14 | beforeAll(() => { 15 | eventEmiter.addListener(event, fn); 16 | }); 17 | 18 | it('should have one element added for event', () => { 19 | expect(eventEmiter.listeners[event]).to.have.lengthOf(1); 20 | }); 21 | }); 22 | 23 | describe('on', () => { 24 | const eventEmiter = eventEmiterBuilder(); 25 | const fn = () => {}; 26 | const fn2 = () => {}; 27 | const event = 'testEvent'; 28 | const event2 = 'testEvent2'; 29 | 30 | beforeAll(() => { 31 | eventEmiter.on(event, fn) 32 | .on(event, fn2) 33 | .on(event2, fn2); 34 | }); 35 | 36 | it('should have two element added for event', () => { 37 | expect(eventEmiter.listeners[event]).to.have.lengthOf(2); 38 | }); 39 | 40 | it('should have one element added for an event', () => { 41 | expect(eventEmiter.listeners[event2]).to.have.lengthOf(1); 42 | }); 43 | }); 44 | 45 | describe('removeListener', () => { 46 | const eventEmiter = eventEmiterBuilder(); 47 | const fn = () => {}; 48 | const event = 'testEvent'; 49 | 50 | beforeAll(() => { 51 | eventEmiter.addListener(event, fn); 52 | }); 53 | 54 | it('should have one element added for event and when remove should have 0', () => { 55 | expect(eventEmiter.listeners[event]).to.have.lengthOf(1); 56 | eventEmiter.removeListener(event, fn); 57 | expect(eventEmiter.listeners[event]).to.have.lengthOf(0); 58 | }); 59 | }); 60 | 61 | describe('off', () => { 62 | const eventEmiter = eventEmiterBuilder(); 63 | const fn = () => {}; 64 | const fn2 = () => {}; 65 | const event = 'testEvent'; 66 | const event2 = 'testEvent2'; 67 | 68 | beforeAll(() => { 69 | eventEmiter.addListener(event, fn); 70 | eventEmiter.addListener(event, fn2); 71 | eventEmiter.addListener(event2, fn2); 72 | }); 73 | 74 | it('should have two element added for event and when remove should have one and keep other events', () => { 75 | expect(eventEmiter.listeners[event]).to.have.lengthOf(2); 76 | eventEmiter.off(event, fn); 77 | expect(eventEmiter.listeners[event]).to.have.lengthOf(1); 78 | expect(eventEmiter.listeners[event2]).to.have.lengthOf(1); 79 | }); 80 | }); 81 | 82 | describe('once', () => { 83 | const eventEmiter = eventEmiterBuilder(); 84 | const fn = () => {}; 85 | const event = 'testEvent'; 86 | 87 | beforeAll(() => { 88 | eventEmiter.once(event, fn); 89 | }); 90 | 91 | it('should have added the function and be removed after the event is emited', () => { 92 | expect(eventEmiter.listeners[event]).to.have.lengthOf(1); 93 | eventEmiter.emit(event, 'data'); 94 | expect(eventEmiter.listeners[event]).to.have.lengthOf(0); 95 | }); 96 | }); 97 | 98 | describe('emit', () => { 99 | describe('for a event with listeners', () => { 100 | const eventEmiter = eventEmiterBuilder(); 101 | const fn = sinon.spy(); 102 | const event = 'testEvent'; 103 | let result; 104 | 105 | beforeAll(() => { 106 | eventEmiter.on(event, fn); 107 | result = eventEmiter.emit(event, 'data'); 108 | }); 109 | 110 | it('should call functions', () => { 111 | expect(fn).to.have.been.calledWith('data'); 112 | }); 113 | 114 | it('should return true', () => { 115 | expect(result).to.eql(true); 116 | }); 117 | }); 118 | 119 | describe('for a event without listeners', () => { 120 | const eventEmiter = eventEmiterBuilder(); 121 | const event = 'testEvent'; 122 | let result; 123 | 124 | beforeAll(() => { 125 | result = eventEmiter.emit(event, 'data'); 126 | }); 127 | 128 | it('should return false', () => { 129 | expect(result).to.eql(false); 130 | }); 131 | }); 132 | }); 133 | 134 | describe('listenerCount', () => { 135 | const eventEmiter = eventEmiterBuilder(); 136 | const fn = () => {}; 137 | const fn2 = () => {}; 138 | const event = 'testEvent'; 139 | 140 | beforeAll(() => { 141 | eventEmiter.addListener(event, fn); 142 | eventEmiter.on(event, fn2); 143 | }); 144 | 145 | it('should return the correct number of listeners', () => { 146 | expect(eventEmiter.listenerCount(event)).to.eql(2); 147 | expect(eventEmiter.listenerCount('other')).to.eql(0); 148 | }); 149 | }); 150 | 151 | describe('rawListeners', () => { 152 | const eventEmiter = eventEmiterBuilder(); 153 | const fn = () => {}; 154 | const fn2 = () => {}; 155 | const event = 'testEvent'; 156 | const event2 = 'testEvent'; 157 | 158 | beforeAll(() => { 159 | eventEmiter.addListener(event, fn); 160 | eventEmiter.on(event2, fn2); 161 | }); 162 | 163 | it('should return all the listeners', () => { 164 | expect(eventEmiter.rawListeners(event)).to.eql(eventEmiter.listeners[event]); 165 | expect(eventEmiter.rawListeners(event2)).to.eql(eventEmiter.listeners[event2]); 166 | }); 167 | }); 168 | }); 169 | -------------------------------------------------------------------------------- /events/event-evemitter/event-emitter-proto.test.js: -------------------------------------------------------------------------------- 1 | const chai = require('chai'); 2 | const sinon = require('sinon'); 3 | const sinonChai = require('sinon-chai'); 4 | const { eventEmiterBuilder } = require('./event-emitter-proto'); 5 | 6 | const { expect } = chai; 7 | chai.use(sinonChai); 8 | describe('event-emiter', () => { 9 | describe('addListener', () => { 10 | const eventEmiter = eventEmiterBuilder(); 11 | const fn = () => {}; 12 | const event = 'testEvent'; 13 | 14 | beforeAll(() => { 15 | eventEmiter.addListener(event, fn); 16 | }); 17 | 18 | it('should have one element added for event', () => { 19 | expect(eventEmiter.listeners[event]).to.have.lengthOf(1); 20 | }); 21 | }); 22 | 23 | describe('on', () => { 24 | const eventEmiter = eventEmiterBuilder(); 25 | const fn = () => {}; 26 | const fn2 = () => {}; 27 | const event = 'testEvent'; 28 | const event2 = 'testEvent2'; 29 | 30 | beforeAll(() => { 31 | eventEmiter.on(event, fn) 32 | .on(event, fn2) 33 | .on(event2, fn2); 34 | }); 35 | 36 | it('should have two element added for event', () => { 37 | expect(eventEmiter.listeners[event]).to.have.lengthOf(2); 38 | }); 39 | 40 | it('should have one element added for an event', () => { 41 | expect(eventEmiter.listeners[event2]).to.have.lengthOf(1); 42 | }); 43 | }); 44 | 45 | describe('removeListener', () => { 46 | const eventEmiter = eventEmiterBuilder(); 47 | const fn = () => {}; 48 | const event = 'testEvent'; 49 | 50 | beforeAll(() => { 51 | eventEmiter.addListener(event, fn); 52 | }); 53 | 54 | it('should have one element added for event and when remove should have 0', () => { 55 | expect(eventEmiter.listeners[event]).to.have.lengthOf(1); 56 | eventEmiter.removeListener(event, fn); 57 | expect(eventEmiter.listeners[event]).to.have.lengthOf(0); 58 | }); 59 | }); 60 | 61 | describe('off', () => { 62 | const eventEmiter = eventEmiterBuilder(); 63 | const fn = () => {}; 64 | const fn2 = () => {}; 65 | const event = 'testEvent'; 66 | const event2 = 'testEvent2'; 67 | 68 | beforeAll(() => { 69 | eventEmiter.addListener(event, fn); 70 | eventEmiter.addListener(event, fn2); 71 | eventEmiter.addListener(event2, fn2); 72 | }); 73 | 74 | it('should have two element added for event and when remove should have one and keep other events', () => { 75 | expect(eventEmiter.listeners[event]).to.have.lengthOf(2); 76 | eventEmiter.off(event, fn); 77 | expect(eventEmiter.listeners[event]).to.have.lengthOf(1); 78 | expect(eventEmiter.listeners[event2]).to.have.lengthOf(1); 79 | }); 80 | }); 81 | 82 | describe('once', () => { 83 | const eventEmiter = eventEmiterBuilder(); 84 | const fn = () => {}; 85 | const event = 'testEvent'; 86 | 87 | beforeAll(() => { 88 | eventEmiter.once(event, fn); 89 | }); 90 | 91 | it('should have added the function and be removed after the event is emited', () => { 92 | expect(eventEmiter.listeners[event]).to.have.lengthOf(1); 93 | eventEmiter.emit(event, 'data'); 94 | expect(eventEmiter.listeners[event]).to.have.lengthOf(0); 95 | }); 96 | }); 97 | 98 | describe('emit', () => { 99 | describe('for a event with listeners', () => { 100 | const eventEmiter = eventEmiterBuilder(); 101 | const fn = sinon.spy(); 102 | const event = 'testEvent'; 103 | let result; 104 | 105 | beforeAll(() => { 106 | eventEmiter.on(event, fn); 107 | result = eventEmiter.emit(event, 'data'); 108 | }); 109 | 110 | it('should call functions', () => { 111 | expect(fn).to.have.been.calledWith('data'); 112 | }); 113 | 114 | it('should return true', () => { 115 | expect(result).to.eql(true); 116 | }); 117 | }); 118 | 119 | describe('for a event without listeners', () => { 120 | const eventEmiter = eventEmiterBuilder(); 121 | const event = 'testEvent'; 122 | let result; 123 | 124 | beforeAll(() => { 125 | result = eventEmiter.emit(event, 'data'); 126 | }); 127 | 128 | it('should return false', () => { 129 | expect(result).to.eql(false); 130 | }); 131 | }); 132 | }); 133 | 134 | describe('listenerCount', () => { 135 | const eventEmiter = eventEmiterBuilder(); 136 | const fn = () => {}; 137 | const fn2 = () => {}; 138 | const event = 'testEvent'; 139 | 140 | beforeAll(() => { 141 | eventEmiter.addListener(event, fn); 142 | eventEmiter.on(event, fn2); 143 | }); 144 | 145 | it('should return the correct number of listeners', () => { 146 | expect(eventEmiter.listenerCount(event)).to.eql(2); 147 | expect(eventEmiter.listenerCount('other')).to.eql(0); 148 | }); 149 | }); 150 | 151 | describe('rawListeners', () => { 152 | const eventEmiter = eventEmiterBuilder(); 153 | const fn = () => {}; 154 | const fn2 = () => {}; 155 | const event = 'testEvent'; 156 | const event2 = 'testEvent'; 157 | 158 | beforeAll(() => { 159 | eventEmiter.addListener(event, fn); 160 | eventEmiter.on(event2, fn2); 161 | }); 162 | 163 | it('should return all the listeners', () => { 164 | expect(eventEmiter.rawListeners(event)).to.eql(eventEmiter.listeners[event]); 165 | expect(eventEmiter.rawListeners(event2)).to.eql(eventEmiter.listeners[event2]); 166 | }); 167 | }); 168 | }); 169 | -------------------------------------------------------------------------------- /events/event-evemitter/event-emitter-class-map.test.js: -------------------------------------------------------------------------------- 1 | const chai = require('chai'); 2 | const sinon = require('sinon'); 3 | const sinonChai = require('sinon-chai'); 4 | const EventEmiter = require('./event-emitter-class-map'); 5 | 6 | const { expect } = chai; 7 | chai.use(sinonChai); 8 | describe('event-emiter', () => { 9 | describe('addListener', () => { 10 | const eventEmiter = new EventEmiter(); 11 | const fn = () => {}; 12 | const event = 'testEvent'; 13 | 14 | beforeAll(() => { 15 | eventEmiter.addListener(event, fn); 16 | }); 17 | 18 | it('should have one element added for event', () => { 19 | expect(eventEmiter.listeners.get(event)).to.have.lengthOf(1); 20 | }); 21 | }); 22 | 23 | describe('on', () => { 24 | const eventEmiter = new EventEmiter(); 25 | const fn = () => {}; 26 | const fn2 = () => {}; 27 | const event = 'testEvent'; 28 | const event2 = 'testEvent2'; 29 | 30 | beforeAll(() => { 31 | eventEmiter.on(event, fn) 32 | .on(event, fn2) 33 | .on(event2, fn2); 34 | }); 35 | 36 | it('should have two element added for event', () => { 37 | expect(eventEmiter.listeners.get(event)).to.have.lengthOf(2); 38 | }); 39 | 40 | it('should have one element added for an event', () => { 41 | expect(eventEmiter.listeners.get(event2)).to.have.lengthOf(1); 42 | }); 43 | }); 44 | 45 | describe('removeListener', () => { 46 | const eventEmiter = new EventEmiter(); 47 | const fn = () => {}; 48 | const event = 'testEvent'; 49 | 50 | beforeAll(() => { 51 | eventEmiter.addListener(event, fn); 52 | }); 53 | 54 | it('should have one element added for event and when remove should have 0', () => { 55 | expect(eventEmiter.listeners.get(event)).to.have.lengthOf(1); 56 | eventEmiter.removeListener(event, fn); 57 | expect(eventEmiter.listeners.get(event)).to.have.lengthOf(0); 58 | }); 59 | }); 60 | 61 | describe('off', () => { 62 | const eventEmiter = new EventEmiter(); 63 | const fn = () => {}; 64 | const fn2 = () => {}; 65 | const event = 'testEvent'; 66 | const event2 = 'testEvent2'; 67 | 68 | beforeAll(() => { 69 | eventEmiter.addListener(event, fn); 70 | eventEmiter.addListener(event, fn2); 71 | eventEmiter.addListener(event2, fn2); 72 | }); 73 | 74 | it('should have two element added for event and when remove should have one and keep other events', () => { 75 | expect(eventEmiter.listeners.get(event)).to.have.lengthOf(2); 76 | eventEmiter.off(event, fn); 77 | expect(eventEmiter.listeners.get(event)).to.have.lengthOf(1); 78 | expect(eventEmiter.listeners.get(event2)).to.have.lengthOf(1); 79 | }); 80 | }); 81 | 82 | describe('once', () => { 83 | const eventEmiter = new EventEmiter(); 84 | const fn = () => {}; 85 | const event = 'testEvent'; 86 | 87 | beforeAll(() => { 88 | eventEmiter.once(event, fn); 89 | }); 90 | 91 | it('should have added the function and be removed after the event is emited', () => { 92 | expect(eventEmiter.listeners.get(event)).to.have.lengthOf(1); 93 | eventEmiter.emit(event, 'data'); 94 | expect(eventEmiter.listeners.get(event)).to.have.lengthOf(0); 95 | }); 96 | }); 97 | 98 | describe('emit', () => { 99 | describe('for a event with listeners', () => { 100 | const eventEmiter = new EventEmiter(); 101 | const fn = sinon.spy(); 102 | const event = 'testEvent'; 103 | let result; 104 | 105 | beforeAll(() => { 106 | eventEmiter.on(event, fn); 107 | result = eventEmiter.emit(event, 'data'); 108 | }); 109 | 110 | it('should call functions', () => { 111 | expect(fn).to.have.been.calledWith('data'); 112 | }); 113 | 114 | it('should return true', () => { 115 | expect(result).to.eql(true); 116 | }); 117 | }); 118 | 119 | describe('for a event without listeners', () => { 120 | const eventEmiter = new EventEmiter(); 121 | const event = 'testEvent'; 122 | let result; 123 | 124 | beforeAll(() => { 125 | result = eventEmiter.emit(event, 'data'); 126 | }); 127 | 128 | it('should return false', () => { 129 | expect(result).to.eql(false); 130 | }); 131 | }); 132 | }); 133 | 134 | describe('listenerCount', () => { 135 | const eventEmiter = new EventEmiter(); 136 | const fn = () => {}; 137 | const fn2 = () => {}; 138 | const event = 'testEvent'; 139 | 140 | beforeAll(() => { 141 | eventEmiter.addListener(event, fn); 142 | eventEmiter.on(event, fn2); 143 | }); 144 | 145 | it('should return the correct number of listeners', () => { 146 | expect(eventEmiter.listenerCount(event)).to.eql(2); 147 | expect(eventEmiter.listenerCount('other')).to.eql(0); 148 | }); 149 | }); 150 | 151 | describe('rawListeners', () => { 152 | const eventEmiter = new EventEmiter(); 153 | const fn = () => {}; 154 | const fn2 = () => {}; 155 | const event = 'testEvent'; 156 | const event2 = 'testEvent'; 157 | 158 | beforeAll(() => { 159 | eventEmiter.addListener(event, fn); 160 | eventEmiter.on(event2, fn2); 161 | }); 162 | 163 | it('should return all the listeners', () => { 164 | expect(eventEmiter.rawListeners(event)).to.eql(eventEmiter.listeners.get(event)); 165 | expect(eventEmiter.rawListeners(event2)).to.eql(eventEmiter.listeners.get(event2)); 166 | }); 167 | }); 168 | }); 169 | -------------------------------------------------------------------------------- /study-guide-resource/Node.js Certification Study Guide by Hey Node_files/analytics.js: -------------------------------------------------------------------------------- 1 | (function(){/* 2 | 3 | Copyright The Closure Library Authors. 4 | SPDX-License-Identifier: Apache-2.0 5 | */ 6 | var m=this||self,n=function(a,b){a=a.split(".");var c=m;a[0]in c||"undefined"==typeof c.execScript||c.execScript("var "+a[0]);for(var d;a.length&&(d=a.shift());)a.length||void 0===b?c=c[d]&&c[d]!==Object.prototype[d]?c[d]:c[d]={}:c[d]=b};var p=function(a,b){for(var c in b)b.hasOwnProperty(c)&&(a[c]=b[c])},q=function(a){for(var b in a)if(a.hasOwnProperty(b))return!0;return!1};var r=window,t=document,u=function(a,b){t.addEventListener?t.addEventListener(a,b,!1):t.attachEvent&&t.attachEvent("on"+a,b)};var v=/^(?:(?:https?|mailto|ftp):|[^:/?#]*(?:[/?#]|$))/i;var w={},x=function(){w.TAGGING=w.TAGGING||[];w.TAGGING[1]=!0};var y=/:[0-9]+$/,A=function(a,b){b&&(b=String(b).toLowerCase());if("protocol"===b||"port"===b)a.protocol=z(a.protocol)||z(r.location.protocol);"port"===b?a.port=String(Number(a.hostname?a.port:r.location.port)||("http"==a.protocol?80:"https"==a.protocol?443:"")):"host"===b&&(a.hostname=(a.hostname||r.location.hostname).replace(y,"").toLowerCase());var c=z(a.protocol);b&&(b=String(b).toLowerCase());switch(b){case "url_no_fragment":b="";a&&a.href&&(b=a.href.indexOf("#"),b=0>b?a.href:a.href.substr(0, 7 | b));a=b;break;case "protocol":a=c;break;case "host":a=a.hostname.replace(y,"").toLowerCase();break;case "port":a=String(Number(a.port)||("http"==c?80:"https"==c?443:""));break;case "path":a.pathname||a.hostname||x();a="/"==a.pathname.substr(0,1)?a.pathname:"/"+a.pathname;a=a.split("/");a:if(b=a[a.length-1],c=[],Array.prototype.indexOf)b=c.indexOf(b),b="number"==typeof b?b:-1;else{for(var d=0;d>2;e=(e&3)<<4|g>>4;g=(g&15)<<2|h>>6;h&=63;f||(h=64,d||(g=64));b.push(D[k],D[e],D[g],D[h])}return b.join("")} 9 | function H(a){function b(k){for(;d>4);64!=g&&(c+=String.fromCharCode(e<<4&240|g>>2),64!=h&&(c+=String.fromCharCode(g<<6&192|h)))}};var I;var N=function(){var a=J,b=K,c=L(),d=function(g){a(g.target||g.srcElement||{})},f=function(g){b(g.target||g.srcElement||{})};if(!c.init){u("mousedown",d);u("keyup",d);u("submit",f);var e=HTMLFormElement.prototype.submit;HTMLFormElement.prototype.submit=function(){b(this);e.call(this)};c.init=!0}},O=function(a,b,c){for(var d=L().decorators,f={},e=0;ec;c++){for(var d=c,f=0;8>f;f++)d=d&1?d>>>1^ 12 | 3988292384:d>>>1;b[c]=d}}I=b;b=4294967295;for(c=0;c>>8^I[(b^a.charCodeAt(c))&255];return((b^-1)>>>0).toString(36)},ca=function(a){return function(b){var c=B(r.location.href),d=c.search.replace("?","");a:{var f=d.split("&");for(var e=0;ec;++c){var d=P.exec(a);if(d){var f=d;break a}a=decodeURIComponent(a)}f=void 0}if(f&&"1"===f[1]){var e=f[2],g=f[3];a:{for(f=0;f>21:b}return b};/* 20 | 21 | Copyright The Closure Library Authors. 22 | SPDX-License-Identifier: Apache-2.0 23 | */ 24 | var $c=function(a){this.w=a||[]};$c.prototype.set=function(a){this.w[a]=!0};$c.prototype.encode=function(){for(var a=[],b=0;b=b.length)wc(a,b,c);else if(8192>=b.length)x(a,b,c)||wd(a,b,c)||wc(a,b,c);else throw ge("len",b.length),new Da(b.length);},pe=function(a,b,c,d){d=d||ua;wd(a+"?"+b,"",d,c)},wc=function(a,b,c){var d=ta(a+"?"+b);d.onload=d.onerror=function(){d.onload=null;d.onerror=null;c()}},wd=function(a,b,c, 35 | d){var e=O.XMLHttpRequest;if(!e)return!1;var g=new e;if(!("withCredentials"in g))return!1;a=a.replace(/^http:/,"https:");g.open("POST",a,!0);g.withCredentials=!0;g.setRequestHeader("Content-Type","text/plain");g.onreadystatechange=function(){if(4==g.readyState){if(d)try{var ca=g.responseText;if(1>ca.length)ge("xhr","ver","0"),c();else if("1"!=ca.charAt(0))ge("xhr","ver",String(ca.length)),c();else if(3=100*R(a,Ka))throw"abort";}function Ma(a){if(G(P(a,Na)))throw"abort";}function Oa(){var a=M.location.protocol;if("http:"!=a&&"https:"!=a)throw"abort";} 38 | function Pa(a){try{O.navigator.sendBeacon?J(42):O.XMLHttpRequest&&"withCredentials"in new O.XMLHttpRequest&&J(40)}catch(c){}a.set(ld,Td(a),!0);a.set(Ac,R(a,Ac)+1);var b=[];ue.map(function(c,d){d.F&&(c=a.get(c),void 0!=c&&c!=d.defaultValue&&("boolean"==typeof c&&(c*=1),b.push(d.F+"="+K(""+c))))});!1===a.get(xe)&&b.push("npa=1");b.push("z="+Bd());a.set(Ra,b.join("&"),!0)} 39 | function Sa(a){var b=P(a,fa);!b&&a.get(Vd)&&(b="beacon");var c=P(a,gd),d=P(a,oe),e=c||(d?d+"/3":bd(!1)+"/collect");switch(P(a,ad)){case "d":e=c||(d?d+"/32":bd(!1)+"/j/collect");b=a.get(qe)||void 0;pe(e,P(a,Ra),b,a.Z(Ia));break;case "b":e=c||(d?d+"/31":bd(!1)+"/r/collect");default:b?(c=P(a,Ra),d=(d=a.Z(Ia))||ua,"image"==b?wc(e,c,d):"xhr"==b&&wd(e,c,d)||"beacon"==b&&x(e,c,d)||ba(e,c,d)):ba(e,P(a,Ra),a.Z(Ia))}e=P(a,Na);e=h(e);b=e.hitcount;e.hitcount=b?b+1:1;e=P(a,Na);delete h(e).pending_experiments; 40 | a.set(Ia,ua,!0)}function Hc(a){qc().expId&&a.set(Nc,qc().expId);qc().expVar&&a.set(Oc,qc().expVar);var b=P(a,Na);if(b=h(b).pending_experiments){var c=[];for(d in b)b.hasOwnProperty(d)&&b[d]&&c.push(encodeURIComponent(d)+"."+encodeURIComponent(b[d]));var d=c.join("!")}else d=void 0;d&&a.set(m,d,!0)}function cd(){if(O.navigator&&"preview"==O.navigator.loadPurpose)throw"abort";}function yd(a){var b=O.gaDevIds;ka(b)&&0!=b.length&&a.set("&did",b.join(","),!0)} 41 | function vb(a){if(!a.get(Na))throw"abort";};var hd=function(){return Math.round(2147483647*Math.random())},Bd=function(){try{var a=new Uint32Array(1);O.crypto.getRandomValues(a);return a[0]&2147483647}catch(b){return hd()}};function Ta(a){var b=R(a,Ua);500<=b&&J(15);var c=P(a,Va);if("transaction"!=c&&"item"!=c){c=R(a,Wa);var d=(new Date).getTime(),e=R(a,Xa);0==e&&a.set(Xa,d);e=Math.round(2*(d-e)/1E3);0=c)throw"abort";a.set(Wa,--c)}a.set(Ua,++b)};var Ya=function(){this.data=new ee};Ya.prototype.get=function(a){var b=$a(a),c=this.data.get(a);b&&void 0==c&&(c=ea(b.defaultValue)?b.defaultValue():b.defaultValue);return b&&b.Z?b.Z(this,a,c):c};var P=function(a,b){a=a.get(b);return void 0==a?"":""+a},R=function(a,b){a=a.get(b);return void 0==a||""===a?0:Number(a)};Ya.prototype.Z=function(a){return(a=this.get(a))&&ea(a)?a:ua}; 42 | Ya.prototype.set=function(a,b,c){if(a)if("object"==typeof a)for(var d in a)a.hasOwnProperty(d)&&ab(this,d,a[d],c);else ab(this,a,b,c)};var ab=function(a,b,c,d){if(void 0!=c)switch(b){case Na:wb.test(c)}var e=$a(b);e&&e.o?e.o(a,b,c,d):a.data.set(b,c,d)};var ue=new ee,ve=[],bb=function(a,b,c,d,e){this.name=a;this.F=b;this.Z=d;this.o=e;this.defaultValue=c},$a=function(a){var b=ue.get(a);if(!b)for(var c=0;c=b?!1:!0},gc=function(a){var b={};if(Ec(b)||Fc(b)){var c=b[Eb];void 0==c||Infinity==c||isNaN(c)||(0c)a[b]=void 0},Fd=function(a){return function(b){if("pageview"==b.get(Va)&&!a.I){a.I=!0;var c=aa(b),d=0a.length)J(12);else{for(var d=[],e=0;e=a&&d.push({hash:ca[0],R:e[g],O:ca})}if(0!=d.length)return 1==d.length?d[0]:Zc(b,d)||Zc(c,d)||Zc(null,d)||d[0]}function Zc(a,b){if(null==a)var c=a=1;else c=La(a),a=La(D(a,".")?a.substring(1):"."+a);for(var d=0;d=ca[0]||0>=ca[1]?"":ca.join("x");a.set(rb,c);a.set(tb,fc());a.set(ob,M.characterSet||M.charset);a.set(sb,b&&"function"===typeof b.javaEnabled&&b.javaEnabled()||!1);a.set(nb,(b&&(b.language||b.browserLanguage)||"").toLowerCase());a.data.set(ce,be("gclid",!0));a.data.set(ie,be("gclsrc",!0));a.data.set(fe, 72 | Math.round((new Date).getTime()/1E3));if(d&&a.get(cc)&&(b=M.location.hash)){b=b.split(/[?&#]+/);d=[];for(c=0;carguments.length)){if("string"===typeof arguments[0]){var b=arguments[0];var c=[].slice.call(arguments,1)}else b=arguments[0]&&arguments[0][Va],c=arguments;b&&(c=za(me[b]||[],c),c[Va]=b,this.b.set(c,void 0,!0),this.filters.D(this.b),this.b.data.m={})}};pc.prototype.ma=function(a,b){var c=this;u(a,c,b)||(v(a,function(){u(a,c,b)}),y(String(c.get(V)),a,void 0,b,!0))};var rc=function(a){if("prerender"==M.visibilityState)return!1;a();return!0},z=function(a){if(!rc(a)){J(16);var b=!1,c=function(){if(!b&&rc(a)){b=!0;var d=c,e=M;e.removeEventListener?e.removeEventListener("visibilitychange",d,!1):e.detachEvent&&e.detachEvent("onvisibilitychange",d)}};L(M,"visibilitychange",c)}};var te=/^(?:(\w+)\.)?(?:(\w+):)?(\w+)$/,sc=function(a){if(ea(a[0]))this.u=a[0];else{var b=te.exec(a[0]);null!=b&&4==b.length&&(this.c=b[1]||"t0",this.K=b[2]||"",this.methodName=b[3],this.a=[].slice.call(a,1),this.K||(this.A="create"==this.methodName,this.i="require"==this.methodName,this.g="provide"==this.methodName,this.ba="remove"==this.methodName),this.i&&(3<=this.a.length?(this.X=this.a[1],this.W=this.a[2]):this.a[1]&&(qa(this.a[1])?this.X=this.a[1]:this.W=this.a[1])));b=a[1];a=a[2];if(!this.methodName)throw"abort"; 74 | if(this.i&&(!qa(b)||""==b))throw"abort";if(this.g&&(!qa(b)||""==b||!ea(a)))throw"abort";if(ud(this.c)||ud(this.K))throw"abort";if(this.g&&"t0"!=this.c)throw"abort";}};function ud(a){return 0<=a.indexOf(".")||0<=a.indexOf(":")};var Yd,Zd,$d,A;Yd=new ee;$d=new ee;A=new ee;Zd={ec:45,ecommerce:46,linkid:47}; 75 | var u=function(a,b,c){b==N||b.get(V);var d=Yd.get(a);if(!ea(d))return!1;b.plugins_=b.plugins_||new ee;if(b.plugins_.get(a))return!0;b.plugins_.set(a,new d(b,c||{}));return!0},y=function(a,b,c,d,e){if(!ea(Yd.get(b))&&!$d.get(b)){Zd.hasOwnProperty(b)&&J(Zd[b]);a=N.j(a);if(p.test(b)){J(52);if(!a)return!0;c=d||{};d={id:b,B:c.dataLayer||"dataLayer",ia:!!a.get("anonymizeIp"),sync:e,G:!1};a.get(">m")==b&&(d.G=!0);var g=String(a.get("name"));"t0"!=g&&(d.target=g);G(String(a.get("trackingId")))||(d.clientId= 76 | String(a.get(Q)),d.ka=Number(a.get(n)),c=c.palindrome?r:q,c=(c=M.cookie.replace(/^|(; +)/g,";").match(c))?c.sort().join("").substring(1):void 0,d.la=c,d.qa=E(a.b.get(kb)||"","gclid"));c=d.B;g=(new Date).getTime();O[c]=O[c]||[];g={"gtm.start":g};e||(g.event="gtm.js");O[c].push(g);c=t(d)}!c&&Zd.hasOwnProperty(b)?(J(39),c=b+".js"):J(43);if(c){if(a){var ca=a.get(oe);qa(ca)||(ca=void 0)}c&&0<=c.indexOf("/")||(c=(ca?ca+"/34":bd(!1)+"/plugins/ua/")+c);ca=ae(c);a=ca.protocol;d=M.location.protocol;if(("https:"== 77 | a||a==d||("http:"!=a?0:"http:"==d))&&B(ca)){if(ca=ca.url)a=(a=M.querySelector&&M.querySelector("script[nonce]")||null)?a.nonce||a.getAttribute&&a.getAttribute("nonce")||"":"",e?(e="",a&&Nd.test(a)&&(e=' nonce="'+a+'"'),f.test(ca)&&M.write("\x3c/script>')):(e=M.createElement("script"),e.type="text/javascript",e.async=!0,e.src=ca,a&&e.setAttribute("nonce",a),ca=M.getElementsByTagName("script")[0],ca.parentNode.insertBefore(e,ca));$d.set(b,!0)}}}},v=function(a,b){var c=A.get(a)|| 78 | [];c.push(b);A.set(a,c)},C=function(a,b){Yd.set(a,b);b=A.get(a)||[];for(var c=0;ca.split("/")[0].indexOf(":")&&(a=ca+e[2].substring(0,e[2].lastIndexOf("/"))+"/"+a);c.href=a; 80 | d=b(c);return{protocol:(c.protocol||"").toLowerCase(),host:d[0],port:d[1],path:d[2],query:c.search||"",url:a||""}};var Z={ga:function(){Z.f=[]}};Z.ga();Z.D=function(a){var b=Z.J.apply(Z,arguments);b=Z.f.concat(b);for(Z.f=[];0c;c++){var d=b[c].src;if(d&&0==d.indexOf(bd(!0)+ 83 | "/analytics")){b=!0;break a}}b=!1}b&&(Ba=!0)}(O.gaplugins=O.gaplugins||{}).Linker=Dc;b=Dc.prototype;C("linker",Dc);X("decorate",b,b.ca,20);X("autoLink",b,b.S,25);C("displayfeatures",fd);C("adfeatures",fd);a=a&&a.q;ka(a)?Z.D.apply(N,a):J(50)}};N.da=function(){for(var a=N.getAll(),b=0;bb)a=0,b=2147483647;return Math.floor(Math.random()*(b-a+1)+a)},ya=function(a,b){for(var c=new xa,d=0;dc.length&&d&&b.push(c)});return b.join(",")};/* 139 | jQuery v1.9.1 (c) 2005, 2012 jQuery Foundation, Inc. jquery.org/license. */ 140 | var Ma=/\[object (Boolean|Number|String|Function|Array|Date|RegExp)\]/,Na=function(a){if(null==a)return String(a);var b=Ma.exec(Object.prototype.toString.call(Object(a)));return b?b[1].toLowerCase():"object"},Oa=function(a,b){return Object.prototype.hasOwnProperty.call(Object(a),b)},Pa=function(a){if(!a||"object"!=Na(a)||a.nodeType||a==a.window)return!1;try{if(a.constructor&&!Oa(a,"constructor")&&!Oa(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}for(var b in a);return void 0=== 141 | b||Oa(a,b)},D=function(a,b){var c=b||("array"==Na(a)?[]:{}),d;for(d in a)if(Oa(a,d)){var e=a[d];"array"==Na(e)?("array"!=Na(c[d])&&(c[d]=[]),c[d]=D(e,c[d])):Pa(e)?(Pa(c[d])||(c[d]={}),c[d]=D(e,c[d])):c[d]=e}return c};var ob; 142 | var pb=[],qb=[],rb=[],sb=[],tb=[],ub={},vb,xb,yb,zb=function(a,b){var c={};c["function"]="__"+a;for(var d in b)b.hasOwnProperty(d)&&(c["vtp_"+d]=b[d]);return c},Ab=function(a,b){var c=a["function"];if(!c)throw Error("Error: No function name given for function call.");var d=ub[c],e={},f;for(f in a)a.hasOwnProperty(f)&&0===f.indexOf("vtp_")&&(e[void 0!==d?f:f.substr(4)]=a[f]);return void 0!==d?d(e):ob(c,e,b)},Cb=function(a,b,c){c=c||[];var d={},e;for(e in a)a.hasOwnProperty(e)&&(d[e]=Bb(a[e],b,c)); 143 | return d},Db=function(a){var b=a["function"];if(!b)throw"Error: No function name given for function call.";var c=ub[b];return c?c.priorityOverride||0:0},Bb=function(a,b,c){if(ra(a)){var d;switch(a[0]){case "function_id":return a[1];case "list":d=[];for(var e=1;e"+a+"";b=b.lastChild;for(var c=[];b.firstChild;)c.push(b.removeChild(b.firstChild));return c},rc=function(a,b,c){c=c||100;for(var d={},e=0;eb)){var c=a.substring(0,b);if(Qc.test(c)){for(var d=a.substring(b+1).split("/"),e=0;e=vd--?(jd("GTM",1),td[pd]=!0):(ud.Xg(),lc(wd()),qd[pd]=!0,xd=yd=sd=rd=""))},wd=function(){var a=pd;if(void 0===a)return"";var b=kd("GTM"),c=kd("TAGGING");return[zd,qd[a]?"":"&es=1",Ad[a],b?"&u="+b:"",c?"&ut="+c:"",ld(),rd,sd,yd,xd,"&z=0"].join("")},Bd=function(){return[ed,"&v=3&t=t", 156 | "&pid="+wa(),"&rv="+Vc.Cb].join("")},Cd="0.005000">Math.random(),zd=Bd(),Dd=function(){zd=Bd()},qd={},rd="",sd="",xd="",yd="",pd=void 0,Ad={},td={},md=void 0,ud=function(a,b){var c=0,d=0;return{wg:function(){if(c=b&&(c=0);return c>=a},Xg:function(){Ea()-d>=b&&(c=0);c++;d=Ea()}}}(2,1E3),vd=1E3,Ed=function(a,b){if(Cd&&!td[a]&&pd!==a){nd();pd=a;xd=rd="";var c;c=0===b.indexOf("gtm.")?encodeURIComponent(b):"*";Ad[a]="&e="+c+"&eid="+a;od()}},Fd=function(a,b,c){if(Cd&& 157 | !td[a]&&b){a!==pd&&(nd(),pd=a);var d,e=String(b[Fb.sa]||"").replace(/_/g,"");0===e.indexOf("cvt")&&(e="cvt");d=e;var f=c+d;rd=rd?rd+"."+f:"&tr="+f;var h=b["function"];if(!h)throw Error("Error: No function name given for function call.");var k=(ub[h]?"1":"2")+d;xd=xd?xd+"."+k:"&ti="+k;od();2022<=wd().length&&nd()}},Gd=function(a,b,c){if(Cd&&!td[a]){a!==pd&&(nd(),pd=a);var d=c+b;sd= 158 | sd?sd+"."+d:"&epr="+d;od();2022<=wd().length&&nd()}};var Hd={},Id=new xa,Jd={},Kd={},Nd={name:"dataLayer",set:function(a,b){D(Ka(a,b),Jd);Ld()},get:function(a){return Md(a,2)},reset:function(){Id=new xa;Jd={};Ld()}},Md=function(a,b){if(2!=b){var c=Id.get(a);if(Cd){var d=Od(a);c!==d&&jd("GTM",5)}return c}return Od(a)},Od=function(a,b,c){var d=a.split("."),e=!1,f=void 0;return e?f:Qd(d)},Qd=function(a){for(var b=Jd,c=0;ck;k++){var l=h[k].src;if(l){l=l.toLowerCase();if(0===l.indexOf(e)){b=3;break a}1===f&&0===l.indexOf(d)&&(f=2)}}b=f}else b=a;return b};var le=new RegExp(/^(.*\.)?(google|youtube|blogger|withgoogle)(\.com?)?(\.[a-z]{2})?\.?$/),me={cl:["ecl"],customPixels:["nonGooglePixels"],ecl:["cl"],ehl:["hl"],hl:["ehl"],html:["customScripts","customPixels","nonGooglePixels","nonGoogleScripts","nonGoogleIframes"],customScripts:["html","customPixels","nonGooglePixels","nonGoogleScripts","nonGoogleIframes"],nonGooglePixels:[],nonGoogleScripts:["nonGooglePixels"],nonGoogleIframes:["nonGooglePixels"]},ne={cl:["ecl"],customPixels:["customScripts","html"], 160 | ecl:["cl"],ehl:["hl"],hl:["ehl"],html:["customScripts"],customScripts:["html"],nonGooglePixels:["customPixels","customScripts","html","nonGoogleScripts","nonGoogleIframes"],nonGoogleScripts:["customScripts","html"],nonGoogleIframes:["customScripts","html","nonGoogleScripts"]},oe="google customPixels customScripts html nonGooglePixels nonGoogleScripts nonGoogleIframes".split(" "); 161 | var qe=function(a){var b=Md("gtm.whitelist");b&&jd("GTM",9);var c=b&&Ja(Ba(b),me),d=Md("gtm.blacklist");d||(d=Md("tagTypeBlacklist"))&&jd("GTM",3); 162 | d?jd("GTM",8):d=[];pe()&&(d=Ba(d),d.push("nonGooglePixels","nonGoogleScripts","sandboxedScripts"));0<=q(Ba(d),"google")&&jd("GTM",2);var e=d&&Ja(Ba(d),ne),f={};return function(h){var k=h&&h[Fb.sa];if(!k||"string"!=typeof k)return!0;k=k.replace(/^_*/,"");if(void 0!==f[k])return f[k];var l=gd[k]||[],m=a(k,l);if(b){var n;if(n=m)a:{if(0>q(c,k))if(l&&0q(c,l[r])){jd("GTM",11);n=!1;break a}}else{n=!1;break a}n=!0}m=n}var t=!1;if(d){var p=0<=q(e,k);if(p)t=p;else{var u=ya(e,l||[]);u&&jd("GTM",10);t=u}}var v=!m||t;v||!(0<=q(l,"sandboxedScripts"))||c&&-1!==q(c,"sandboxedScripts")||(v=ya(e,oe));return f[k]=v}},pe=function(){return le.test(F.location&&F.location.hostname)};var re={Tf:function(a,b){b[Fb.vd]&&"string"===typeof a&&(a=1==b[Fb.vd]?a.toLowerCase():a.toUpperCase());b.hasOwnProperty(Fb.xd)&&null===a&&(a=b[Fb.xd]);b.hasOwnProperty(Fb.zd)&&void 0===a&&(a=b[Fb.zd]);b.hasOwnProperty(Fb.yd)&&!0===a&&(a=b[Fb.yd]);b.hasOwnProperty(Fb.wd)&&!1===a&&(a=b[Fb.wd]);return a}};var se={active:!0,isWhitelisted:function(){return!0}},te=function(a){var b=Wc.zones;!b&&a&&(b=Wc.zones=a());return b};var ue=function(){};var ve=!1,we=0,xe=[];function ye(a){if(!ve){var b=G.createEventObject,c="complete"==G.readyState,d="interactive"==G.readyState;if(!a||"readystatechange"!=a.type||c||!b&&d){ve=!0;for(var e=0;ewe){we++;try{G.documentElement.doScroll("left"),ye()}catch(a){F.setTimeout(ze,50)}}}var Ae=function(a){ve?a():xe.push(a)};var Be={},Ce={},De=function(a,b,c,d){if(!Ce[a]||Yc[b]||"__zone"===b)return-1;var e={};Pa(d)&&(e=D(d,e));e.id=c;e.status="timeout";return Ce[a].tags.push(e)-1},Ee=function(a,b,c,d){if(Ce[a]){var e=Ce[a].tags[b];e&&(e.status=c,e.executionTime=d)}};function Fe(a){for(var b=Be[a]||[],c=0;c=c&&Fe(a)})},Ff:function(){d=!0;b>=c&&Fe(a)}}};var Je=function(){function a(d){return!qa(d)||0>d?0:d}if(!Wc._li&&F.performance&&F.performance.timing){var b=F.performance.timing.navigationStart,c=qa(Nd.get("gtm.start"))?Nd.get("gtm.start"):0;Wc._li={cst:a(c-b),cbt:a(cd-b)}}};var Ne={},Oe=function(){return F.GoogleAnalyticsObject&&F[F.GoogleAnalyticsObject]},Pe=!1; 165 | var Qe=function(a){F.GoogleAnalyticsObject||(F.GoogleAnalyticsObject=a||"ga");var b=F.GoogleAnalyticsObject;if(F[b])F.hasOwnProperty(b)||jd("GTM",12);else{var c=function(){c.q=c.q||[];c.q.push(arguments)};c.l=Number(new Date);F[b]=c}Je();return F[b]},Re=function(a,b,c,d){b=String(b).replace(/\s+/g,"").split(",");var e=Oe();e(a+"require","linker");e(a+"linker:autoLink",b,c,d)}; 166 | var Te=function(a){},Se=function(){return F.GoogleAnalyticsObject||"ga"};var Ve=/^(?:(?:https?|mailto|ftp):|[^:/?#]*(?:[/?#]|$))/i;var We=/:[0-9]+$/,Xe=function(a,b,c){for(var d=a.split("&"),e=0;ec?a.href:a.href.substr(0,c)}return b}, 169 | af=function(a){var b=G.createElement("a");a&&(b.href=a);var c=b.pathname;"/"!==c[0]&&(a||jd("TAGGING",1),c="/"+c);var d=b.hostname.replace(We,"");return{href:b.href,protocol:b.protocol,host:b.host,hostname:d,pathname:c,search:b.search,hash:b.hash,port:b.port}};function ff(a,b,c,d){var e=sb[a],f=gf(a,b,c,d);if(!f)return null;var h=Bb(e[Fb.Pd],c,[]);if(h&&h.length){var k=h[0];f=ff(k.index,{B:f,w:1===k.ke?b.terminate:f,terminate:b.terminate},c,d)}return f} 170 | function gf(a,b,c,d){function e(){if(f[Fb.af])k();else{var w=Cb(f,c,[]),y=De(c.id,String(f[Fb.sa]),Number(f[Fb.Rd]),w[Fb.bf]),x=!1;w.vtp_gtmOnSuccess=function(){if(!x){x=!0;var A=Ea()-z;Fd(c.id,sb[a],"5");Ee(c.id,y,"success",A);h()}};w.vtp_gtmOnFailure=function(){if(!x){x=!0;var A=Ea()-z;Fd(c.id,sb[a],"6");Ee(c.id,y,"failure",A);k()}};w.vtp_gtmTagId=f.tag_id; 171 | w.vtp_gtmEventId=c.id;Fd(c.id,f,"1");var B=function(){var A=Ea()-z;Fd(c.id,f,"7");Ee(c.id,y,"exception",A);x||(x=!0,k())};var z=Ea();try{Ab(w,c)}catch(A){B(A)}}}var f=sb[a],h=b.B,k=b.w,l=b.terminate;if(c.Qc(f))return null;var m=Bb(f[Fb.Sd],c,[]);if(m&&m.length){var n=m[0],r=ff(n.index,{B:h,w:k,terminate:l},c,d);if(!r)return null;h=r;k=2===n.ke?l:r}if(f[Fb.Hd]||f[Fb.ff]){var t=f[Fb.Hd]?tb:c.gh,p=h,u=k;if(!t[a]){e=Ga(e);var v=hf(a,t,e);h=v.B;k=v.w}return function(){t[a](p,u)}}return e} 172 | function hf(a,b,c){var d=[],e=[];b[a]=jf(d,e,c);return{B:function(){b[a]=kf;for(var f=0;fe?1:dk?1:h>2,m=(f&3)<<4|h>>4,n=(h&15)<<2|k>>6,r=k&63;e||(r=64,d||(n=64));b.push(Rf[l],Rf[m],Rf[n],Rf[r])}return b.join("")} 183 | function Vf(a){function b(l){for(;d>4);64!=h&&(c+=String.fromCharCode(f<<4&240|h>>2),64!=k&&(c+=String.fromCharCode(h<<6&192|k)))}};var Wf;var $f=function(){var a=Xf,b=Yf,c=Zf(),d=function(h){a(h.target||h.srcElement||{})},e=function(h){b(h.target||h.srcElement||{})};if(!c.init){mc(G,"mousedown",d);mc(G,"keyup",d);mc(G,"submit",e);var f=HTMLFormElement.prototype.submit;HTMLFormElement.prototype.submit=function(){b(this);f.call(this)};c.init=!0}},ag=function(a,b,c,d,e){var f={callback:a,domains:b,fragment:2===c,placement:c,forms:d,sameHost:e};Zf().decorators.push(f)},cg=function(a,b,c){for(var d=Zf().decorators,e={},f=0;ff;f++){for(var h=f,k=0;8>k;k++)h= 186 | h&1?h>>>1^3988292384:h>>>1;e[f]=h}d=e}Wf=d;for(var l=4294967295,m=0;m>>8^Wf[(l^c.charCodeAt(m))&255];return((l^-1)>>>0).toString(36)},lg=function(){return function(a){var b=af(F.location.href),c=b.search.replace("?",""),d=Xe(c,"_gl",!0)||"";a.query=kg(d)||{};var e=$e(b,"fragment").match(hg("_gl"));a.fragment=kg(e&&e[3]||"")||{}}},mg=function(){var a=lg(),b=Zf();b.data||(b.data={query:{},fragment:{}},a(b.data));var c={},d=b.data;d&&(Ha(c,d.query),Ha(c,d.fragment));return c},kg=function(a){var b; 187 | b=void 0===b?3:b;try{if(a){var c;a:{for(var d=a,e=0;3>e;++e){var f=dg.exec(d);if(f){c=f;break a}d=decodeURIComponent(d)}c=void 0}var h=c;if(h&&"1"===h[1]){var k=h[3],l;a:{for(var m=h[2],n=0;np){t=!0;break b}t=!1}t||Nf(m,n,c,d,0==e?void 0:new Date(r+1E3*(null==e?7776E3:e)),!0)}}}var w={prefix:b,path:c,domain:d};Dg(Bg(f.gclid,f.gclsrc),w)},Fg=function(a,b){var c=zg[a];if(void 0!==c)return b+c},Gg=function(a){var b=a.split(".");return 3!==b.length||"GCL"!==b[0]?0:1E3*(Number(b[1])|| 198 | 0)};function Ig(a){var b=a.split(".");if(3==b.length&&"GCL"==b[0]&&b[1])return b[2]} 199 | var Jg=function(a,b,c,d,e){if(ra(b)){var f=Ag(e);rg(function(){for(var h={},k=0;k>21:d;return[Math.round(2147483647*Math.random())^d&2147483647,Math.round(Ea()/1E3)].join(".")},dh=function(a,b,c,d){var e=bh(b);return Jf(a,e,ch(c),d)},eh=function(a,b,c,d){var e=""+bh(c),f=ch(d);1=Number(c);case "_gt":return Number(b)>Number(c);case "_lc":var n;n=String(b).split(",");return 0<=q(n,String(c));case "_le":return Number(b)<=Number(c);case "_lt":return Number(b)=f)f=2E3;var h=c.vtp_uniqueTriggerId||"0";if(d){var k=function(m){return Math.max(f,m)};wj("lcl","mwt",k,0);e||wj("lcl","nv.mwt",k,0)}var l=function(m){m.push(h);return m};wj("lcl","ids",l,[]);e||wj("lcl","nv.ids",l,[]);jk("lcl")||(a(),kk("lcl"));I(c.vtp_gtmOnSuccess)})}(); 280 | 281 | var Dm={};Dm.macro=function(a){if(qj.Cc.hasOwnProperty(a))return qj.Cc[a]},Dm.onHtmlSuccess=qj.fe(!0),Dm.onHtmlFailure=qj.fe(!1);Dm.dataLayer=Nd;Dm.callback=function(a){fd.hasOwnProperty(a)&&pa(fd[a])&&fd[a]();delete fd[a]};function Em(){Wc[Vc.s]=Dm;Ha(gd,Z.a);xb=xb||qj;yb=re} 282 | function Fm(){vg.gtm_3pds=!0;Wc=F.google_tag_manager=F.google_tag_manager||{};if(Wc[Vc.s]){var a=Wc.zones;a&&a.unregisterChild(Vc.s)}else{for(var b=data.resource||{},c=b.macros||[],d=0;d