├── Chapter01 ├── benchmark.html ├── chrome_performance.html ├── chrome_rendering.html ├── chrome_rendering_deferred.html ├── fake_library.js ├── jsperf_code.html ├── main.css └── shader_editor.html ├── Chapter02 ├── currying.js ├── generator.js ├── immutable.html ├── immutable.min.js ├── not_deep_copy.html ├── null_resources.html ├── safe_mutable.html ├── safe_state_module.js └── todo_redux.html ├── Chapter03 ├── arrow_functions.html ├── collections.html ├── custom_tooltip.html ├── dom.html ├── fetch.html ├── lib.js ├── lib2.js ├── modules.html ├── node │ ├── app.js │ └── server.js ├── other_changes.html ├── prototype_and_classes.html ├── reflection_proxy.html ├── server.js └── variables.html ├── Chapter04 ├── todo │ ├── public │ │ └── global.css │ └── src │ │ ├── App.svelte │ │ ├── Todo.svelte │ │ ├── main.js │ │ ├── stores.js │ │ └── todo_store.js └── weather │ ├── public │ └── global.css │ └── src │ ├── App.svelte │ ├── Dropdown.svelte │ ├── WeatherInput.svelte │ ├── WeatherOutput.svelte │ ├── main.js │ └── stores.js ├── Chapter05 ├── bad_code.js ├── child.js ├── custom_transform.js ├── example.js ├── example.txt ├── first_fs.js ├── http_get.js ├── http_server.js ├── import_script.js ├── main.js ├── named_pipe.js ├── named_pipe_child.js ├── package.json ├── parent_child_pipe.js ├── read_file_stream.js └── require_script.js ├── Chapter06 ├── cluster │ ├── main.js │ └── package.json ├── datagram │ ├── client.js │ ├── main.js │ ├── package.json │ └── price.js ├── http2server │ ├── basic.js │ ├── package.json │ ├── server.crt.pem │ ├── server.js │ ├── server.key.pem │ └── static │ │ ├── index.html │ │ ├── main.css │ │ └── main.js ├── local │ ├── cache.js │ ├── helper.js │ ├── main.js │ ├── package.json │ └── send.js ├── main_worker.js ├── package.json ├── quic │ ├── client.js │ ├── main.js │ └── package.json └── worker.js ├── Chapter07 ├── batch │ ├── count.js │ ├── input.txt │ ├── main.js │ ├── output.txt │ ├── package.json │ └── simple_readable.js ├── duplex │ ├── main.js │ └── package.json ├── generators │ ├── generators.js │ ├── package.json │ └── temp.txt ├── harness │ ├── client.js │ ├── output.txt │ ├── package.json │ ├── server.js │ └── writable_server.js ├── readable │ ├── main.js │ ├── output.txt │ └── package.json ├── transform │ ├── main.js │ └── package.json └── writable │ ├── main.js │ └── package.json ├── Chapter08 ├── json │ ├── jsonformat.json │ ├── main.js │ └── package.json ├── messagepack │ ├── main.js │ └── package.json ├── protobuf │ ├── main.js │ ├── package.json │ └── test.proto └── schema │ ├── decoder.js │ ├── encoder.js │ ├── example.js │ ├── helper.js │ ├── main.js │ ├── package.json │ ├── test.json │ └── test2.json ├── Chapter09 └── microserve │ ├── cache.js │ ├── main.js │ ├── package.json │ ├── publish │ ├── first.md │ ├── further │ │ └── three.md │ └── second.md │ ├── template.js │ └── template │ ├── css │ └── main.css │ ├── html │ ├── footer.html │ ├── header.html │ └── sidebar.html │ └── main.html ├── Chapter10 ├── cache.html ├── cache_shared.js ├── largeObject.js ├── mock_customer_data.js ├── shared.js ├── test.js ├── worker.html ├── worker.js └── worker_to_shared.js ├── Chapter11 ├── app.js ├── offline_storage │ ├── OfflineCacheWorker.js │ ├── index.html │ ├── interactions.js │ └── main.css └── source │ ├── BaseServiceWorker.js │ ├── CacheServiceWorker.js │ ├── handlers.js │ ├── index.html │ ├── interactions.js │ ├── main.css │ └── row.template ├── Chapter12 ├── .circleci │ └── config.yml ├── .gitignore ├── LICENSE ├── build │ ├── rollup.config.js │ └── rollup.sass.config.js ├── cache.js ├── main-sass.js ├── main.js ├── package.json ├── publish │ ├── first.md │ ├── further │ │ └── three.md │ └── second.md ├── template.js ├── template │ ├── html │ │ ├── footer.html │ │ ├── header.html │ │ └── sidebar.html │ ├── main.html │ └── stylesheets │ │ └── main.scss └── test │ ├── cache.tester.js │ ├── test_cache.txt │ └── tests.test.js ├── Chapter13 ├── c_index.html ├── extern.c ├── external.js ├── first.wat ├── fizzbuzz.c ├── hamming.c ├── hello_world.c ├── index.html ├── math.wat ├── sharing_resources.wat ├── sqltest │ └── index.html └── useless.wat ├── LICENSE ├── README.md └── newfile1.txt /Chapter01/benchmark.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |

Benchmark Code

9 | 10 | 54 | 55 | -------------------------------------------------------------------------------- /Chapter01/chrome_performance.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 23 | 24 | 150 | 151 | 152 | -------------------------------------------------------------------------------- /Chapter01/chrome_rendering.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 18 | 19 | 20 |
21 |
22 | 23 | 24 | 93 | -------------------------------------------------------------------------------- /Chapter01/chrome_rendering_deferred.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 35 | 36 | 37 | 38 | 39 | 77 | -------------------------------------------------------------------------------- /Chapter01/fake_library.js: -------------------------------------------------------------------------------- 1 | window.Library = { 2 | sum : function() { 3 | let sum = 0; 4 | for(let i = 0; i < 10000; i++) { 5 | sum += i; 6 | } 7 | return sum; 8 | }, 9 | unusedItem : { 10 | item : 'this' 11 | }, 12 | outerFun : function(run=false) { 13 | const innerFun = () => { 14 | let _i = Object.assign({}, this.unusedItem); 15 | let sumSum = 0; 16 | for(let i = 0; i < 10; i++) { 17 | sumSum += this.sum(); 18 | } 19 | return sumSum; 20 | } 21 | if( run ) { 22 | return innerFun(); 23 | } else { 24 | return 'nothin to run'; 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /Chapter01/jsperf_code.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 28 | -------------------------------------------------------------------------------- /Chapter01/main.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin : 0; 3 | } -------------------------------------------------------------------------------- /Chapter01/shader_editor.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 13 | 20 | 111 | -------------------------------------------------------------------------------- /Chapter02/currying.js: -------------------------------------------------------------------------------- 1 | const add = function(a, b) { 2 | return function(b) { 3 | return a + b; 4 | } 5 | } 6 | 7 | console.log(add(2)(5), 'this will be 7'); 8 | const add5 = add(5); 9 | console.log(add5(5), 'this will be 10'); 10 | 11 | const fullFun = function(a, b, c) { 12 | console.log('a', a); 13 | console.log('b', b); 14 | console.log('c', c); 15 | } 16 | 17 | const tempFun = fullFun.bind(null, 2); 18 | setTimeout(() => { 19 | const temp2Fun = tempFun.bind(null, 3); 20 | setTimeout(() => { 21 | const temp3Fun = temp2Fun.bind(null, 5); 22 | setTimeout(() => { 23 | console.log('temp3Fun'); 24 | temp3Fun(); 25 | }, 1000); 26 | }, 1000); 27 | console.log('temp2Fun'); 28 | temp2Fun(5) 29 | }, 1000); 30 | console.log('tempFun'); 31 | tempFun(3, 5); 32 | 33 | const calculateArbitraryValueWithPrecision = function(prec=0, val) { 34 | return function(val) { 35 | return parseFloat((val / 1000).toFixed(prec)); 36 | } 37 | } 38 | 39 | const arr = new Array(50000); 40 | for(let i = 0; i < arr.length; i++) { 41 | arr[i] = i + 1000; 42 | } 43 | 44 | console.log(arr.map(calculateArbitraryValueWithPrecision(2))); -------------------------------------------------------------------------------- /Chapter02/generator.js: -------------------------------------------------------------------------------- 1 | const simpleGenerator = function* () { 2 | let it = 0; 3 | for(;;) { 4 | yield it; 5 | it++; 6 | } 7 | } 8 | 9 | const sg = simpleGenerator(); 10 | for(let i = 0; i < 10; i++) { 11 | console.log(sg.next().value); 12 | } 13 | sg.return(); 14 | 15 | console.log(sg.next().value); 16 | 17 | const timing = function*(time) { 18 | yield Date.now() - time; 19 | } 20 | 21 | const time = timing(Date.now()); 22 | let sum = 0; 23 | for(let i = 0; i < 1000000; i++) { 24 | sum = sum + i; 25 | } 26 | console.log(time.next().value); 27 | 28 | const nums = function*(fn=null) { 29 | let i = 0; 30 | for(;;) { 31 | yield i; 32 | if( fn ) { 33 | i += fn(i); 34 | } else { 35 | i += 1; 36 | } 37 | } 38 | } 39 | 40 | const data = []; 41 | for(let i in nums()) { 42 | if( i > 100 ) { 43 | break; 44 | } 45 | data.push(i); 46 | } 47 | 48 | console.log('data', data); 49 | 50 | const nums = function*(fn=null) { 51 | let i = 0; 52 | for(;;) { 53 | yield i; 54 | if( fn ) { 55 | i += fn(i); 56 | } else { 57 | i += 1; 58 | } 59 | } 60 | } 61 | 62 | const data = []; 63 | const gen = nums(); 64 | for(let i of gen) { 65 | console.log(i); 66 | if( i > 100 ) { 67 | break; 68 | } 69 | data.push(i); 70 | } 71 | 72 | const fakestream = function*(data) { 73 | const chunkSize = 10; 74 | const dataLength = data.length; 75 | let i = 0; 76 | while(i < dataLength ) { 77 | const outData = []; 78 | for(let j = 0; j < chunkSize; j++) { 79 | outData.push(data[i]); 80 | i+=1; 81 | } 82 | yield outData; 83 | } 84 | } 85 | 86 | for(let i of fakestream(data)) { 87 | console.log(i); 88 | } 89 | 90 | const trampoline = fun => { 91 | return (...arguments) => { 92 | let result = fun(...arguments); 93 | while( typeof result === 'function' ) { 94 | result = result(); 95 | } 96 | 97 | return result; 98 | } 99 | } 100 | 101 | const _d = new Array(100000); 102 | for(let i = 0; i < _d.length; i++) { 103 | _d[i] = i; 104 | } 105 | 106 | const recurseSummer = function(data, sum=0) { 107 | if(!data.length) { 108 | return sum; 109 | } 110 | return () => recurseSummer(data.slice(1), sum + data[0]); 111 | } 112 | 113 | const recurseFilter = function(data, con, filtered=[]) { 114 | if(!data.length) { 115 | return filtered; 116 | } 117 | return () => recurseFilter(data.slice(1),con, con(data[0]) ? filtered.length ? new Array(...filtered, data[0]) : [data[0]] : filtered); 118 | } 119 | 120 | const finalFilter = trampoline(recurseFilter); 121 | console.log(finalFilter(_d, item => item % 2 === 0)); -------------------------------------------------------------------------------- /Chapter02/immutable.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 100 | 101 | -------------------------------------------------------------------------------- /Chapter02/not_deep_copy.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 30 | 31 | -------------------------------------------------------------------------------- /Chapter02/null_resources.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
7 |

The hashed value is:

8 | 9 |
10 | 57 | 58 | -------------------------------------------------------------------------------- /Chapter02/safe_mutable.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 156 | 157 | -------------------------------------------------------------------------------- /Chapter02/safe_state_module.js: -------------------------------------------------------------------------------- 1 | const state = {}; 2 | 3 | export default updateState = function(update) { 4 | const x = Object.keys(update); 5 | for(let i = 0; i < x.length; i++) { 6 | state[x[i]] = update[x[i]]; 7 | } 8 | } -------------------------------------------------------------------------------- /Chapter03/arrow_functions.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 20 | 21 | -------------------------------------------------------------------------------- /Chapter03/collections.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 80 | 81 | -------------------------------------------------------------------------------- /Chapter03/custom_tooltip.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 26 | 27 | 28 | That was a successful operation! 29 | 30 | 81 | 82 | -------------------------------------------------------------------------------- /Chapter03/dom.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 14 | 15 | 16 |

This is a paragraph element

17 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 |
33 | 93 | 94 | -------------------------------------------------------------------------------- /Chapter03/fetch.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 6 | 85 | 86 | -------------------------------------------------------------------------------- /Chapter03/lib.js: -------------------------------------------------------------------------------- 1 | const exports = { 2 | this : 'that', 3 | that : 'this' 4 | } 5 | 6 | export { exports as Item }; 7 | 8 | export default function() { 9 | console.log('this is going to be our simple lib'); 10 | } -------------------------------------------------------------------------------- /Chapter03/lib2.js: -------------------------------------------------------------------------------- 1 | export default function() { 2 | console.log('this is going to be loaded after the initial call'); 3 | } -------------------------------------------------------------------------------- /Chapter03/modules.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 28 | 29 | -------------------------------------------------------------------------------- /Chapter03/node/app.js: -------------------------------------------------------------------------------- 1 | const _static = require('node-static'); 2 | 3 | const serve = new _static.Server('./files'); 4 | require('http').createServer((req, res) => { 5 | req.addListener('end', () => { 6 | serve.serve(req, res); 7 | }).resume(); 8 | }).listen(8080); 9 | -------------------------------------------------------------------------------- /Chapter03/node/server.js: -------------------------------------------------------------------------------- 1 | const http = require('http'); 2 | 3 | const erver = http.createServer((req, res) => { 4 | console.log(req.url, req.method); 5 | res.setHeader('Access-Control-Allow-Origin', "*"); 6 | res.setHeader('Access-Control-Allow-Credentials', "true"); 7 | res.setHeader('Access-Control-Allow-Methods', "GET,HEAD,OPTIONS"); 8 | res.setHeader('Access-Control-Allow-Headers', "Access-Control-Allow-Origin, Access-Control-Allow-Headers, Origin,Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers, Accept") 9 | if( req.method === "GET" ) { 10 | switch(req.url) { 11 | case '/sample': { 12 | res.write(Buffer.from(JSON.stringify({what : 'this'}))); 13 | res.end(); 14 | break; 15 | } 16 | case '/rot': { 17 | res.write(Buffer.from("ABCDEFGHIJKLMNOPQRSTUVWXYZ")); 18 | res.end(); 19 | break; 20 | } 21 | case '/longload': { 22 | setTimeout(() => { 23 | res.write(Buffer.from("a very long load!")); 24 | res.end(); 25 | }, 10000); 26 | } 27 | } 28 | } 29 | 30 | }).listen(8081); 31 | -------------------------------------------------------------------------------- /Chapter03/other_changes.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 86 | 87 | -------------------------------------------------------------------------------- /Chapter03/prototype_and_classes.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 116 | 117 | -------------------------------------------------------------------------------- /Chapter03/reflection_proxy.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 73 | 74 | -------------------------------------------------------------------------------- /Chapter03/server.js: -------------------------------------------------------------------------------- 1 | const http = require('http'); 2 | 3 | const server = http.createServer((req, res) => { 4 | console.log(req.url, req.method); 5 | res.setHeader('Access-Control-Allow-Origin', "*"); 6 | res.setHeader('Access-Control-Allow-Credentials', "true"); 7 | res.setHeader('Access-Control-Allow-Methods', "GET,HEAD"); 8 | res.setHeader('Access-Control-Allow-Headers', "Access-Control-Allow-Headers, Origin,Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers") 9 | res.end(); 10 | }).listen(8081); -------------------------------------------------------------------------------- /Chapter03/variables.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 27 | 28 | -------------------------------------------------------------------------------- /Chapter04/todo/public/global.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | position: relative; 3 | width: 100%; 4 | height: 100%; 5 | } 6 | 7 | body { 8 | color: #333; 9 | margin: 0; 10 | padding: 8px; 11 | box-sizing: border-box; 12 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; 13 | } 14 | 15 | a { 16 | color: rgb(0,100,200); 17 | text-decoration: none; 18 | } 19 | 20 | a:hover { 21 | text-decoration: underline; 22 | } 23 | 24 | a:visited { 25 | color: rgb(0,80,160); 26 | } 27 | 28 | label { 29 | display: block; 30 | } 31 | 32 | input, button, select, textarea { 33 | font-family: inherit; 34 | font-size: inherit; 35 | padding: 0.4em; 36 | margin: 0 0 0.5em 0; 37 | box-sizing: border-box; 38 | border: 1px solid #ccc; 39 | border-radius: 2px; 40 | } 41 | 42 | input:disabled { 43 | color: #ccc; 44 | } 45 | 46 | input[type="range"] { 47 | height: 0; 48 | } 49 | 50 | button { 51 | background-color: #f4f4f4; 52 | outline: none; 53 | } 54 | 55 | button:active { 56 | background-color: #ddd; 57 | } 58 | 59 | button:focus { 60 | border-color: #666; 61 | } -------------------------------------------------------------------------------- /Chapter04/todo/src/App.svelte: -------------------------------------------------------------------------------- 1 | 31 | 32 | 35 | 36 |

Todo Application! Current number of Todos: {currSize}

37 | 39 | 40 | 41 | 42 |
43 | 44 | -------------------------------------------------------------------------------- /Chapter04/todo/src/Todo.svelte: -------------------------------------------------------------------------------- 1 | 13 | 18 | {#if 19 | !(($completed && !_completed) || 20 | ($overdue && new Date(dueDate).getTime() >= Date.now()) 21 | ) 22 | } 23 |
  • 24 | Task {num}: {description} - Due on {dueDate} 25 | 26 | 27 |
  • 28 | {/if} -------------------------------------------------------------------------------- /Chapter04/todo/src/main.js: -------------------------------------------------------------------------------- 1 | import App from './App.svelte'; 2 | 3 | const app = new App({ 4 | target: document.body, 5 | props: {} 6 | }); 7 | 8 | export default app; -------------------------------------------------------------------------------- /Chapter04/todo/src/stores.js: -------------------------------------------------------------------------------- 1 | import { writable } from 'svelte/store'; 2 | 3 | export const overdue = writable(false); 4 | export const completed = writable(false); -------------------------------------------------------------------------------- /Chapter04/todo/src/todo_store.js: -------------------------------------------------------------------------------- 1 | import { writable } from 'svelte/store'; 2 | 3 | export const todo_store = writable(new Set()); -------------------------------------------------------------------------------- /Chapter04/weather/public/global.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | position: relative; 3 | width: 100%; 4 | height: 100%; 5 | } 6 | 7 | body { 8 | display : flex; 9 | flex-direction : column; 10 | align-items : center; 11 | color: #333; 12 | margin: 0; 13 | padding: 8px; 14 | box-sizing: border-box; 15 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; 16 | } 17 | 18 | a { 19 | color: rgb(0,100,200); 20 | text-decoration: none; 21 | } 22 | 23 | a:hover { 24 | text-decoration: underline; 25 | } 26 | 27 | a:visited { 28 | color: rgb(0,80,160); 29 | } 30 | 31 | label { 32 | display: block; 33 | } 34 | 35 | input, button, select, textarea { 36 | font-family: inherit; 37 | font-size: inherit; 38 | padding: 0.4em; 39 | margin: 0 0 0.5em 0; 40 | box-sizing: border-box; 41 | border: 1px solid #ccc; 42 | border-radius: 2px; 43 | } 44 | 45 | input:disabled { 46 | color: #ccc; 47 | } 48 | 49 | input[type="range"] { 50 | height: 0; 51 | } 52 | 53 | button { 54 | background-color: #f4f4f4; 55 | outline: none; 56 | } 57 | 58 | button:active { 59 | background-color: #ddd; 60 | } 61 | 62 | button:focus { 63 | border-color: #666; 64 | } -------------------------------------------------------------------------------- /Chapter04/weather/src/App.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /Chapter04/weather/src/Dropdown.svelte: -------------------------------------------------------------------------------- 1 | 49 | 74 | 75 |
    76 | {#if type === "text"} 77 | 78 | {:else} 79 | 80 | {/if} 81 | 86 |
    -------------------------------------------------------------------------------- /Chapter04/weather/src/WeatherInput.svelte: -------------------------------------------------------------------------------- 1 | 14 | 23 |
    24 | 25 | {#if $zipcode} 26 | 27 | {:else} 28 | 29 | {/if} 30 | 31 | 32 | 33 |
    -------------------------------------------------------------------------------- /Chapter04/weather/src/WeatherOutput.svelte: -------------------------------------------------------------------------------- 1 | 4 | 9 |
    10 | {#if $weather.error } 11 |

    There was an error getting your data!

    12 | {:else if $weather.data } 13 |
    14 |
    Conditions
    15 |
    {$weather.weather}
    16 |
    Temperature
    17 |
    {$weather.temperature.current}
    18 |
    {$weather.temperature.min}
    19 |
    {$weather.temperature.max}
    20 |
    Humidity
    21 |
    {$weather.humidity}
    22 |
    Sunrise
    23 |
    {$weather.sunrise}
    24 |
    Sunset
    25 |
    {$weather.sunset}
    26 |
    Windspeed
    27 |
    {$weather.windspeed}
    28 |
    Direction
    29 |
    {$weather.direction}
    30 |
    31 | {:else} 32 |

    No city or zipcode has been submitted!

    33 | {/if} 34 |
    -------------------------------------------------------------------------------- /Chapter04/weather/src/main.js: -------------------------------------------------------------------------------- 1 | import App from './App.svelte'; 2 | 3 | const app = new App({ 4 | target: document.body, 5 | props: { 6 | name: 'world' 7 | } 8 | }); 9 | 10 | export default app; -------------------------------------------------------------------------------- /Chapter04/weather/src/stores.js: -------------------------------------------------------------------------------- 1 | import { writable } from 'svelte/store'; 2 | 3 | function createWeather() { 4 | const { subscribe, update} = writable({}); 5 | 6 | const api_key = '79181f5439884711cf5ada2867e73c8a' 7 | 8 | return { 9 | subscribe, 10 | gather: (cc, _z, _m, zip=null, city=null) => { 11 | fetch(`https://api.openweathermap.org/data/2.5/weather?q=${_z ? zip : city},${cc}&appid=${api_key}&units=${_m ? 'metric' : 'imperial'}`) 12 | .then(res => res.json()) 13 | .then(final => update(() => { 14 | const sr = new Date(final.sys.sunrise); 15 | const ss = new Date(final.sys.sunset); 16 | return { 17 | data : true, 18 | msg : final.weather[0].description, 19 | weather : final.weather[0].main, 20 | temperature : { 21 | current : final.main.temp, 22 | min : final.main.temp_min, 23 | max : final.main.temp_max 24 | }, 25 | humidity : `${final.main.humidity}%`, 26 | sunrise : `${sr.getHours() + 1}:${sr.getMinutes()}:${sr.getSeconds()}`, 27 | sunset : `${ss.getHours() + 1}:${ss.getMinutes()}:${ss.getSeconds()}`, 28 | windspeed : `${final.wind.speed} `, 29 | direction : `${final.wind.deg}\u00B0`, 30 | error : false 31 | } 32 | } 33 | )).catch(err => { 34 | return { 35 | error : true 36 | } 37 | }); 38 | } 39 | } 40 | } 41 | 42 | export const zipcode = writable(false); 43 | export const metric = writable(0); 44 | export const weather = createWeather({ 45 | data : false, 46 | msg : '', 47 | weather : '', 48 | temperature : { 49 | current : '', 50 | min : '', 51 | max : '' 52 | }, 53 | humidity : '', 54 | sunrise : '', 55 | sunset : '', 56 | windspeed : '', 57 | direction : '' 58 | }); -------------------------------------------------------------------------------- /Chapter05/bad_code.js: -------------------------------------------------------------------------------- 1 | const fun = function() { 2 | const item = 10; 3 | for(let i = 0; i < item; i++) { 4 | const tempObj = {} 5 | tempObj[i] = "what " + i; 6 | } 7 | return function() { 8 | console.log('we will have access to other things'); 9 | const alternative = 'what'; 10 | 11 | return item; 12 | } 13 | } 14 | 15 | console.log('this is some code'); 16 | const x = 'what'; 17 | fun()(); -------------------------------------------------------------------------------- /Chapter05/child.js: -------------------------------------------------------------------------------- 1 | process.on('message', (msg) => { 2 | switch(msg) { 3 | case 'DISCONNECT': { 4 | process.send('DISCONNECT'); 5 | // time to remove ourselved 6 | process.exit(); 7 | break; 8 | } 9 | } 10 | }); 11 | 12 | process.send('CONNECT'); -------------------------------------------------------------------------------- /Chapter05/custom_transform.js: -------------------------------------------------------------------------------- 1 | import { Transform } from 'stream'; 2 | 3 | class GetThe extends Transform { 4 | #currPos = 0; 5 | #numberOfThe = 0; 6 | 7 | static chars = Buffer.from('the'); 8 | constructor(options) { 9 | super(options) 10 | } 11 | _transform(chunk, encoding, callback) { 12 | for(let i = 0; i < chunk.byteLength; i++) { 13 | const char = chunk[i]; 14 | if(char === GetThe.chars[this.#currPos]) { 15 | if(this.#currPos === GetThe.chars.byteLength - 1) { //we are at the end so reset 16 | this.#numberOfThe += 1; 17 | this.#currPos = 0; 18 | } else { 19 | this.#currPos += 1; 20 | } 21 | } else { 22 | this.#currPos = 0; 23 | } 24 | } 25 | callback(); 26 | } 27 | _flush(callback) { 28 | callback(null, this.#numberOfThe.toString()); 29 | } 30 | } 31 | 32 | export default GetThe; -------------------------------------------------------------------------------- /Chapter05/example.js: -------------------------------------------------------------------------------- 1 | const x = 'what'; 2 | console.log('this is the value of x', x); 3 | const obj = { 4 | what : 'that', 5 | huh : 'this', 6 | eh : 'yeah' 7 | } 8 | console.log(obj); -------------------------------------------------------------------------------- /Chapter05/example.txt: -------------------------------------------------------------------------------- 1 | This is some data 2 | is should be processed by our system 3 | it should be coming in chunks that our system can handle 4 | most likely this will all come in one chunk 5 | the -------------------------------------------------------------------------------- /Chapter05/first_fs.js: -------------------------------------------------------------------------------- 1 | import { promises } from 'fs'; 2 | 3 | (async() => { 4 | await promises.writeFile('example2.txt', "Here is some text\n"); 5 | const fd = await promises.open('example2.txt', 'a'); 6 | await fd.appendFile("Here is some more text\n"); 7 | await fd.close(); 8 | console.log(await promises.readFile('example2.txt', 'utf8')); 9 | })(); -------------------------------------------------------------------------------- /Chapter05/http_get.js: -------------------------------------------------------------------------------- 1 | import https from 'https'; 2 | 3 | https.get('https://en.wikipedia.org/wiki/Surprise_(emotion)', (res) => { 4 | if( res.statusCode === 200 ) { 5 | res.on('data', (data) => { 6 | console.log(data.toString('utf8')); 7 | }); 8 | res.on('end', () => { 9 | console.log('no more information'); 10 | }); 11 | } else { 12 | console.error('bad error code!', res.statusCode); 13 | } 14 | }); 15 | -------------------------------------------------------------------------------- /Chapter05/http_server.js: -------------------------------------------------------------------------------- 1 | import http from 'http'; 2 | 3 | const server = http.createServer((req, res) => { 4 | console.log(req.url); 5 | if( req.method === "GET" && 6 | req.url === "/main.css" ) { 7 | res.writeHead(200, { 'Content-Type' : 'text/css'}); 8 | res.end(` 9 | h1 { 10 | color : green; 11 | } 12 | p { 13 | color : purple; 14 | } 15 | `); 16 | } else { 17 | res.writeHead(200, { 'Content-Type' : "text/html"}); 18 | res.end(` 19 | 20 | 21 | 22 | 23 | 24 |

    Hello!

    25 |

    This is from our server!

    26 | 27 | 28 | `); 29 | } 30 | }); 31 | server.listen(8000, "127.0.0.1") -------------------------------------------------------------------------------- /Chapter05/import_script.js: -------------------------------------------------------------------------------- 1 | import os from 'os'; 2 | 3 | console.log(os.arch()); 4 | console.log(os.cpus()); -------------------------------------------------------------------------------- /Chapter05/main.js: -------------------------------------------------------------------------------- 1 | console.log(process.env.npm_package_config_port); 2 | console.log(process.env.npm_package_config_name); 3 | console.log(process.env.npm_package_config_testdata_thing); 4 | console.log(process.env.npm_package_config_testdata_other); -------------------------------------------------------------------------------- /Chapter05/named_pipe.js: -------------------------------------------------------------------------------- 1 | import net from 'net'; 2 | import path from 'path'; 3 | import os from 'os'; 4 | 5 | const pipeName = (os.platform() === 'win32') ? 6 | path.join('\\\\?\\pipe', process.cwd(), 'temp') : 7 | path.join(process.cwd(), "temp"); 8 | 9 | const server = net.createServer().listen(pipeName) 10 | server.on('connection', (socket) => { 11 | debugger; 12 | console.log('a socket has joined the party!'); 13 | socket.write("DISCONNECT"); 14 | socket.on('close', () => { 15 | console.log('socket has been closed!'); 16 | }); 17 | }); -------------------------------------------------------------------------------- /Chapter05/named_pipe_child.js: -------------------------------------------------------------------------------- 1 | import net from 'net'; 2 | import path from 'path'; 3 | import os from 'os'; 4 | 5 | const pipeName = (os.platform() === 'win32') ? 6 | path.join('\\\\?\\pipe', process.cwd(), 'temp') : 7 | path.join(process.cwd(), "temp"); 8 | 9 | const socket = new net.Socket().connect(pipeName); 10 | socket.on('connect', () => { 11 | console.log('we have connected'); 12 | }) 13 | socket.on('data', (data) => { 14 | if( data.toString('utf8') === 'DISCONNECT') { 15 | socket.destroy(); 16 | } 17 | }); -------------------------------------------------------------------------------- /Chapter05/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "Chapter5_import_example", 3 | "version" : "0.0.1", 4 | "type" : "module", 5 | "config" : { 6 | "port" : 8080, 7 | "name" : "example", 8 | "testdata" : { 9 | "thing" : 5, 10 | "other" : 10 11 | } 12 | }, 13 | "scripts" : { 14 | "example-script" : "node main.js" 15 | } 16 | } -------------------------------------------------------------------------------- /Chapter05/parent_child_pipe.js: -------------------------------------------------------------------------------- 1 | import { fork } from 'child_process'; 2 | 3 | const child = fork('child.js'); 4 | child.on('message', (msg) => { 5 | switch(msg) { 6 | case 'CONNECT': { 7 | console.log('our child is connected to us. Tell it to dispose of itself'); 8 | setTimeout(() => { 9 | child.send('DISCONNECT'); 10 | }, 15000); 11 | break; 12 | } 13 | case 'DISCONNECT': { 14 | console.log('our child is disposing of itself. Time for us to do the same'); 15 | process.exit(); 16 | break; 17 | } 18 | } 19 | }); -------------------------------------------------------------------------------- /Chapter05/read_file_stream.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import { PassThrough } from 'stream'; 3 | import GetThe from './custom_transform.js'; 4 | 5 | let numberOfThe = 0; 6 | const chars = Buffer.from('the'); 7 | let currPos = 0; 8 | const str = fs.createReadStream('./example.txt'); 9 | const pt = new PassThrough(); 10 | str.pipe(pt); 11 | pt.on('data', (chunk) => { 12 | for(let i = 0; i < chunk.byteLength; i++) { 13 | const char = chunk[i]; 14 | if(char === chars[currPos]) { 15 | if(currPos === chars.byteLength - 1) { //we are at the end so reset 16 | numberOfThe += 1; 17 | currPos = 0; 18 | } else { 19 | currPos += 1; 20 | } 21 | } else { 22 | currPos = 0; 23 | } 24 | } 25 | }); 26 | pt.on('end', () => { 27 | console.log('the number of THE in the text is: ', numberOfThe); 28 | }); 29 | 30 | const gt = new GetThe(); 31 | gt.on('data', (data) => { 32 | console.log('the number of THE produced by the custom stream is: ', data.toString('utf8')); 33 | }); 34 | const str2 = fs.createReadStream('./example.txt'); 35 | str2.pipe(gt); -------------------------------------------------------------------------------- /Chapter05/require_script.js: -------------------------------------------------------------------------------- 1 | const os = require('os'); 2 | 3 | console.log(os.arch()); 4 | console.log(os.cpus()); -------------------------------------------------------------------------------- /Chapter06/cluster/main.js: -------------------------------------------------------------------------------- 1 | import cluster from 'cluster'; 2 | import https from 'https'; 3 | import http from 'http'; 4 | import { URL } from 'url'; 5 | 6 | const numWorkers = 2; 7 | const CACHE = 0; 8 | const SEND = 1; 9 | const location = 'http://127.0.0.1'; 10 | const port = 3000; 11 | 12 | if( cluster.isMaster ) { 13 | let count = 1; 14 | const cache = new Map(); 15 | for(let i = 0; i < numWorkers; i++) { 16 | const worker = cluster.fork(); 17 | worker.on('message', (msg) => { 18 | switch(msg.cmd) { 19 | case 'STOP': { 20 | process.exit(); 21 | break; 22 | } 23 | case 'DELETE': { 24 | if( msg.opt !== 'all' ) { 25 | cache.delete(parseInt(msg.opt)); 26 | } else { 27 | cache.clear(); 28 | } 29 | worker.send({cmd : 'GOOD' }); 30 | break; 31 | } 32 | case 'GET': { 33 | worker.send(cache.get(parseInt(msg.opt)) || 'nada'); 34 | break; 35 | } 36 | case 'GRAB': { 37 | const buf = []; 38 | https.get(msg.opt, (res) => { 39 | res.on('data', (data) => { 40 | buf.push(data.toString('utf8')); 41 | }); 42 | res.on('end', () => { 43 | const final = buf.join(''); 44 | cache.set(count, final); 45 | count += 1; 46 | worker.send({cmd : 'GOOD' }); 47 | }); 48 | }) 49 | } 50 | } 51 | }) 52 | } 53 | cluster.on('exit', (worker, code, signal) => { 54 | console.log(`worker ${worker.process.pid} died because of: ${code}`); 55 | }); 56 | } else { 57 | const handleCommand = function(res, command, params=null) { 58 | switch(command) { 59 | case 'grab': { 60 | if(!params) { 61 | res.writeHead(200, { 'Content-Type' : 'text/plain' }); 62 | res.end('Cannot grab this url!'); 63 | } else { 64 | process.send({cmd : 'GRAB', opt : params}); 65 | process.once('message', (msg) => { 66 | res.writeHead(200, { 'Content-Type' : 'text/plain' }); 67 | res.end('Cached url'); 68 | }) 69 | } 70 | break; 71 | } 72 | case 'get': { 73 | if(!params) { 74 | res.writeHead(200, { 'Content-Type' : 'text/plain' }); 75 | res.end('no record to get!'); 76 | } else { 77 | process.send({cmd : 'GET', opt : params }); 78 | process.once('message', (msg) => { 79 | res.writeHead(200, { 'Content-Type' : 'text/plain'}); 80 | res.end(msg); 81 | }); 82 | } 83 | break; 84 | } 85 | case 'delete': { 86 | process.send({cmd : 'DELETE', opt : params || 'all' }); 87 | process.once('message', (msg) => { 88 | res.writeHead(200, { 'Content-Type' : 'text/plain' }); 89 | res.end('deleting record(s)'); 90 | }); 91 | break; 92 | } 93 | case 'stop': { 94 | res.writeHead(200, { 'Content-Type' : 'text/plain' }); 95 | res.end('cache server shutting down'); 96 | process.send({cmd : 'STOP'}); 97 | break; 98 | } 99 | } 100 | } 101 | 102 | http.Server((req, res) => { 103 | const search = new URL(`${location}${req.url}`).searchParams; 104 | const command = search.get('command'); 105 | if(!command) { 106 | res.writeHead(404, { 'Content-Type' : 'text/plain' }); 107 | res.end('command not found!'); 108 | } else { 109 | const params = search.get('options'); 110 | handleCommand(res, command, params); 111 | } 112 | }).listen(port); 113 | } -------------------------------------------------------------------------------- /Chapter06/cluster/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "Chapter5_Cluster", 3 | "version" : "0.0.1", 4 | "type" : "module", 5 | "main" : "main.js" 6 | } -------------------------------------------------------------------------------- /Chapter06/datagram/client.js: -------------------------------------------------------------------------------- 1 | import dgram from 'dgram'; 2 | import { Socket } from 'net'; 3 | const multicastAddress = '239.192.0.0'; 4 | const sendMessageBadOutput = 'message needs to be formatted as follows: BUY|SELL '; 5 | const recvClient = dgram.createSocket({type : 'udp4', reuseAddr: true }); 6 | const sendClient = new Socket().connect(3000, "127.0.0.1"); 7 | recvClient.on('connect', () => { 8 | console.log('client is connected to the server'); 9 | }); 10 | recvClient.on('message', (msg) => { 11 | console.log('client received message', msg.toString('utf8')); 12 | }); 13 | recvClient.bind(3000, () => { 14 | recvClient.addMembership(multicastAddress); 15 | }); 16 | process.stdin.setEncoding('utf8'); 17 | process.stdin.on('data', (msg) => { 18 | const input = msg.split(' '); 19 | if( input.length !== 3 ) { 20 | console.log(sendMessageBadOutput); 21 | } 22 | const num = parseInt(input[2]); 23 | console.log('number is', num); 24 | if( num.toString() === 'NaN' ) { 25 | console.log(sendMessageBadOutput); 26 | } 27 | sendClient.write(msg); 28 | }); 29 | sendClient.on('data', (data) => { 30 | console.log(data.toString('utf8')); 31 | }); -------------------------------------------------------------------------------- /Chapter06/datagram/main.js: -------------------------------------------------------------------------------- 1 | import dgram from 'dgram'; 2 | import net from 'net'; 3 | 4 | const multicastAddress = '239.192.0.0'; 5 | const badListingNumMessage = 'to list a new ticker the following format needs to be followed |'; 6 | const symbolTable = new Map(); 7 | const clientTable = new Map(); 8 | const server = dgram.createSocket({type : 'udp4', reuseAddr : true}).bind(3000); 9 | const recvServer = net.createServer().listen(3000, '127.0.0.1'); 10 | recvServer.on('connection', (socket) => { 11 | const temp = new Map(); 12 | const availableSymbols = Object.keys(symbolTable); 13 | console.log(availableSymbols); 14 | for(let i = 0; i < availableSymbols.length; i++) { 15 | temp.set(availableSymbols[i], 0); 16 | } 17 | clientTable.set(socket, temp); 18 | socket.on('data', (msg) => { 19 | // logic to say if we are able to actually process request 20 | const input = msg.toString('utf8').split(' '); 21 | if( input.length !== 3 ) { 22 | console.log('wrong number of arguments'); 23 | socket.write('ERROR!'); 24 | return; 25 | } 26 | if( input[0] !== 'SELL' && 27 | input[0] !== 'BUY' ) { 28 | console.log('this is not a command'); 29 | socket.write('ERROR!'); 30 | return; 31 | } 32 | const buyOrSell = input[0]; 33 | const tickerSymbol = input[1]; 34 | const num = parseInt(input[2]); 35 | if( typeof num === 'NaN' ) { 36 | console.log('the amount that the client wants to buy or sell is not a number'); 37 | socket.write('ERROR!'); 38 | return; 39 | } 40 | // number held by server 41 | const numHeld = symbolTable.get(input[1]); 42 | if( typeof numHeld === 'undefined' || 43 | (buyOrSell === 'BUY' && ( 44 | num <= 0 || 45 | numHeld - num <= 0 46 | ))) { 47 | console.log('there is not that many shares currently held by the reserve'); 48 | socket.write('ERROR!'); 49 | return; 50 | } 51 | // check to make sure the client has that amount in their book 52 | const clientBook = clientTable.get(socket); 53 | const clientAmount = clientBook.get(tickerSymbol); 54 | console.log(clientAmount); 55 | if( buyOrSell === 'SELL' && 56 | clientAmount - num < 0 ) { 57 | console.log('the client does not hold that amount in their book'); 58 | socket.write('ERROR!'); 59 | return; 60 | } 61 | // process the request 62 | if( buyOrSell === 'BUY' ) { 63 | clientBook.set(tickerSymbol, clientAmount + num); 64 | symbolTable.set(tickerSymbol, numHeld - num); 65 | } else if( buyOrSell === 'SELL' ) { 66 | clientBook.set(tickerSymbol, clientAmount - num); 67 | symbolTable.set(tickerSymbol, numHeld + num); 68 | } else { 69 | return; 70 | } 71 | const m = Buffer.from(`${tickerSymbol} ${symbolTable.get(tickerSymbol)}`); 72 | server.send(m, 0, m.byteLength, 3000, multicastAddress); 73 | socket.write(`successfully processed request, you now hold ${clientBook.get(tickerSymbol)} of ${tickerSymbol}`); 74 | console.log('processed request', tickerSymbol, symbolTable.get(tickerSymbol)); 75 | }); 76 | socket.on('error', (err) => console.log('client removed from list')); 77 | }); 78 | server.on('message', (msg, info) => { 79 | console.log('we received a msg', msg, info); 80 | }); 81 | server.on('error', (err) => { 82 | console.error(`server err: ${err.stack}`); 83 | server.close(); 84 | }); 85 | server.on('listening', () => { 86 | console.log(`udp server listening on ${JSON.stringify(server.address())}`); 87 | }); 88 | process.stdin.setEncoding('utf8'); 89 | process.stdin.on('data', (data) => { 90 | const input = data.split(' '); 91 | if( input.length !== 2 ) { 92 | console.log(badListingNumMessage); 93 | return; 94 | } 95 | const num = parseInt(input[1]); 96 | if( num.toString() === 'NaN' ) { 97 | console.log(badListingNumMessage); 98 | return; 99 | } 100 | symbolTable.set(input[0], num); 101 | for( const client of clientTable ) { 102 | client[1].set(input[0], 0); 103 | } 104 | server.send(Buffer.from(data), 0, data.length, 3000, multicastAddress); 105 | }); -------------------------------------------------------------------------------- /Chapter06/datagram/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "Chapter5_Datagram", 3 | "version" : "0.0.1", 4 | "type" : "module", 5 | "main" : "main.js" 6 | } -------------------------------------------------------------------------------- /Chapter06/datagram/price.js: -------------------------------------------------------------------------------- 1 | export const trade = { 2 | symbol : null, 3 | price : null, 4 | number : null, 5 | type : null 6 | }; 7 | 8 | export const types = { 9 | SELL : 0, 10 | BUY : 0 11 | } -------------------------------------------------------------------------------- /Chapter06/http2server/basic.js: -------------------------------------------------------------------------------- 1 | import http2 from 'http2'; 2 | import fs from 'fs'; 3 | 4 | const server = http2.createSecureServer({ 5 | key : fs.readFileSync('server.key.pem'), 6 | cert : fs.readFileSync('server.crt.pem') 7 | }); 8 | server.on('error', (err) => console.error(err)); 9 | 10 | server.on('stream', (stream, headers) => { 11 | stream.respond({ 12 | 'content-type': 'text/plain', 13 | ':status' : 200 14 | }); 15 | stream.end('Hello from Http2 server'); 16 | }); 17 | server.listen(8081, '127.0.0.1'); -------------------------------------------------------------------------------- /Chapter06/http2server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "Chapter5_Http2_Server", 3 | "version" : "0.0.1", 4 | "type" : "module", 5 | "main" : "static_server.js", 6 | "config" : { 7 | "cert" : "server.crt.pem", 8 | "key" : "server.key.pem", 9 | "static" : "static" 10 | }, 11 | "scripts": { 12 | "server" : "node --experimental-modules server.js" 13 | } 14 | } -------------------------------------------------------------------------------- /Chapter06/http2server/server.crt.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICYTCCAcegAwIBAgIJAMGI7H/rQicOMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV 3 | BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX 4 | aWRnaXRzIFB0eSBMdGQwHhcNMTkwNzIxMTUwNTQzWhcNMjAwNzIwMTUwNTQzWjBF 5 | MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50 6 | ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIGiMA0GCSqGSIb3DQEBAQUAA4GQADCBjAKB 7 | hADZ4haXseiWCNNU8wLiwlwsokuMGGSHDR+szSJO/2fXRM95kuKQwHOQn2dSPcLv 8 | KMxKMtc6nvLIRSwCrBkyY+jtjJONisYLZbLgpPcuErp5ylB70PbabzAvyN2QWLQR 9 | G//PQEaROByUFI5uFxQKdUO5iluXHZIB+jFsa2gSMizWwyjXfwIDAQABo1MwUTAd 10 | BgNVHQ4EFgQUC3gp8L+v2oDdOvGBnG7ZL+y8aMAwHwYDVR0jBBgwFoAUC3gp8L+v 11 | 2oDdOvGBnG7ZL+y8aMAwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOB 12 | hAB8j6nX4ocm6TNJQ543dPDRlUg0C61GdiA9aixf+aO+tTqaVzBSgOl6RShPZuex 13 | Qvjut6KKc68XGCkSyRHK2XbIVnxwZGqB3mkES3RDWSa4+mGMSm1RPufvVVjD7Cu0 14 | Qipz626zO9BGp0QROegVaYxWRBEIqoUXbRoyTbUAwYdF5u/8fg== 15 | -----END CERTIFICATE----- 16 | -------------------------------------------------------------------------------- /Chapter06/http2server/server.js: -------------------------------------------------------------------------------- 1 | import http2 from 'http2' 2 | import fs from 'fs' 3 | import path from 'path' 4 | 5 | const basePath = process.env.npm_package_config_static; 6 | const supportedTypes = new Set(['.ico', '.html', '.css', '.js']); 7 | 8 | const server = http2.createSecureServer({ 9 | key : fs.readFileSync(process.env.npm_package_config_key), 10 | cert : fs.readFileSync(process.env.npm_package_config_cert), 11 | allowHTTP1 : true 12 | }); 13 | server.on('error', (err) => console.error(err)); 14 | 15 | server.on('stream', (stream, header) => { 16 | const fileLoc = header[':path']; 17 | const extension = path.extname(fileLoc); 18 | if(!supportedTypes.has(extension)) { 19 | stream.respond({ 20 | ':status' : 400, 21 | 'content-type' : 'application/json' 22 | }); 23 | stream.end(JSON.stringify({ 24 | error : 'unsupported data type!', 25 | extension 26 | })); 27 | return; 28 | } 29 | stream.respondWithFile( 30 | path.join(process.cwd(), basePath, fileLoc), 31 | { 32 | ':status' : 200, 33 | 'content-type' : 34 | extension === ".html" ? 35 | 'text/html' : 36 | extension === ".css" ? 37 | 'text/css' : 38 | 'text/javascript' 39 | }, 40 | { 41 | onError : (err) => { 42 | if( err.code === 'ENOENT') { 43 | stream.respond({ ':status' : 404 }); 44 | } else { 45 | stream.respond({ ':status' : 500 }); 46 | } 47 | stream.end(); 48 | } 49 | } 50 | ) 51 | }); 52 | 53 | server.listen(80, '127.0.0.1'); -------------------------------------------------------------------------------- /Chapter06/http2server/server.key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIICgwIBADANBgkqhkiG9w0BAQEFAASCAm0wggJpAgEAAoGEANniFpex6JYI01Tz 3 | AuLCXCyiS4wYZIcNH6zNIk7/Z9dEz3mS4pDAc5CfZ1I9wu8ozEoy1zqe8shFLAKs 4 | GTJj6O2Mk42KxgtlsuCk9y4SunnKUHvQ9tpvMC/I3ZBYtBEb/89ARpE4HJQUjm4X 5 | FAp1Q7mKW5cdkgH6MWxraBIyLNbDKNd/AgMBAAECgYN2iN+Nq4ZDiY2fW42oW+DV 6 | j33WO7cFGgivc/JEVALkRFUzaMvFwE2e0nndbihNAD8T5tDc3Xhrp+YnlgUnOt+P 7 | zKixrvk4njGcqkod92B5/RBfFPKNnKc5HNJnOfPxcG5jVRvDcex872hafwwjSbda 8 | 9FyQyTIMwkKpiLccmluglZDAYQJCDu3XsKzPz2Zokl9n233Su0ytgU8JdnvKVe6T 9 | uZyIgGkmDjldrpP9c42CK56TCIwvWydaCn6eC9G+QjoEPpYEdv8ZAkIOmDS09S9/ 10 | mIlm8fRHA66O77bXRrt+PTERJS1tI/+H7jZfZiiFam23efQN5jSNxb+VkuQBlwCl 11 | yQHIToalhqCsFlcCQgx830MmCt6N50MpExMC+Nc7mJblqiC2A9ZFrsRAL95aFj8n 12 | tTlnCUczQ4OBLbSkrqgtDqBgsYtpcyCqrwo7KpjvYQJCCttsClqEFVXykaTZZqL0 13 | jt0BljDGdmkrQWf43UJ0bDjoNhwXdPjx2hZdWqGw4u8DUVBPUmd1Ud+w9cSFIkp+ 14 | 6MZNAkIFtY1/3xc8g91z7A+DnjiZiuAriMP7PtHk7T1OZSrbbbFUhS6DvlS8XNZF 15 | tbb28W/Yc7c/ASKVIjPllBzp8PXF5EY= 16 | -----END PRIVATE KEY----- 17 | -------------------------------------------------------------------------------- /Chapter06/http2server/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |

    This is our page

    8 |

    Here is some stuff

    9 | 10 | -------------------------------------------------------------------------------- /Chapter06/http2server/static/main.css: -------------------------------------------------------------------------------- 1 | h1 { 2 | color : blue; 3 | } -------------------------------------------------------------------------------- /Chapter06/http2server/static/main.js: -------------------------------------------------------------------------------- 1 | console.log('we have data'); -------------------------------------------------------------------------------- /Chapter06/local/cache.js: -------------------------------------------------------------------------------- 1 | import net from 'net'; 2 | import pipeName from './helper.js'; 3 | 4 | let count = 0; 5 | let cacheTable = new Map(); 6 | const begin = Buffer.from('!!!BEGIN!!!'); 7 | const end = Buffer.from('!!!END!!!'); 8 | const get = Buffer.from('!!!GET!!!'); 9 | const del = Buffer.from('!!!DELETE!!!'); 10 | let currData = []; 11 | 12 | const socket = new net.Socket().connect(pipeName()); 13 | socket.on('data', (data) => { 14 | if( data.toString('utf8') === 'WHOIS' ) { 15 | return socket.write('cache'); 16 | } 17 | if( data.includes(get) ) { 18 | const loc = parseInt(data.slice(get.byteLength).toString('utf8')); 19 | const d = cacheTable.get(loc); 20 | if( typeof d !== 'undefined' ) { 21 | socket.write(begin.toString('utf8') + d + end.toString('utf8')); 22 | } 23 | } 24 | if( data.includes(del) ) { 25 | if( data.byteLength === del.byteLength ) { 26 | cacheTable.clear(); 27 | } else { 28 | const loc = parseInt(data.slice(del.byteLength).toString('utf8')); 29 | console.log('location', loc); 30 | cacheTable.delete(loc); 31 | } 32 | return console.log('size of cache is now', cacheTable.size); 33 | } 34 | 35 | if( data.includes(begin) ) { 36 | currData.push(data.slice(begin.byteSize).toString('utf8')); 37 | } 38 | if( currData.length ) { 39 | currData.push(data.toString('utf8')); 40 | } 41 | if( data.includes(end) ) { 42 | currData.push(data.slice(0, data.byteLength - end.byteLength).toString('utf8')); 43 | cacheTable.set(count, currData.join('')); 44 | currData = []; 45 | } 46 | }); -------------------------------------------------------------------------------- /Chapter06/local/helper.js: -------------------------------------------------------------------------------- 1 | import os from 'os'; 2 | import path from 'path'; 3 | 4 | const pipeName = 'temp'; 5 | 6 | // grabs the name of the pipe so all modules 7 | // will know where to point themselves 8 | export default function() { 9 | return os.platform() === 'win32' ? 10 | path.join('\\\\?\\pipe', process.cwd(), pipeName) : 11 | path.join(process.cwd(), pipeName); 12 | } -------------------------------------------------------------------------------- /Chapter06/local/main.js: -------------------------------------------------------------------------------- 1 | import { Worker } from 'worker_threads'; 2 | import net from 'net'; 3 | import tty from 'tty'; 4 | import pipeName from './helper.js'; 5 | 6 | const table = new Map(); 7 | let currData = []; 8 | const failure = Buffer.from('!!!FALSE!!!'); 9 | const begin = Buffer.from('!!!BEGIN!!!'); 10 | const end = Buffer.from('!!!END!!!'); 11 | const methodTable = new WeakMap(); 12 | const cacheHandler = function(data) { 13 | if( data.includes(begin) ) { 14 | currData.push(data.toString('utf8')); 15 | } 16 | if( currData.lenght ) { 17 | currData.push(data.toString('utf8')); 18 | } 19 | if( data.includes(end) ) { 20 | currData.push(data.toString('utf8')); 21 | const final = currData.join(''); 22 | console.log(currData.join('').substring(begin.byteLength, final.length - end.byteLength)); 23 | currData = []; 24 | } 25 | } 26 | const sendHandler = function(data) { 27 | if( data.equals(failure) ) { 28 | return console.log(false); 29 | } 30 | if( data.includes(begin) ) { 31 | currData.push(data.toString('utf8')); 32 | } 33 | if( currData.length ) { 34 | currData.push(data.toString('utf8')); 35 | } 36 | if( data.includes(end) ) { 37 | table.get('cache').write(currData.join('')); 38 | currData = []; 39 | } 40 | } 41 | const testConnections = function() { 42 | return table.size === 2; 43 | } 44 | const setupHandlers = function() { 45 | table.forEach((value, key) => { 46 | value.on('data', methodTable.get(value).bind(value)); 47 | }); 48 | } 49 | const startCLIMode = function() { 50 | process.stdin.on('data', function(data) { 51 | const d = data.toString('utf8'); 52 | const instruction = d.trim().split(/\s/ig); 53 | switch(instruction[0]) { 54 | case 'delete': { 55 | table.get('cache').write(`!!!DELETE!!!${instruction[1] || ''}`); 56 | break; 57 | } 58 | case 'get': { 59 | if( typeof instruction[1] === 'undefined' ) { 60 | return console.log('get needs a number associated with it!'); 61 | } 62 | table.get('cache').write(`!!!GET!!!${instruction[1]}`); 63 | break; 64 | } 65 | case 'grab': { 66 | table.get('send').write(instruction[1]); 67 | break; 68 | } 69 | case 'stop': { 70 | console.log('closing application'); 71 | table.forEach((value, key) => value.end()); 72 | process.exit(); 73 | break; 74 | } 75 | default: { 76 | console.log('This is an unsupported commnad. The following commands are allowed:'); 77 | console.table([{ 78 | command : 'get', 79 | arguments : '', 80 | description : 'grabs the data from the cache if available. Will return null if not found' 81 | }, { 82 | command : 'grab', 83 | arguments : '', 84 | description : 'grabs the webpage and stores it into the cache. Returns a boolean' 85 | }, { 86 | command : 'delete', 87 | arguments : '|all', 88 | description : 'delete a specific record or all of the records. Returns nothing' 89 | }, { 90 | command : 'stop', 91 | arguments : 'none', 92 | description : 'shuts down the threads and then the entire application' 93 | }]); 94 | break; 95 | } 96 | } 97 | }); 98 | } 99 | 100 | // start up the local server so everyone can connect to us 101 | const server = net.createServer().listen(pipeName()); 102 | server.on('connection', (socket) => { 103 | // we are setting up a simple lookup for the type 104 | socket.once('data', (data) => { 105 | const type = data.toString('utf8'); 106 | table.set(type, socket); 107 | switch(type) { 108 | case 'cache': { 109 | methodTable.set(socket, cacheHandler); 110 | break; 111 | } 112 | case 'send': { 113 | methodTable.set(socket, sendHandler); 114 | break; 115 | } 116 | default: { 117 | console.error('UNSUPPORTED SUB SYSTEM TYPE!'); 118 | break; 119 | } 120 | } 121 | if( testConnections() ) { 122 | setupHandlers(); 123 | startCLIMode(); 124 | } 125 | }); 126 | socket.once('close', () => { 127 | table.delete(type) 128 | }); 129 | socket.write('WHOIS'); 130 | }); 131 | 132 | // startup our two helpers 133 | const cache = new Worker('./cache.js'); 134 | const send = new Worker('./send.js'); -------------------------------------------------------------------------------- /Chapter06/local/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "Chapter5_Local", 3 | "version" : "0.0.1", 4 | "type" : "module", 5 | "main" : "main.js" 6 | } -------------------------------------------------------------------------------- /Chapter06/local/send.js: -------------------------------------------------------------------------------- 1 | import net from 'net'; 2 | import https from 'https'; 3 | import pipeName from './helper.js'; 4 | 5 | const socket = new net.Socket().connect(pipeName()); 6 | socket.on('data', (data) => { 7 | if( data.toString('utf8') === 'WHOIS' ) { 8 | return socket.write('send'); 9 | } 10 | const all = [] 11 | https.get(data.toString('utf8'), (res) => { 12 | res.on('data', (data) => { 13 | all.push(data.toString('utf8')); 14 | }); 15 | res.on('end', () => { 16 | socket.write('!!!BEGIN!!!'); 17 | socket.write(all.join('')); 18 | socket.write('!!!END!!!'); 19 | }); 20 | }).on('error', (err) => { 21 | socket.write('!!!FALSE!!!'); 22 | }) 23 | console.log('we received data from the main application', data.toString('utf8')); 24 | }); -------------------------------------------------------------------------------- /Chapter06/main_worker.js: -------------------------------------------------------------------------------- 1 | import { Worker } from 'worker_threads'; 2 | 3 | const data = {what: 'this', huh : 'yeah'}; 4 | 5 | const worker = new Worker("./worker.js"); 6 | worker.postMessage(data); 7 | worker.on('message', (data) => { 8 | worker.terminate(); 9 | }); 10 | worker.on('exit', (code) => { 11 | console.log('our worker stopped with the following code: ', code); 12 | }) -------------------------------------------------------------------------------- /Chapter06/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "Chapter6", 3 | "version" : "0.0.1", 4 | "type" : "module" 5 | } -------------------------------------------------------------------------------- /Chapter06/quic/client.js: -------------------------------------------------------------------------------- 1 | import quic from 'node-quic' 2 | import { StringDecoder } from 'string_decoder'; 3 | 4 | const port = 3000; 5 | const address = '127.0.0.1'; 6 | 7 | process.stdin.setEncoding('utf8'); 8 | process.stdin.on('data', (data) => { 9 | quic.send(port, address, data.trim()) 10 | .onData((data) => { 11 | console.log('we received the following back: ', data); 12 | }) 13 | }) -------------------------------------------------------------------------------- /Chapter06/quic/main.js: -------------------------------------------------------------------------------- 1 | import quic from 'node-quic' 2 | 3 | const port = 3000; 4 | const address = '127.0.0.1'; 5 | 6 | quic.listen(port, address) 7 | .then(() => {}) 8 | .onError((err) => console.error(err)) 9 | .onData((data, stream, buffer) => { 10 | console.log('we received data:', data); 11 | if( data === 'quit' ) { 12 | console.log('we are going to stop listening for data'); 13 | quic.stopListening(); 14 | } else { 15 | stream.write("Thank you for the data!"); 16 | } 17 | }); -------------------------------------------------------------------------------- /Chapter06/quic/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Chapter5_Quic", 3 | "version": "0.0.1", 4 | "type": "module", 5 | "main": "main.js", 6 | "dependencies": { 7 | "node-quic": "^0.1.3" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Chapter06/worker.js: -------------------------------------------------------------------------------- 1 | import { parentPort } from 'worker_threads'; 2 | 3 | parentPort.on('message', (msg) => { 4 | console.log('we received a message from our parent', msg); 5 | parentPort.postMessage({RECEIVED : true}); 6 | }); 7 | 8 | -------------------------------------------------------------------------------- /Chapter07/batch/count.js: -------------------------------------------------------------------------------- 1 | import { readFileSync, createReadStream } from 'fs' 2 | 3 | const file = readFileSync('./input.txt', {encoding : 'utf8'}); 4 | const re = /lorem/g; 5 | const matches = file.match(re); 6 | console.log('the number of matches is', matches.length); 7 | 8 | const stream = createReadStream('./input.txt'); 9 | const buf = Buffer.from('lorem'); 10 | let found = 0; 11 | let count = 0; 12 | stream.on('data', (chunk) => { 13 | for(const byte of chunk) { 14 | if( byte === buf[found] ) { 15 | found += 1; 16 | } else { 17 | found = 0; 18 | } 19 | if( found === buf.byteLength ) { 20 | count += 1; 21 | found = 0; 22 | } 23 | } 24 | }).on('end', () => { 25 | console.log('the number of matches is', count) 26 | }); -------------------------------------------------------------------------------- /Chapter07/batch/main.js: -------------------------------------------------------------------------------- 1 | import { readFileSync, createReadStream } from 'fs' 2 | import { Readable } from 'stream' 3 | 4 | const count = readFileSync('./input.txt', {encoding : 'utf8'}) 5 | .split(/\n|\r\n/g).length; 6 | console.timeEnd('full_file'); 7 | 8 | const newLine = 0x0A; 9 | const readStream = createReadStream('./input.txt'); 10 | let counter = 1; 11 | readStream.on('data', (chunk) => { 12 | for(const byte of chunk) { 13 | if( newLine === byte ) counter += 1; 14 | } 15 | }).on('end', () => { 16 | console.log('number of line in our file is', counter); 17 | }); -------------------------------------------------------------------------------- /Chapter07/batch/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "Chapter7_Batch", 3 | "type" : "module", 4 | "version" : "0.0.1" 5 | } -------------------------------------------------------------------------------- /Chapter07/batch/simple_readable.js: -------------------------------------------------------------------------------- 1 | import { Readable } from 'stream' 2 | import { createReadStream, createWriteStream } from 'fs' 3 | 4 | class LoremFinder extends Readable { 5 | #lorem = Buffer.from('lorem') 6 | #found = 0 7 | #totalCount = 0 8 | #startByteLoc = -1 9 | #file = null 10 | 11 | #data = function(chunk) { 12 | for(let i = 0; i < chunk.byteLength; i++) { 13 | const byte = chunk[i]; 14 | if( byte === this.#lorem[this.#found] ) { 15 | if(!this.#found ) { 16 | this.#startByteLoc = this.#totalCount + i; 17 | } 18 | this.#found += 1; 19 | } else { 20 | this.#found = 0; 21 | } 22 | if( this.#found === this.#lorem.byteLength ) { 23 | const buf = Buffer.alloc(4); 24 | buf.writeUInt32BE(this.#startByteLoc); 25 | this.push(this.#startByteLoc.toString() + "\r\n"); 26 | this.#found = 0; 27 | } 28 | } 29 | this.#totalCount += chunk.byteLength; 30 | } 31 | constructor(opts) { 32 | super(opts); 33 | if(!opts.stream ) { 34 | throw new Error("This stream needs a stream to be provided!"); 35 | } 36 | this.#file = opts.stream; 37 | this.#file.on('data', this.#data.bind(this)); 38 | this.#file.on('end', () => this.push(null)); 39 | } 40 | 41 | _read(size) { 42 | this.#file.resume(); 43 | } 44 | } 45 | 46 | const locs = new Set(); 47 | const loremFinder = new LoremFinder({ 48 | stream : createReadStream('./input.txt') 49 | }); 50 | const writeable = createWriteStream('./output.txt'); 51 | loremFinder.pipe(writeable) 52 | // loremFinder.on('data', (chunk) => { 53 | // const num = chunk.readUInt32BE(); 54 | // locs.add(num); 55 | // }); 56 | // loremFinder.on('end', () => { 57 | // console.log('here are all of the locations:'); 58 | // for(const val of locs) { 59 | // console.log('location: ', val); 60 | // } 61 | // console.log('number of lorems found is', locs.size); 62 | // }); -------------------------------------------------------------------------------- /Chapter07/duplex/main.js: -------------------------------------------------------------------------------- 1 | import { createConnection } from 'net' 2 | import { Duplex } from 'stream' 3 | import { createWriteStream } from 'fs' 4 | 5 | export default class MessageTranslator extends Duplex { 6 | #socket = null; 7 | #internalWriteBuf = new Map(); 8 | #internalReadHoldBuf = []; 9 | #internalPacketNum = 0; 10 | #readSize = 0; 11 | #writeCounter = 0; 12 | constructor(opts) { 13 | if(!opts.socket ) { 14 | throw new Error("MessageTranslator stream needs a socket!"); 15 | } 16 | super(opts); 17 | this.#socket = opts.socket; 18 | // we are assuming single message for each chunk 19 | this.#socket.on('data', (chunk) => { 20 | if(!this.#readSize ) { 21 | this.#internalPacketNum = chunk.readInt32BE(); 22 | this.#readSize = chunk.readInt32BE(4); 23 | this.#internalReadHoldBuf.push(chunk.slice(8)); 24 | this.#readSize -= chunk.byteLength - 8 25 | } else { 26 | this.#internalReadHoldBuf.push(chunk); 27 | this.#readSize -= chunk.byteLength; 28 | } 29 | // reached end of message 30 | if(!this.#readSize ) { 31 | this.push(Buffer.concat(this.#internalReadHoldBuf)); 32 | this.#internalReadHoldBuf = []; 33 | } 34 | }); 35 | } 36 | #processChunkHelper = function(chunk) { 37 | if(chunk.readInt32BE() === -1) { 38 | this.#internalWriteBuf.get(this.#writeCounter).done = true; 39 | this.#writeCounter += 1; 40 | this.#internalWriteBuf.set(this.#writeCounter, {buf : [], done : false}); 41 | } else { 42 | if(!this.#internalWriteBuf.has(this.#writeCounter)) { 43 | this.#internalWriteBuf.set(this.#writeCounter, {buf : [], done : false}); 44 | } 45 | this.#internalWriteBuf.get(this.#writeCounter).buf.push(chunk); 46 | } 47 | } 48 | #writeHelper = function(cb) { 49 | const writeOut = []; 50 | for(const [key, val] of this.#internalWriteBuf) { 51 | if( val.done ) { 52 | const cBuf = Buffer.allocUnsafe(4); 53 | const valBuf = Buffer.concat(val.buf); 54 | const sizeBuf = Buffer.allocUnsafe(4); 55 | cBuf.writeInt32BE(valBuf.readInt32BE()); 56 | sizeBuf.writeInt32BE(valBuf.byteLength - 4); 57 | writeOut.push(Buffer.concat([cBuf, sizeBuf, valBuf.slice(4)])); 58 | val.buf = []; 59 | } 60 | } 61 | if( writeOut.length ) { 62 | this.#socket.write(Buffer.concat(writeOut)); 63 | } 64 | cb(); 65 | } 66 | _writev(chunks, cb) { 67 | for(const chunk of chunks) { 68 | this.#processChunkHelper(chunk); 69 | } 70 | this.#writeHelper(cb); 71 | } 72 | _write(chunk, encoding, cb) { 73 | this.#processChunkHelper(chunk); 74 | this.#writeHelper(cb); 75 | } 76 | _read() { 77 | this.#socket.resume(); 78 | } 79 | _final(cb) { 80 | cb(); // nothing to do since it all should be consumed at this point 81 | } 82 | } 83 | const socket = createConnection(3334); 84 | const writer = createWriteStream('./output.txt'); 85 | const OneHelper = new MessageTranslator({ socket }); 86 | OneHelper.pipe(writer); -------------------------------------------------------------------------------- /Chapter07/duplex/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "Chapter7_Duplex", 3 | "version" : "0.0.1", 4 | "type" : "module" 5 | } -------------------------------------------------------------------------------- /Chapter07/generators/generators.js: -------------------------------------------------------------------------------- 1 | import { Readable } from 'stream' 2 | import { once } from 'events' 3 | import { createWriteStream } from 'fs' 4 | 5 | const data = [ 6 | "here is some data", 7 | "here is some more data", 8 | "here is some some more data", 9 | "we are going to finish our data" 10 | ] 11 | 12 | function* grabData() { 13 | while(data.length) { 14 | yield data.shift(); 15 | } 16 | } 17 | 18 | function* handleData() { 19 | let _char = 97; 20 | while(_char < 123 ) { //char code of 'z' 21 | yield String.fromCharCode(_char++); 22 | } 23 | } 24 | 25 | const readable = Readable.from(handleData()); 26 | readable.on('data', (chunk) => { 27 | console.log(chunk); 28 | }); 29 | 30 | (async() => { 31 | const readable2 = Readable.from(grabData()); 32 | const tempFile = createWriteStream('./temp.txt'); 33 | readable2.pipe(tempFile); 34 | await once(tempFile, 'finish'); 35 | console.log('all done'); 36 | })(); 37 | -------------------------------------------------------------------------------- /Chapter07/generators/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "Chapter7_Generators", 3 | "version" : "0.0.1", 4 | "type" : "module" 5 | } -------------------------------------------------------------------------------- /Chapter07/generators/temp.txt: -------------------------------------------------------------------------------- 1 | here is some datahere is some more datahere is some some more datawe are going to finish our data -------------------------------------------------------------------------------- /Chapter07/harness/client.js: -------------------------------------------------------------------------------- 1 | import { Socket } from 'net'; 2 | 3 | const socket = new Socket(); 4 | socket.on('data', (data) => { 5 | console.log('data', data); 6 | }) 7 | socket.connect({host : 'localhost', port : 3333}); -------------------------------------------------------------------------------- /Chapter07/harness/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "Chapter7_Harness", 3 | "version" : "0.0.1", 4 | "type" : "module" 5 | } -------------------------------------------------------------------------------- /Chapter07/harness/server.js: -------------------------------------------------------------------------------- 1 | import { createServer } from 'net' 2 | import WrappedWritableStream from '../writable/main.js' 3 | import MessageTranslator from '../duplex/main.js' 4 | 5 | const server = createServer((con) => { 6 | 7 | console.log('client connected. sending test data'); 8 | const wrapped = new WrappedWritableStream({ socket : con }); 9 | for(let i = 0; i < 100000; i++) { 10 | wrapped.write(`data${i}\r\n`); 11 | } 12 | wrapped.write(Buffer.from([0x00])); 13 | for(let i = 0; i < 100000; i++) { 14 | wrapped.write(`more_data${i}\r\n`); 15 | } 16 | wrapped.write(Buffer.from([0x00])); 17 | wrapped.end(); 18 | console.log('finished sending test data'); 19 | }); 20 | server.listen(3333); 21 | 22 | const duplexServer = createServer((con) => { 23 | console.log('client connected on duplex server. Sending test data'); 24 | const wrapped = new MessageTranslator({ socket : con }); 25 | const buf = Buffer.allocUnsafe(4); 26 | buf.writeInt32BE(1); 27 | wrapped.write(buf); 28 | for(let i = 0; i < 100000; i++) { 29 | wrapped.write(`duplex${i}\r\n`); 30 | } 31 | const endBuf = Buffer.allocUnsafe(4); 32 | endBuf.writeInt32BE(-1); 33 | wrapped.write(endBuf); 34 | wrapped.end(); 35 | console.log('finished sending test data on duplex server'); 36 | }).listen(3334) -------------------------------------------------------------------------------- /Chapter07/harness/writable_server.js: -------------------------------------------------------------------------------- 1 | import { createServer } from 'net' 2 | import WrappedWritableStream from '../writable/main.js' 3 | const server = createServer((con) => { 4 | console.log('client connected. sending test data'); 5 | const wrapped = new WrappedWritableStream({ socket : con }); 6 | for(let i = 0; i < 100000; i++) { 7 | wrapped.write(`data${i}\r\n`); 8 | } 9 | wrapped.write(Buffer.from([0x00])); 10 | wrapped.end(); 11 | console.log('finished sending test data'); 12 | }); 13 | server.listen(3333); -------------------------------------------------------------------------------- /Chapter07/readable/main.js: -------------------------------------------------------------------------------- 1 | import { createConnection } from 'net' 2 | import { Readable } from 'stream' 3 | import { createWriteStream } from 'fs' 4 | 5 | export default class ReadMessagePassStream extends Readable { 6 | #socket = null 7 | #bufBegin = Buffer.from("!!!START!!!") 8 | #bufEnd = Buffer.from("!!!END!!!") 9 | #internalBuffer = []; 10 | #size = 0; 11 | #data = function(chunk) { 12 | let i = -1 13 | if((i = chunk.indexOf(this.#bufBegin)) !== -1) { 14 | const tempBuf = chunk.slice(i + this.#bufBegin.byteLength); 15 | this.#size += tempBuf.byteLength; 16 | this.#internalBuffer.push(tempBuf); 17 | 18 | } 19 | else if((i = chunk.indexOf(this.#bufEnd)) !== -1) { 20 | const tempBuf = chunk.slice(0, i); 21 | this.#size += tempBuf.byteLength; 22 | this.#internalBuffer.push(tempBuf); 23 | const final = Buffer.concat(this.#internalBuffer); 24 | this.#internalBuffer = []; 25 | if(!this.push(final)) { 26 | this.#socket.pause(); 27 | } 28 | } else { 29 | this.#size += chunk.byteLength; 30 | this.#internalBuffer.push(chunk); 31 | } 32 | } 33 | constructor(options) { 34 | if( options.objectMode ) { 35 | options.objectMode = false //we don't want it on 36 | } 37 | super(options); 38 | if(!options.socket ) { 39 | throw "Need a socket to attach to!" 40 | } 41 | this.#socket = options.socket; 42 | this.#socket.on('data', this.#data.bind(this)); 43 | this.#socket.on('end', () => this.push(null)); 44 | } 45 | _read(size) { 46 | this.#socket.resume(); 47 | } 48 | } 49 | 50 | const socket = createConnection(3333); 51 | const write = createWriteStream('./output.txt'); 52 | const messageStream = new ReadMessagePassStream({ socket }); 53 | messageStream.pipe(write); -------------------------------------------------------------------------------- /Chapter07/readable/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "Chapter7_Readable", 3 | "version" : "0.0.1", 4 | "type" : "module" 5 | } -------------------------------------------------------------------------------- /Chapter07/transform/main.js: -------------------------------------------------------------------------------- 1 | import { Transform } from 'stream' 2 | 3 | // implemented in stream form from https://stackoverflow.com/questions/7616461/generate-a-hash-from-string-in-javascript 4 | export default class StreamHashCreator extends Transform { 5 | #currHash = 0; 6 | constructor(options={}) { 7 | if( options.objectMode ) { 8 | throw new Error("This stream does not support object mode!"); 9 | } 10 | options.decodeStrings = true; 11 | super(options); 12 | } 13 | _transform(chunk, encoding, callback) { 14 | if( Buffer.isBuffer(chunk) ) { 15 | const str = chunk.toString('utf8'); 16 | for(let i = 0; i < str.length; i++) { 17 | const char = str.charCodeAt(i); 18 | this.#currHash = ((this.#currHash << 5) - this.#currHash ) + char; 19 | this.#currHash |= 0; 20 | } 21 | } 22 | callback(); 23 | } 24 | _flush(callback) { 25 | const buf = Buffer.alloc(4); 26 | buf.writeInt32BE(this.#currHash); 27 | this.push(buf); 28 | callback(null); 29 | } 30 | } 31 | 32 | const hasher = new StreamHashCreator(); 33 | hasher.on('data', (data) => { 34 | console.log('our hash is', data.readInt32BE()); 35 | }) 36 | hasher.write("Here is some data"); 37 | hasher.write("Here is some more data"); 38 | hasher.end(); 39 | 40 | const hasher2 = new StreamHashCreator(); 41 | hasher2.on('data', (data) => { 42 | console.log('our has is', data.readInt32BE()); 43 | }); 44 | hasher2.write("Here is some more data"); 45 | hasher2.write("Here is also some more data"); 46 | hasher2.end(); 47 | 48 | const hasher3 = new StreamHashCreator(); 49 | hasher3.on('data', (data) => { 50 | console.log('out hash is', data.readInt32BE()); 51 | }); 52 | hasher3.write("Here is some more data"); 53 | hasher3.write("Here is some data"); 54 | hasher3.end(); 55 | 56 | const hasher4 = new StreamHashCreator(); 57 | hasher4.on('data', (data) => { 58 | console.log('our hash is', data.readInt32BE()); 59 | }); 60 | hasher4.write("Here is some data"); 61 | hasher4.write("Here is some more data"); 62 | hasher4.end(); -------------------------------------------------------------------------------- /Chapter07/transform/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "Chapter7_Transform", 3 | "version" : "0.0.1", 4 | "type" : "module" 5 | } -------------------------------------------------------------------------------- /Chapter07/writable/main.js: -------------------------------------------------------------------------------- 1 | import { Writable } from 'stream' 2 | 3 | export default class WriteMessagePassStream extends Writable { 4 | #socket = null; 5 | #writing = false; 6 | constructor(options) { 7 | if( options.objectMode ) { //make sure we do not turn on object mode 8 | options.objectMode = false; 9 | } 10 | if(!options.socket ) { 11 | throw new Error("A socket is required to contruct this stream!"); 12 | } 13 | super(options); 14 | this.#socket = options.socket; 15 | } 16 | _write(chunk, encoding, callback) { 17 | if(!this.#writing) { 18 | this.#writing = true; 19 | this.#socket.write("!!!START!!!"); 20 | } 21 | let i = -1; 22 | let numCount = 0; 23 | let prevI = 0; 24 | while((i = chunk.indexOf(0x00, i)) !== -1) { 25 | const buf = chunk.slice(prevI, i); 26 | this.#socket.write(buf); 27 | this.#socket.write("!!!END!!!"); 28 | if( i !== chunk.byteLength - 1) { // we are not at the end of the chunk start new frame 29 | this.#socket.write("!!!START!!!"); 30 | } else { 31 | return callback(); 32 | } 33 | numCount += 1; 34 | } 35 | if(!numCount ) { 36 | this.#socket.write(chunk); 37 | } 38 | return callback(); 39 | } 40 | } -------------------------------------------------------------------------------- /Chapter07/writable/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "Chapter7_Writable", 3 | "version" : "0.0.1", 4 | "type" : "module" 5 | } -------------------------------------------------------------------------------- /Chapter08/json/jsonformat.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "Bob", 3 | "birth" : "01/02/1993", 4 | "address" : { 5 | "zipcode" : 11111, 6 | "street" : "avenue of av.", 7 | "streetnumber" : 123, 8 | "state" : "CA", 9 | "country" : "US" 10 | }, 11 | "contact" : { 12 | "primary" : "111-222-3333", 13 | "secondary" : "444-555-6666", 14 | "email" : "bob@bob.com" 15 | } 16 | } -------------------------------------------------------------------------------- /Chapter08/json/main.js: -------------------------------------------------------------------------------- 1 | import json from './jsonformat.json'; 2 | import { gzipSync } from 'zlib'; 3 | 4 | const send = new Array(100); 5 | send.fill(json); 6 | console.log('size of over the wire buffer is: ', Buffer.from(JSON.stringify(send)).byteLength); 7 | console.log('size of gzipped version is: ', gzipSync(Buffer.from(JSON.stringify(send))).byteLength); 8 | 9 | const send2 = new Array(500); 10 | send2.fill(json); 11 | console.log('size of over the wire buffer is: ', Buffer.from(JSON.stringify(send2)).byteLength); 12 | console.log('size of gzipped version is: ', gzipSync(Buffer.from(JSON.stringify(send2))).byteLength); -------------------------------------------------------------------------------- /Chapter08/json/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version" : "0.0.1", 3 | "name" : "json", 4 | "type" : "module" 5 | } -------------------------------------------------------------------------------- /Chapter08/messagepack/main.js: -------------------------------------------------------------------------------- 1 | import MessagePack from 'what-the-pack'; 2 | import json from '../schema/test.json'; 3 | 4 | const { encode, decode } = MessagePack.initialize(2**22); 5 | 6 | const encoded = encode(json); 7 | const decoded = decode(encoded); 8 | 9 | console.log(encoded.byteLength, Buffer.from(JSON.stringify(decoded)).byteLength); 10 | 11 | console.log(encoded, decoded); -------------------------------------------------------------------------------- /Chapter08/messagepack/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.0.1", 3 | "type": "module", 4 | "name": "message_pack_example", 5 | "dependencies": { 6 | "what-the-pack": "^2.0.3" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Chapter08/protobuf/main.js: -------------------------------------------------------------------------------- 1 | import protobuf from 'protobufjs'; 2 | import json from '../schema/test.json'; 3 | 4 | const TestType = new protobuf.Type("TestType"); 5 | TestType.add(new protobuf.Field("item1", 1, "string")); 6 | TestType.add(new protobuf.Field("item2", 2, "int32")); 7 | TestType.add(new protobuf.Field("item3", 3, "float")); 8 | 9 | if( TestType.verify(json) ) { 10 | throw Error("Invalid type!"); 11 | } 12 | 13 | const message = TestType.create(json); 14 | const buf = TestType.encode(message).finish(); 15 | const final = TestType.decode(buf); 16 | console.log(buf.byteLength, Buffer.from(JSON.stringify(final)).byteLength); 17 | console.log(buf, final); 18 | 19 | protobuf.load('test.proto', function(err, root) { 20 | if( err ) throw err; 21 | 22 | const TestTypeProto = root.lookupType("exampleProtobuf.TestData"); 23 | if( TestTypeProto.verify(json) ) { 24 | throw Error("Invalid type!"); 25 | } 26 | 27 | const message2 = TestTypeProto.create(json); 28 | const buf2 = TestTypeProto.encode(message2).finish(); 29 | const final2 = TestTypeProto.decode(buf2); 30 | console.log(buf2.byteLength, Buffer.from(JSON.stringify(final2)).byteLength); 31 | console.log(buf2, final2); 32 | }); -------------------------------------------------------------------------------- /Chapter08/protobuf/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.0.1", 3 | "type": "module", 4 | "name": "protobuf_example", 5 | "dependencies": { 6 | "protobufjs": "^6.8.8" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Chapter08/protobuf/test.proto: -------------------------------------------------------------------------------- 1 | package exampleProtobuf; 2 | syntax = "proto3"; 3 | 4 | message TestData { 5 | string item1 = 1; 6 | int32 item2 = 2; 7 | float item3 = 3; 8 | } -------------------------------------------------------------------------------- /Chapter08/schema/decoder.js: -------------------------------------------------------------------------------- 1 | import { Transform } from 'stream' 2 | import { decodeString, decodeNumber, CONSTANTS } from './helper.js' 3 | 4 | export default class SimpleSchemaReader extends Transform { 5 | #obj = {} 6 | #inHeaders = false 7 | #inBody = false 8 | #keys = [] 9 | #currKey = 0 10 | #decode = function(chunk, index, type='headers') { 11 | const item = chunk[index] === CONSTANTS.string ? 12 | decodeString(chunk.slice(index)) : 13 | decodeNumber(chunk.slice(index, index + 5)); 14 | 15 | if( type === 'headers' ) { 16 | this.#obj[item] = null; 17 | } else { 18 | this.#obj[this.#keys[this.#currKey]] = item; 19 | } 20 | return chunk[index] === CONSTANTS.string ? 21 | index + item.length + 5 : 22 | index + 5; 23 | } 24 | 25 | constructor(opts={}) { 26 | opts.readableObjectMode = true; 27 | super(opts); 28 | } 29 | 30 | _transform(chunk, encoding, callback) { 31 | let index = 0; 32 | while(index <= chunk.byteLength ) { 33 | const byte = chunk[index]; 34 | if( byte === CONSTANTS.header ) { 35 | this.#inHeaders = !this.#inHeaders 36 | index += 1; 37 | continue; 38 | } else if( byte === CONSTANTS.body ) { 39 | this.#inBody = !this.#inBody 40 | if(!this.#inBody ) { 41 | this.push(this.#obj); 42 | this.#obj = {}; 43 | this.#keys = []; 44 | this.#currKey = 0; 45 | return callback(); 46 | } else { 47 | this.#keys = Object.keys(this.#obj); 48 | } 49 | index += 1; 50 | continue; 51 | } 52 | if( this.#inHeaders ) { 53 | index = this.#decode(chunk, index); 54 | } else if( this.#inBody ) { 55 | index = this.#decode(chunk, index, 'body'); 56 | this.#currKey += 1; 57 | } else { 58 | callback(new Error("Unkown state!")); 59 | } 60 | } 61 | } 62 | } -------------------------------------------------------------------------------- /Chapter08/schema/encoder.js: -------------------------------------------------------------------------------- 1 | import { Transform } from 'stream' 2 | import { encodeString, encodeNumber } from './helper.js' 3 | 4 | export default class SimpleSchemaWriter extends Transform { 5 | #encode = function(data) { 6 | return typeof data === 'string' ? 7 | encodeString(data) : 8 | typeof data === 'number' ? 9 | encodeNumber(data) : 10 | null; 11 | } 12 | 13 | constructor(opts={}) { 14 | opts.writableObjectMode = true; 15 | super(opts); 16 | } 17 | 18 | _transform(chunk, encoding, callback) { 19 | console.log(Buffer.from(JSON.stringify(chunk)).byteLength); 20 | const buf = []; 21 | buf.push(Buffer.from([0x10])); 22 | for(const key of Object.keys(chunk)) { 23 | const item = this.#encode(key); 24 | if(item === null) { 25 | return callback(new Error("Unable to parse!")) 26 | } 27 | buf.push(item); 28 | } 29 | buf.push(Buffer.from([0x10])); 30 | buf.push(Buffer.from([0x11])); 31 | for(const val of Object.values(chunk)) { 32 | const item = this.#encode(val); 33 | if(item === null) { 34 | return callback(new Error("Unable to parse!")) 35 | } 36 | buf.push(item); 37 | } 38 | buf.push(Buffer.from([0x11])); 39 | console.log(Buffer.concat(buf).byteLength); 40 | this.push(Buffer.concat(buf)); 41 | callback(); 42 | } 43 | } -------------------------------------------------------------------------------- /Chapter08/schema/example.js: -------------------------------------------------------------------------------- 1 | const keys = ['item1', 'item2', 'item3']; 2 | const values = [1, 'what', 2.2]; 3 | const tempObj = {}; 4 | 5 | for(let i = 0; i < keys.length; i++) { 6 | tempObj[keys[i]] = null; 7 | } 8 | for(let i = 0; i < values.length; i++) { 9 | tempObj[keys[i]] = values[i]; 10 | } 11 | 12 | console.log(tempObj); -------------------------------------------------------------------------------- /Chapter08/schema/helper.js: -------------------------------------------------------------------------------- 1 | export const CONSTANTS = { 2 | object : 0x04, 3 | number : 0x01, 4 | floating : 0x02, 5 | string : 0x03, 6 | header : 0x10, 7 | body : 0x11 8 | } 9 | 10 | export const encodeString = function(str) { 11 | const buf = Buffer.from(str); 12 | const len = Buffer.alloc(4); 13 | len.writeUInt32BE(buf.byteLength); 14 | return Buffer.concat([Buffer.from([0x03]), len, buf]); 15 | } 16 | 17 | export const decodeString = function(buf) { 18 | if(buf[0] !== CONSTANTS.string) { 19 | return false; 20 | } 21 | const len = buf.readUInt32BE(1); 22 | return buf.slice(5, 5 + len).toString('utf8'); 23 | } 24 | 25 | export const encodeNumber = function(num) { 26 | const type = Math.abs(num) === num ? 0x01 : 0x02; 27 | const buf = Buffer.alloc(4); 28 | buf.writeInt32BE(num); 29 | return Buffer.concat([Buffer.from([type]), buf]); 30 | } 31 | 32 | export const decodeNumber = function(buf) { 33 | return buf.readInt32BE(1); 34 | } -------------------------------------------------------------------------------- /Chapter08/schema/main.js: -------------------------------------------------------------------------------- 1 | import encoder from './encoder.js' 2 | import decoder from './decoder.js' 3 | import json from './test.json' 4 | 5 | const enc = new encoder(); 6 | const dec = new decoder(); 7 | enc.pipe(dec); 8 | dec.on('data', (obj) => { 9 | console.log(obj); 10 | }); 11 | enc.write(json); -------------------------------------------------------------------------------- /Chapter08/schema/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version" : "0.0.1", 3 | "name" : "schema", 4 | "type" : "module" 5 | } -------------------------------------------------------------------------------- /Chapter08/schema/test.json: -------------------------------------------------------------------------------- 1 | { 2 | "item1" : "item", 3 | "item2" : 12, 4 | "item3" : 3.3 5 | } -------------------------------------------------------------------------------- /Chapter08/schema/test2.json: -------------------------------------------------------------------------------- 1 | { 2 | "item1" : "item", 3 | "item2" : 120000000, 4 | "item3" : 3.3, 5 | "item4" : 120000000, 6 | "item5" : 120000000, 7 | "item6" : 120000000 8 | } -------------------------------------------------------------------------------- /Chapter09/microserve/cache.js: -------------------------------------------------------------------------------- 1 | export default class LRUCache { 2 | #cache = new Map() 3 | #numEntries = 10 4 | constructor(num=10) { 5 | this.#numEntries = num; 6 | } 7 | 8 | add(file, url) { 9 | const val = { 10 | page : file, 11 | time : Date.now() 12 | } 13 | console.log('we added a file!') 14 | if( this.#cache.size === this.#numEntries ) { 15 | console.log('we replaced a file!'); 16 | let top = Number.MAX_VALUE; 17 | let earliest = null; 18 | for(const [key, val] of this.#cache) { 19 | if( val.time < top ) { 20 | top = val.time; 21 | earliest = key; 22 | } 23 | } 24 | this.#cache.delete(earliest); 25 | } 26 | this.#cache.set(url, val); 27 | } 28 | 29 | get(url) { 30 | const val = this.#cache.get(url); 31 | if( val ) { 32 | console.log('we grabbed a file!'); 33 | val.time = Date.now(); 34 | this.#cache.set(url, val); 35 | return val.page; 36 | } 37 | return null; 38 | } 39 | } -------------------------------------------------------------------------------- /Chapter09/microserve/main.js: -------------------------------------------------------------------------------- 1 | import http2 from 'http2' 2 | import fs from 'fs' 3 | import cluster from 'cluster' 4 | import path from 'path' 5 | import cache from './cache.js' 6 | import os from 'os'; 7 | import {PassThrough} from 'stream' 8 | import {LoopingStream} from './template.js' 9 | 10 | const ENV_VARS = process.env; 11 | 12 | const port = ENV_VARS.npm_package_config_port || 80; 13 | const key = ENV_VARS.npm_package_config_key || 'key.pem'; 14 | const cert = ENV_VARS.npm_package_config_certificate || 'cert.pem'; 15 | const templateDirectory = ENV_VARS.npm_package_config_template || 'template'; 16 | const publishedDirectory = ENV_VARS.npm_package_config_bodyfiles || 'body'; 17 | const developmentMode = ENV_VARS.npm_package_config_development || true; 18 | 19 | const FILE_TYPES = new Map([ 20 | ['.css', path.join('.', templateDirectory, 'css')], 21 | ['.html', path.join('.', templateDirectory, 'html')] 22 | ]); 23 | if( cluster.isMaster ) { 24 | const numCpus = os.cpus().length; 25 | for(let i = 0; i < numCpus; i++) { 26 | cluster.fork(); 27 | } 28 | cluster.on('exit', (worker, code, signal) => { 29 | console.log(`worker ${worker.process.pid} died`); 30 | }); 31 | } else { 32 | const serverCache = new cache(); 33 | const server = http2.createSecureServer({ 34 | key: fs.readFileSync(key), 35 | cert: fs.readFileSync(cert) 36 | }); 37 | server.on('error', (err) => { 38 | console.error(err); 39 | process.exit(); 40 | }); 41 | server.on('stream', (stream, headers) => { 42 | const p = headers[':path']; 43 | for(const [fileType, loc] of FILE_TYPES) { 44 | if( p.endsWith(fileType) ) { 45 | stream.respondWithFile( 46 | path.join(loc, path.posix.basename(p)), 47 | { 48 | 'content-type': `text/${fileType.slice(1)}`, 49 | ':status': 200 50 | } 51 | ); 52 | return; 53 | } 54 | } 55 | try { 56 | const f = fs.statSync(path.join('.', publishedDirectory, `${p}.md`)); 57 | stream.respond({ 58 | 'content-type': 'text/html', 59 | ':status': 200 60 | }); 61 | const cacheHit = serverCache.get(p); 62 | if( cacheHit ) { 63 | stream.end(cacheHit); 64 | } else { 65 | const file = fs.createReadStream('./template/main.html'); 66 | const tStream = new LoopingStream({ 67 | dir: templateDirectory, 68 | publish: publishedDirectory, 69 | vars : { 70 | articles : [ 71 | { 72 | location : 'temp1', 73 | name : 'article 1' 74 | }, 75 | { 76 | location : 'temp2', 77 | name : 'article 2' 78 | }, 79 | { 80 | location : 'temp3', 81 | name : 'article 3' 82 | }, 83 | { 84 | location : 'temp4', 85 | name : 'article 4' 86 | }, 87 | { 88 | location : 'temp5', 89 | name : 'article 5' 90 | } 91 | ], 92 | fileToProcess : `${p}.md` 93 | }, 94 | loopAmount : 2 95 | }); 96 | file.pipe(tStream); 97 | tStream.once('data', (data) => { 98 | serverCache.add(data, p); 99 | stream.end(data); 100 | }); 101 | } 102 | } catch(e) { 103 | stream.respond({ 104 | 'content-type': 'text/html', 105 | ':status' : 404 106 | }); 107 | stream.end('File Not Found! Turn Back!'); 108 | console.warn('following file requested and not found! ', p); 109 | } 110 | }); 111 | console.log('port', port); 112 | server.listen(port); 113 | } -------------------------------------------------------------------------------- /Chapter09/microserve/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.0.1", 3 | "name": "microserver", 4 | "type": "module", 5 | "dependencies": { 6 | "remarkable": "^2.0.0" 7 | }, 8 | "config" : { 9 | "port" : 50000, 10 | "key" : "selfsignedkey.pem", 11 | "certificate" : "selfsignedcertificate.pem", 12 | "template" : "template", 13 | "bodyfiles" : "publish", 14 | "development" : true 15 | }, 16 | "scripts" : { 17 | "start": "node --experimental-modules main.js" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Chapter09/microserve/publish/first.md: -------------------------------------------------------------------------------- 1 | # This is our first article -------------------------------------------------------------------------------- /Chapter09/microserve/publish/further/three.md: -------------------------------------------------------------------------------- 1 | # This is inside our further directory! -------------------------------------------------------------------------------- /Chapter09/microserve/publish/second.md: -------------------------------------------------------------------------------- 1 | # Another file! -------------------------------------------------------------------------------- /Chapter09/microserve/template/css/main.css: -------------------------------------------------------------------------------- 1 | *, html { 2 | margin : 0; 3 | padding : 0; 4 | } 5 | 6 | :root { 7 | --main-color : "#003A21"; 8 | --text-color : "#efefef"; 9 | } 10 | 11 | /* header styles */ 12 | header { 13 | display : block; 14 | height : 2rem; 15 | margin : 10px 15px; 16 | overflow : hidden; 17 | position : fixed; 18 | top : 0; 19 | width : 100%; 20 | background : var(--main-color); 21 | color : var(--text-color); 22 | } 23 | header h1 { 24 | float : left; 25 | } 26 | 27 | header nav { 28 | float : right; 29 | } 30 | 31 | /* Footer styles */ 32 | footer { 33 | position : fixed; 34 | left : 0; 35 | bottom : 0; 36 | width : 100%; 37 | background : var(--main-color); 38 | color : var(--text-color); 39 | } 40 | footer h2 { 41 | float : left; 42 | } 43 | footer a { 44 | float : right; 45 | } 46 | 47 | /* navbar */ 48 | nav { 49 | width : 25%; 50 | display : block; 51 | } 52 | 53 | #content { 54 | width : 75%; 55 | display : block; 56 | } -------------------------------------------------------------------------------- /Chapter09/microserve/template/html/footer.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Chapter09/microserve/template/html/header.html: -------------------------------------------------------------------------------- 1 |
    2 |

    Our Website

    3 | 8 |
    -------------------------------------------------------------------------------- /Chapter09/microserve/template/html/sidebar.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Chapter09/microserve/template/main.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | <% from html header %> 7 | <% from html sidebar %> 8 |
    9 | <% file base.md %> 10 |
    11 | <% from html footer %> 12 | 13 | -------------------------------------------------------------------------------- /Chapter10/cache.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |
    Customer NameZipcodePhone NumberEmailBuy Order AmountSell Order Amount
    30 | 100 | 101 | -------------------------------------------------------------------------------- /Chapter10/cache_shared.js: -------------------------------------------------------------------------------- 1 | importScripts('./mock_customer_data.js'); 2 | 3 | const handleReq = function(arr) { 4 | const res = new Array(arr.length) 5 | for(let i = 0; i < arr.length; i++) { 6 | const num = arr[i]; 7 | for(let j = 0; j < cache.length; j++) { 8 | if( num === cache[j].id ) { 9 | res[i] = cache[j]; 10 | break; 11 | } 12 | } 13 | } 14 | return res; 15 | } 16 | 17 | onconnect = function(e) { 18 | let port = e.ports[0]; 19 | port.onmessage = function(e) { 20 | const request = e.data; 21 | if( request.id && 22 | Array.isArray(request.data) ) { 23 | const response = handleReq(request.data); 24 | console.log('we reached this point', response, request); 25 | port.postMessage({ 26 | id : request.id, 27 | data : response 28 | }); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /Chapter10/largeObject.js: -------------------------------------------------------------------------------- 1 | // const dataToSend = new Array(1000000); 2 | // const baseObj = {prop1 : 1, prop2 : 'one'}; 3 | // for(let i = 0; i < dataToSend.length; i++) { 4 | // dataToSend[i] = Object.assign({}, baseObj); 5 | // dataToSend[i].prop1 = i; 6 | // dataToSend[i].prop2 = `Data for ${i}`; 7 | // } 8 | 9 | // console.log('send at', Date.now()); 10 | // postMessage(dataToSend); 11 | const viewOfData = new Int32Array(1000000); 12 | for(let i = 1; i <= viewOfData.length; i++) { 13 | viewOfData[i] = i; 14 | } 15 | console.log('data sent', Date.now()); 16 | postMessage(viewOfData, [viewOfData.buffer]); 17 | console.log('length of our buffer', viewOfData.byteLength); -------------------------------------------------------------------------------- /Chapter10/shared.js: -------------------------------------------------------------------------------- 1 | const ports = []; 2 | let data = null; 3 | const buf = new SharedArrayBuffer(4); 4 | 5 | const add = function(a, b) { 6 | return a + b; 7 | } 8 | 9 | const mult = function(a, b) { 10 | return a * b; 11 | } 12 | 13 | const divide = function(a, b) { 14 | return a / b; 15 | } 16 | 17 | const subtract = function(a, b) { 18 | return a - b; 19 | } 20 | 21 | onconnect = function(e) { 22 | let port = e.ports[0]; 23 | port.onmessage = function(e) { 24 | console.log(e); 25 | if( typeof e.data === 'object' ) { 26 | console.log('was this hit?'); 27 | data = new Int32Array(e.data); 28 | } 29 | console.log(e.data); 30 | const _d = e.data.split(' '); 31 | const in1 = parseInt(_d[1]); 32 | const in2 = parseInt(_d[2]); 33 | switch(_d[0]) { 34 | case 'add': { 35 | Atomics.store(data,0 ,add(in1, in2)); 36 | //port.postMessage(add(in1, in2)); 37 | break; 38 | } 39 | case 'subtract': { 40 | Atomics.store(data,0 , subtract(in1, in2)); 41 | //port.postMessage(subtract(in1, in2)); 42 | break; 43 | } 44 | case 'multiply': { 45 | Atomics.store(data,0 ,multiply(in1, in2)); 46 | //port.postMessage(mult(in1, in2)); 47 | break; 48 | } 49 | case 'divide': { 50 | Atomics.store(data,0 ,divide(in1, in2)); 51 | //port.postMessage(divide(in1, in2)); 52 | break; 53 | } 54 | } 55 | } 56 | console.log(buf); 57 | port.postMessage('here is data'); 58 | ports.push(port); 59 | } -------------------------------------------------------------------------------- /Chapter10/test.js: -------------------------------------------------------------------------------- 1 | const channel = new BroadcastChannel('workers'); 2 | channel.onmessage = function(ev) { 3 | console.log(ev.data, 'was received by', name); 4 | } -------------------------------------------------------------------------------- /Chapter10/worker.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |

    The primes for the number is:

    7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |

    15 | 16 |

    17 | 112 | 113 | -------------------------------------------------------------------------------- /Chapter10/worker.js: -------------------------------------------------------------------------------- 1 | let sharedPort = null; 2 | 3 | function calculatePrimes(val) { 4 | let numForPrimes = val; 5 | const primes = []; 6 | while( numForPrimes % 2 === 0 ) { 7 | primes.push(2); 8 | numForPrimes /= 2; 9 | } 10 | for(let i = 3; i <= Math.sqrt(numForPrimes); i+=2) { 11 | while( numForPrimes % i === 0 ) { 12 | primes.push(i); 13 | numForPrimes /= i; 14 | } 15 | } 16 | if( numForPrimes > 2 ) { 17 | primes.push(numForPrimes); 18 | } 19 | return primes; 20 | } 21 | onmessage = function(ev) { 22 | const data = ev.data; 23 | if( typeof data === 'string' ) { 24 | if( data === 'quit' ) { 25 | return close(); 26 | } else { 27 | sharedPort.postMessage(data); 28 | } 29 | } 30 | if( typeof data === 'number' ) { 31 | const result = calculatePrimes(data); 32 | const send = new Int32Array(result); 33 | return postMessage(send, [send.buffer]); 34 | } 35 | // handle the port 36 | sharedPort = data; 37 | console.log(sharedPort); 38 | sharedPort.onmessage = function(ev) { 39 | console.log('data', ev.data); 40 | } 41 | } 42 | // const mainChannelName = name.includes("odd") ? "odd" : "even"; 43 | // const mainChannel = new BroadcastChannel(mainChannelName); 44 | // mainChannel.onmessage = function(ev) { 45 | // if( typeof ev.data === 'number' ) { 46 | // const result = calculatePrimes(ev.data); 47 | // const send = new Int32Array(result); 48 | // this.postMessage(result, [result.buffer]); 49 | // } 50 | // } 51 | // const globalChannel = new BroadcastChannel('global'); 52 | // globalChannel.onmessage = function(ev) { 53 | // if( ev.data === 'quit' ) { 54 | // close(); 55 | // } 56 | // } 57 | 58 | -------------------------------------------------------------------------------- /Chapter10/worker_to_shared.js: -------------------------------------------------------------------------------- 1 | let sharedPort = null; 2 | let buf = null; 3 | onmessage = function(ev) { 4 | const data = ev.data; 5 | if( typeof data === 'number' ) { 6 | Atomics.add(buf, 0, 1); 7 | } else { 8 | buf = new Int32Array(ev.data); 9 | } 10 | } -------------------------------------------------------------------------------- /Chapter11/app.js: -------------------------------------------------------------------------------- 1 | const handler = require('serve-handler'); 2 | const http = require('http'); 3 | 4 | const server = http.createServer((req, res) => { 5 | return handler(req, res, { 6 | public : 'offline_storage' 7 | }); 8 | }); 9 | 10 | server.listen(3000, () => { 11 | console.log('listening at 3000'); 12 | }) 13 | -------------------------------------------------------------------------------- /Chapter11/offline_storage/OfflineCacheWorker.js: -------------------------------------------------------------------------------- 1 | self.addEventListener('install', (event) => { 2 | event.waitUntil( 3 | caches.delete('v1').then(() => { 4 | caches.open('v1').then((cache) => { 5 | return cache.addAll([ 6 | '/', 7 | './interactions.js', 8 | './main.css' 9 | ]); 10 | }); 11 | }) 12 | ); 13 | }); 14 | const controller = new AbortController(); 15 | const signal = controller.signal; 16 | const pollTime = 30000; 17 | self.addEventListener('fetch', (event) => { 18 | event.respondWith( 19 | caches.match(event.request).then((response) => { 20 | if( response ) { 21 | return response 22 | } 23 | if( event.request.url.includes('/stop') ) { 24 | controller.abort(); 25 | return new Response(new Blob(["all done"], {type : 'text/plain'}), {status : 200}); 26 | } 27 | if(!navigator.onLine ) { 28 | return new Promise((resolve, reject) => { 29 | const interval = setInterval(() => { 30 | if( navigator.onLine ) { 31 | clearInterval(interval); 32 | resolve(actualRequestHandler(event)); 33 | } 34 | }, pollTime) 35 | signal.addEventListener('abort', () => { 36 | reject('aborted'); 37 | }) 38 | }); 39 | } else { 40 | return actualRequestHandler(event); 41 | } 42 | }) 43 | ) 44 | }); 45 | 46 | let counter = 0; 47 | let name = 65; 48 | const handleRequest = function() { 49 | const data = { 50 | id : counter, 51 | name : String.fromCharCode(name), 52 | phone : Math.round(Math.random() * 10000) 53 | } 54 | counter += 1; 55 | name += 1; 56 | return new Response(new Blob([JSON.stringify(data)], {type : 'application/json'}), {status : 200}); 57 | } 58 | 59 | const handleDelete = function(url) { 60 | const id = url.split("/")[2]; 61 | return new Response(new Blob([id], {type : 'text/plain'}), {status : 200}); 62 | } 63 | 64 | const actualRequestHandler = function(req) { 65 | if( req.request.url.includes('/request') ) { 66 | return handleRequest(); 67 | } 68 | if( req.request.url.includes('/delete') ) { 69 | return handleDelete(req.request.url); 70 | } 71 | fetch(req.request); 72 | } -------------------------------------------------------------------------------- /Chapter11/offline_storage/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Offline Storage Example 4 | 5 | 6 | 7 |

    Offline Storage

    8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
    idnamephonedelete
    20 |

    Are we online?: No

    21 |

    Oustanding requests: 0

    22 | 23 | 24 | 43 | 44 | -------------------------------------------------------------------------------- /Chapter11/offline_storage/interactions.js: -------------------------------------------------------------------------------- 1 | const requestMaker = document.querySelector('#makeRequest'); 2 | const tableBody = document.querySelector('#body'); 3 | const requestAmount = document.querySelector('#outstanding'); 4 | const stopRequests = document.querySelector('#stop'); 5 | 6 | let numRequests = 0; 7 | requestMaker.addEventListener('click', (ev) => { 8 | numRequests += 1; 9 | requestAmount.textContent = numRequests; 10 | fetch('/request').then((res) => res.json()).then((fin) => { 11 | const row = document.createElement('tr'); 12 | row.innerHTML = ` 13 | ${fin.id} 14 | ${fin.name} 15 | ${fin.phone} 16 | 17 | ` 18 | row.querySelector('button').addEventListener('click', (ev) => { 19 | fetch(`/delete/${ev.target.id}`).then(() => { 20 | tableBody.removeChild(row); 21 | }); 22 | }); 23 | tableBody.appendChild(row); 24 | numRequests -= 1; 25 | requestAmount.textContent = numRequests; 26 | }) 27 | }); 28 | stopRequests.addEventListener('click', (ev) => { 29 | fetch('/stop').then((res) => { 30 | numRequests = 0; 31 | requestAmount.textContent = numRequests; 32 | }); 33 | }); -------------------------------------------------------------------------------- /Chapter11/offline_storage/main.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Hands-On-High-Performance-Web-Development-with-JavaScript/b2fdd0a44074042406721b329d107bb0a428a88f/Chapter11/offline_storage/main.css -------------------------------------------------------------------------------- /Chapter11/source/BaseServiceWorker.js: -------------------------------------------------------------------------------- 1 | self.addEventListener('install', (event) => { 2 | event.waitUntil( 3 | caches.open('v1').then((cache) => { 4 | return cache.addAll([ 5 | './main.css', 6 | '/' 7 | ]); 8 | }).then(() => { 9 | console.log('we are ready!'); 10 | }) 11 | ); 12 | }); 13 | self.addEventListener('fetch', (event) => { 14 | event.respondWith( 15 | caches.match(event.request).then((response) => { 16 | return response || fetch(event.request); 17 | }) 18 | ) 19 | }); -------------------------------------------------------------------------------- /Chapter11/source/CacheServiceWorker.js: -------------------------------------------------------------------------------- 1 | self.addEventListener('install', (event) => { 2 | event.waitUntil( 3 | caches.delete('v1').then(() => { 4 | caches.open('v1').then((cache) => { 5 | return cache.addAll([ 6 | '/', 7 | './interactions.js', 8 | './main.css' 9 | ]); 10 | }); 11 | }) 12 | ); 13 | }); 14 | const add = {id : 1, name : 'bob', description : 'knight', points : 100}; 15 | self.addEventListener('fetch', (event) => { 16 | event.respondWith( 17 | caches.match(event.request).then((response) => { 18 | if( event.request.url.includes('/add') ) { 19 | return fetch('./row.template') 20 | .then((res) => res.text()) 21 | .then((template) => { 22 | return new Response(new Blob([renderTemplate(template, add)], {type : 'text/html'}), {status : 200}); 23 | }) 24 | } else if( response ) { 25 | return response 26 | } else { 27 | fetch(event.request); 28 | } 29 | }) 30 | ) 31 | }); 32 | 33 | const renderTemplate = function(template, obj) { 34 | const regex = /\${([a-zA-Z0-9]+)\}/; 35 | const keys = Object.keys(obj); 36 | let match = null; 37 | while(match = regex.exec(template)) { 38 | const key = match[1]; 39 | console.log(match); 40 | if( keys.includes(key) ) { 41 | template = template.replace(match[0], obj[key]); 42 | } else { 43 | match = null; 44 | } 45 | } 46 | return template; 47 | } -------------------------------------------------------------------------------- /Chapter11/source/handlers.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Hands-On-High-Performance-Web-Development-with-JavaScript/b2fdd0a44074042406721b329d107bb0a428a88f/Chapter11/source/handlers.js -------------------------------------------------------------------------------- /Chapter11/source/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
    IdNameDescriptionPoints
    21 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /Chapter11/source/interactions.js: -------------------------------------------------------------------------------- 1 | const add = document.querySelector('#addRow'); 2 | const remove = document.querySelector('#remove'); 3 | const random = document.querySelector('#random'); 4 | const tableBody = document.querySelector('#tablebody'); 5 | 6 | add.addEventListener('click', (ev) => { 7 | fetch('/add').then((res) => res.text()).then((fin) => { 8 | const row = document.createElement('tr'); 9 | row.innerHTML = fin; 10 | tableBody.appendChild(row); 11 | }); 12 | }); 13 | remove.addEventListener('click', (ev) => { 14 | while(tableBody.firstChild) { 15 | tableBody.removeChild(tableBody.firstChild); 16 | } 17 | }); 18 | random.addEventListener('click', (ev) => { 19 | fetch('/row.template').then((res) => res.text()).then((fin) => { 20 | console.log(renderTemplate(fin, {id : 1, name : 'justin', description: 'thing', points: 10})); 21 | }) 22 | }) 23 | -------------------------------------------------------------------------------- /Chapter11/source/main.css: -------------------------------------------------------------------------------- 1 | *, :root { 2 | margin : 0; 3 | padding : 0; 4 | font-size : 12px; 5 | } 6 | 7 | table { 8 | margin: 15px; 9 | border : 1px solid black; 10 | } 11 | 12 | th { 13 | border : 1px solid black; 14 | padding : 2px; 15 | } 16 | 17 | button { 18 | border : 1px solid black; 19 | padding :5px; 20 | background : #2e2e2e; 21 | color : #cfcfcf; 22 | cursor : pointer; 23 | margin-left : 15px; 24 | margin-top : 15px; 25 | } -------------------------------------------------------------------------------- /Chapter11/source/row.template: -------------------------------------------------------------------------------- 1 | ${id} 2 | ${name} 3 | ${description} 4 | ${points} -------------------------------------------------------------------------------- /Chapter12/.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version : 2.1 2 | orbs: 3 | snyk: snyk/snyk@0.0.8 4 | heroku: circleci/heroku@0.0.10 5 | jobs: 6 | build: 7 | docker: 8 | - image: circleci/node:12.13 9 | working_directory: ~/repo 10 | steps: 11 | - add_ssh_keys: 12 | fingerprints: 13 | - "32:21:86:9e:c8:b6:4f:41:cb:c3:db:3b:73:92:15:c2" 14 | - checkout 15 | - restore_cache: 16 | keys: 17 | - v1-dependencies-{{ checksum "package.json" }} 18 | - v1-dependencies- 19 | 20 | - run: npm install 21 | 22 | - snyk/scan 23 | 24 | - run: npm run build 25 | 26 | - save_cache: 27 | paths: 28 | - node_modules 29 | key: v1-dependencies-{{ checksum "package.json" }} 30 | - deploy: 31 | command: | 32 | git push 33 | deploy: 34 | executor: heroku/default 35 | steps: 36 | - checkout 37 | - heroku/install 38 | - heroku/deploy-via-git: 39 | only-branch: master 40 | 41 | workflows: 42 | version: 2 43 | build_and_deploy: 44 | jobs: 45 | - build: 46 | context: build 47 | - deploy: 48 | context: deploy 49 | requires: 50 | - build 51 | -------------------------------------------------------------------------------- /Chapter12/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # next.js build output 61 | .next 62 | 63 | # dist files 64 | dist/ 65 | template/css/ 66 | 67 | package-lock.json -------------------------------------------------------------------------------- /Chapter12/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Justin Scherer 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Chapter12/build/rollup.config.js: -------------------------------------------------------------------------------- 1 | import { terser } from 'rollup-plugin-terser'; 2 | import sass from 'rollup-plugin-sass'; 3 | 4 | module.exports = { 5 | input: "./main.js", 6 | output: { 7 | file: "./dist/build.js", 8 | format: "esm", 9 | plugins: [ 10 | terser(), 11 | sass() 12 | ] 13 | } 14 | } -------------------------------------------------------------------------------- /Chapter12/build/rollup.sass.config.js: -------------------------------------------------------------------------------- 1 | import sass from 'rollup-plugin-sass'; 2 | 3 | module.exports = { 4 | input: "./main-sass.js", 5 | output: { 6 | file: "./template/css/main.css", 7 | format: "cjs" 8 | }, 9 | plugins: [ 10 | sass() 11 | ] 12 | } -------------------------------------------------------------------------------- /Chapter12/cache.js: -------------------------------------------------------------------------------- 1 | export default class LRUCache { 2 | constructor(num=10) { 3 | this.numEntries = num; 4 | this.cach = new Map(); 5 | } 6 | 7 | add(file, url) { 8 | const val = { 9 | page : file, 10 | time : Date.now() 11 | } 12 | console.log('we added a file!') 13 | if( this.cache.size === this.numEntries ) { 14 | console.log('we replaced a file!'); 15 | let top = Number.MAX_VALUE; 16 | let earliest = null; 17 | for(const [key, val] of this.cache) { 18 | if( val.time < top ) { 19 | top = val.time; 20 | earliest = key; 21 | } 22 | } 23 | this.cache.delete(earliest); 24 | } 25 | this.cache.set(url, val); 26 | } 27 | 28 | get(url) { 29 | const val = this.cache.get(url); 30 | if( val ) { 31 | console.log('we grabbed a file!'); 32 | val.time = Date.now(); 33 | this.cache.set(url, val); 34 | return val.page; 35 | } 36 | return null; 37 | } 38 | } -------------------------------------------------------------------------------- /Chapter12/main-sass.js: -------------------------------------------------------------------------------- 1 | import main_sass from './template/stylesheets/main.scss' 2 | 3 | export default main_sass; -------------------------------------------------------------------------------- /Chapter12/main.js: -------------------------------------------------------------------------------- 1 | import http2 from 'http2' 2 | import fs from 'fs' 3 | import cluster from 'cluster' 4 | import path from 'path' 5 | import cache from './cache.js' 6 | import os from 'os'; 7 | import {PassThrough} from 'stream' 8 | import {LoopingStream} from './template.js' 9 | 10 | const ENV_VARS = process.env; 11 | 12 | const port = ENV_VARS.npm_package_config_port || 80; 13 | const key = ENV_VARS.npm_package_config_key || 'key.pem'; 14 | const cert = ENV_VARS.npm_package_config_certificate || 'cert.pem'; 15 | const templateDirectory = ENV_VARS.npm_package_config_template || 'template'; 16 | const publishedDirectory = ENV_VARS.npm_package_config_bodyfiles || 'body'; 17 | const developmentMode = ENV_VARS.npm_package_config_development || true; 18 | 19 | const FILE_TYPES = new Map([ 20 | ['.css', path.join('.', templateDirectory, 'css')], 21 | ['.html', path.join('.', templateDirectory, 'html')] 22 | ]); 23 | if( cluster.isMaster ) { 24 | const numCpus = os.cpus().length; 25 | for(let i = 0; i < numCpus; i++) { 26 | cluster.fork(); 27 | } 28 | cluster.on('exit', (worker, code, signal) => { 29 | console.log(`worker ${worker.process.pid} died`); 30 | }); 31 | } else { 32 | const serverCache = new cache(); 33 | const server = http2.createSecureServer({ 34 | key: fs.readFileSync(key), 35 | cert: fs.readFileSync(cert) 36 | }); 37 | server.on('error', (err) => { 38 | console.error(err); 39 | process.exit(); 40 | }); 41 | server.on('stream', (stream, headers) => { 42 | const p = headers[':path']; 43 | for(const [fileType, loc] of FILE_TYPES) { 44 | if( p.endsWith(fileType) ) { 45 | stream.respondWithFile( 46 | path.join(loc, path.posix.basename(p)), 47 | { 48 | 'content-type': `text/${fileType.slice(1)}`, 49 | ':status': 200 50 | } 51 | ); 52 | return; 53 | } 54 | } 55 | try { 56 | const f = fs.statSync(path.join('.', publishedDirectory, `${p}.md`)); 57 | stream.respond({ 58 | 'content-type': 'text/html', 59 | ':status': 200 60 | }); 61 | const cacheHit = serverCache.get(p); 62 | if( cacheHit ) { 63 | stream.end(cacheHit); 64 | } else { 65 | const file = fs.createReadStream('./template/main.html'); 66 | const tStream = new LoopingStream({ 67 | dir: templateDirectory, 68 | publish: publishedDirectory, 69 | vars : { 70 | articles : [ 71 | { 72 | location : 'temp1', 73 | name : 'article 1' 74 | }, 75 | { 76 | location : 'temp2', 77 | name : 'article 2' 78 | }, 79 | { 80 | location : 'temp3', 81 | name : 'article 3' 82 | }, 83 | { 84 | location : 'temp4', 85 | name : 'article 4' 86 | }, 87 | { 88 | location : 'temp5', 89 | name : 'article 5' 90 | } 91 | ], 92 | fileToProcess : `${p}.md` 93 | }, 94 | loopAmount : 2 95 | }); 96 | file.pipe(tStream); 97 | tStream.once('data', (data) => { 98 | serverCache.add(data, p); 99 | stream.end(data); 100 | }); 101 | } 102 | } catch(e) { 103 | stream.respond({ 104 | 'content-type': 'text/html', 105 | ':status' : 404 106 | }); 107 | stream.end('File Not Found! Turn Back!'); 108 | console.warn('following file requested and not found! ', p); 109 | } 110 | }); 111 | console.log('port', port); 112 | server.listen(port); 113 | } -------------------------------------------------------------------------------- /Chapter12/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.0.1", 3 | "name": "microserver", 4 | "type": "module", 5 | "dependencies": { 6 | "remarkable": "^2.0.0" 7 | }, 8 | "config": { 9 | "port": 50000, 10 | "key": "../selfsignedkey.pem", 11 | "certificate": "../selfsignedcertificate.pem", 12 | "template": "../template", 13 | "bodyfiles": "../publish", 14 | "development": true 15 | }, 16 | "scripts": { 17 | "start": "node --experimental-modules dist/build.js", 18 | "build": "rollup --config ./build/rollup.config.js && rollup --config ./build/rollup.sass.config.js", 19 | "watch": "rollup --config ./build/rollup.config.js --watch", 20 | "test": "jest ./test" 21 | }, 22 | "devDependencies": { 23 | "esm": "^3.2.25", 24 | "jest": "^24.9.0", 25 | "rollup-plugin-sass": "^1.2.2", 26 | "rollup-plugin-terser": "^5.1.2", 27 | "rollup-plugin-uglify": "^6.0.3", 28 | "rollup": "^1.27.5" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Chapter12/publish/first.md: -------------------------------------------------------------------------------- 1 | # This is our first article -------------------------------------------------------------------------------- /Chapter12/publish/further/three.md: -------------------------------------------------------------------------------- 1 | # This is inside our further directory! -------------------------------------------------------------------------------- /Chapter12/publish/second.md: -------------------------------------------------------------------------------- 1 | # Another file! -------------------------------------------------------------------------------- /Chapter12/template.js: -------------------------------------------------------------------------------- 1 | import { Transform, PassThrough } from 'stream' 2 | import { once } from 'events' 3 | import Remarkable from 'remarkable' 4 | import fs from 'fs' 5 | import path from 'path' 6 | 7 | class Pair { 8 | constructor() { 9 | this.start = -1; 10 | this.end = -1; 11 | } 12 | } 13 | 14 | const MarkdownRenderer = new Remarkable.Remarkable(); 15 | 16 | const processPattern = function(pattern, templateDir, publishDir, vars=null) { 17 | const process = pattern.toString('utf8').trim(); 18 | const LOOP = "loop"; 19 | const FIND = "from"; 20 | const FILE = "file"; 21 | const breakdown = process.split(' '); 22 | switch(breakdown[0]) { 23 | case LOOP: { 24 | const num = parseInt(breakdown[1]); 25 | const bufs = new Array(num); 26 | const varName = breakdown[2].trim(); 27 | for(let i = 0; i < num; i++) { 28 | let temp = breakdown.slice(3).join(' '); 29 | const replace = /\${([0-9a-zA-Z]+)}/ 30 | let results = replace.exec(temp); 31 | while( results ) { 32 | if( vars[varName][i][results[1]] ) { 33 | temp = temp.replace(results[0], vars[varName][i][results[1]]); 34 | } 35 | results = replace.exec(temp); 36 | } 37 | bufs[i] = Buffer.from(temp); 38 | } 39 | return Buffer.concat(bufs); 40 | } 41 | case FIND: { 42 | const type = breakdown[1]; 43 | const HTML = 'html'; 44 | const CSS = 'css'; 45 | if(!(type === HTML || type === CSS)) return new Error("This is not a valid template type! " + breakdown[1]); 46 | return fs.readFileSync(path.join(templateDir, type, `${breakdown[2]}.${type}`)); 47 | } 48 | case FILE: { 49 | const file = breakdown[1]; 50 | const html = MarkdownRenderer.render(fs.readFileSync(path.join(publishDir, vars.fileToProcess || file[1])).toString('utf8')); 51 | return Buffer.from(html); 52 | } 53 | default: 54 | return new Error("Process directory not found! " + breakdown[0]); 55 | } 56 | } 57 | 58 | export default class TemplateBuilder extends Transform { 59 | constructor(opts={}) { 60 | super(opts); 61 | if( opts.templateDirectory ) { 62 | this.template = opts.templateDirectory; 63 | } 64 | if( opts.templateVariables ) { 65 | this.vars = opts.templateVariables; 66 | } 67 | if( opts.publishDirectory ) { 68 | this.publish = opts.publishDirectory; 69 | } 70 | this.pair = new Pair(); 71 | this.beforePattern = Buffer.from("<%"); 72 | this.afterPattern = Buffer.from("%>"); 73 | this.pattern = []; 74 | } 75 | _transform(chunk, encoding, cb) { 76 | let location = 0; 77 | do { 78 | if(!this.pattern.length && this.pair.start === -1 ) { 79 | const tLoc = location; 80 | location = chunk.indexOf(this.beforePattern, location); 81 | if( location !== -1 ) { 82 | this.push(chunk.slice(tLoc, location)); 83 | 84 | location += 2; 85 | this.pair.start = location; 86 | } else { 87 | this.push(chunk.slice(tLoc)); 88 | } 89 | } else { 90 | if( this.pair.start !== -1 ) { 91 | location = chunk.indexOf(this.afterPattern, location); 92 | if( location !== -1 ) { 93 | this.pair.end = location; 94 | location += 2; 95 | this.push(processPattern(chunk.slice(this.pair.start, this.pair.end), this.template, this.publish, this.vars)); 96 | this.pair = new Pair(); 97 | } else { 98 | this.pattern.push(chunk.slice(this.pair.start)); 99 | } 100 | } else { 101 | let tLoc = location; 102 | location = chunk.indexOf(this.afterPattern, location); 103 | if( location !== -1 ) { 104 | this.pattern.push(chunk.slice(0, location)); 105 | location += 2; 106 | this.push(processPattern(Buffer.concat(this.pattern), this.template, this.publish, this.vars)); 107 | this.pattern = []; 108 | } else { 109 | if(!this.pattern.length ) { 110 | 111 | this.push(chunk.slice(tLoc)); 112 | } else { 113 | this.pattern.push(chunk); 114 | } 115 | } 116 | } 117 | } 118 | } 119 | while( location !== -1 ); 120 | if( this.pattern.length === 1 && 121 | this.pattern[0].indexOf(this.beforePattern) === -1) { 122 | this.push(this.pattern.pop()); 123 | } 124 | cb(); 125 | } 126 | _flush(cb) { 127 | cb(); 128 | } 129 | } 130 | 131 | export class LoopingStream extends Transform { 132 | constructor(opts={}) { 133 | super(opts); 134 | if( 'loopAmount' in opts ) { 135 | this.numberOfRolls = opts.loopAmount || 1; 136 | } 137 | if( opts.vars ) { 138 | this.vars = opts.vars; 139 | } 140 | if( opts.dir) { 141 | this.dir = opts.dir; 142 | } 143 | if( opts.publish ) { 144 | this.publish = opts.publish; 145 | } 146 | this.data = []; 147 | } 148 | _transform(chunk, encoding, cb) { 149 | this.data.push(chunk); 150 | cb(); 151 | } 152 | async _flush(cb) { 153 | let tData = Buffer.concat(this.data); 154 | let tempBuf = []; 155 | for(let i = 0; i < this.numberOfRolls; i++) { 156 | const passThrough = new PassThrough(); 157 | const templateBuilder = new TemplateBuilder({ 158 | templateDirectory : this.dir, 159 | templateVariables : this.vars, 160 | publishDirectory : this.publish 161 | }); 162 | passThrough.pipe(templateBuilder); 163 | templateBuilder.on('data', (data) => { 164 | tempBuf.push(data); 165 | }); 166 | passThrough.end(tData); 167 | await once(templateBuilder, 'end'); 168 | tData = Buffer.concat(tempBuf); 169 | tempBuf = []; 170 | } 171 | this.push(tData); 172 | cb(); 173 | } 174 | } -------------------------------------------------------------------------------- /Chapter12/template/html/footer.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Chapter12/template/html/header.html: -------------------------------------------------------------------------------- 1 |
    2 |

    Our Website

    3 | 8 |
    -------------------------------------------------------------------------------- /Chapter12/template/html/sidebar.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Chapter12/template/main.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | <% from html header %> 7 | <% from html sidebar %> 8 |
    9 | <% file base.md %> 10 |
    11 | <% from html footer %> 12 | 13 | -------------------------------------------------------------------------------- /Chapter12/template/stylesheets/main.scss: -------------------------------------------------------------------------------- 1 | $main-color: "#003A21"; 2 | $text-color: "#efefef"; 3 | 4 | *, html { 5 | margin : 0; 6 | padding : 0; 7 | } 8 | 9 | /* header styles */ 10 | header { 11 | display : block; 12 | height : 2rem; 13 | margin : 10px 15px; 14 | overflow : hidden; 15 | position : fixed; 16 | top : 0; 17 | width : 100%; 18 | background : $main-color; 19 | color : $text-color; 20 | h1 { 21 | float : left; 22 | } 23 | nav { 24 | float : right; 25 | } 26 | } 27 | 28 | /* Footer styles */ 29 | footer { 30 | position : fixed; 31 | left : 0; 32 | bottom : 0; 33 | width : 100%; 34 | background : $main-color; 35 | color : $text-color; 36 | h2 { 37 | float : left; 38 | } 39 | a { 40 | float : right; 41 | } 42 | } 43 | 44 | /* navbar */ 45 | nav { 46 | width : 25%; 47 | display : block; 48 | } 49 | 50 | #content { 51 | width : 75%; 52 | display : block; 53 | } 54 | 55 | html { 56 | color : blue; 57 | } -------------------------------------------------------------------------------- /Chapter12/test/cache.tester.js: -------------------------------------------------------------------------------- 1 | import Cache from '../cache.js'; 2 | import { promises as fsPromises } from 'fs'; 3 | 4 | test('Adds a file to the cache', async () => { 5 | const cache = new Cache(); 6 | const file = await fsPromises.readFile('./test/test_cache.txt'); 7 | cache.add(file, '/local'); 8 | const data = cache.get("/local"); 9 | expect(data).toEqual(file); 10 | }) 11 | -------------------------------------------------------------------------------- /Chapter12/test/test_cache.txt: -------------------------------------------------------------------------------- 1 | Hello -------------------------------------------------------------------------------- /Chapter12/test/tests.test.js: -------------------------------------------------------------------------------- 1 | const esmImport = require('esm')(module); 2 | const cacheTest = esmImport('./cache.tester.js'); -------------------------------------------------------------------------------- /Chapter13/c_index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Chapter13/extern.c: -------------------------------------------------------------------------------- 1 | #include 2 | extern int add(int, int); 3 | 4 | int main() { 5 | printf("%d\n", add(100, 200)); 6 | return 1; 7 | } -------------------------------------------------------------------------------- /Chapter13/external.js: -------------------------------------------------------------------------------- 1 | mergeInto(LibraryManager.library, { 2 | add: function(x, y) { 3 | return x + y; 4 | } 5 | }) -------------------------------------------------------------------------------- /Chapter13/first.wat: -------------------------------------------------------------------------------- 1 | (module 2 | (import "console" "log" (func $log (param i32 i32))) 3 | (import "js" "mem" (memory 1)) 4 | (global $g (mut i32) (i32.const 0)) 5 | (func $checkFizz (param $p1 i32) 6 | local.get $p1 7 | i32.const 3 8 | i32.rem_s 9 | (if (i32.eq (i32.const 0)) 10 | (then 11 | (i32.store8 (global.get $g) (i32.const 70)) 12 | (i32.store8 (i32.add (global.get $g) (i32.const 1)) (i32.const 105)) 13 | (i32.store8 (i32.add (global.get $g) (i32.const 2)) (i32.const 122)) 14 | (i32.store8 (i32.add (global.get $g) (i32.const 3)) (i32.const 122)) 15 | (global.set $g (i32.add (global.get $g) (i32.const 4))) 16 | ) 17 | ) 18 | ) 19 | (func $checkBuzz (param $p1 i32) 20 | local.get $p1 21 | i32.const 5 22 | i32.rem_s 23 | (if (i32.eq (i32.const 0)) 24 | (then 25 | (i32.store8 (global.get $g) (i32.const 66)) 26 | (i32.store8 (i32.add (global.get $g) (i32.const 1)) (i32.const 117)) 27 | (i32.store8 (i32.add (global.get $g) (i32.const 2)) (i32.const 122)) 28 | (i32.store8 (i32.add (global.get $g) (i32.const 3)) (i32.const 122)) 29 | (global.set $g (i32.add (global.get $g) (i32.const 4))) 30 | ) 31 | ) 32 | ) 33 | (func $fizzbuzz (param $p i32) 34 | (local $start i32) 35 | (local.set $start (i32.const 1)) 36 | (block 37 | (loop 38 | (call $checkFizz (local.get $start)) 39 | (call $checkBuzz (local.get $start)) 40 | (br_if 1 (i32.eq (local.get $start) (local.get $p))) 41 | (local.set $start (i32.add (local.get $start) (i32.const 1))) 42 | (br 0) 43 | ) 44 | ) 45 | i32.const 0 46 | global.get $g 47 | call $log 48 | ) 49 | (export "fizzbuzz" (func $fizzbuzz)) 50 | ) -------------------------------------------------------------------------------- /Chapter13/fizzbuzz.c: -------------------------------------------------------------------------------- 1 | #include 2 | void fizzbuzz(int num) { 3 | for(int i = 1; i <= num; i++) { 4 | if(i%3 == 0) { 5 | printf("Fizz"); 6 | } 7 | if(i%5 == 0) { 8 | printf("Buzz"); 9 | } 10 | } 11 | printf("\n"); 12 | } -------------------------------------------------------------------------------- /Chapter13/hamming.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | const int INT_SIZE = sizeof(int) * 8; 4 | 5 | void placeBits(int data, int* parity) { 6 | int currentDataLoc = 1; 7 | int dataIterator = 0; 8 | for(int i = 1, j = 0; i < INT_SIZE; i++, j++) { 9 | if(ceil(log2(i)) == floor(log2(i))) continue; //we are at a parity bit section 10 | *parity |= ((data & (currentDataLoc << dataIterator)) << (j - dataIterator)); 11 | dataIterator++; 12 | } 13 | } 14 | 15 | void createParity(int* data) { 16 | // now, based off of our current number is what tells us how we check for each parity bit location 17 | int parityChecks[4] = {1, 2, 4, 8}; 18 | int bitSet[4] = {1, 2, 8, 128}; 19 | for(int i = 0; i < 4; i++) { 20 | int count = 0; 21 | for(int j = 0; j < INT_SIZE; j++) { 22 | // we are at a location to test if there is a 0 23 | if((parityChecks[i] & (j+1)) != 0) { 24 | count += ((*data & (1 << j)) != 0) ? 1 : 0; 25 | } 26 | } 27 | if( count % 2 != 0 ) { 28 | *data |= bitSet[i]; 29 | } 30 | } 31 | } 32 | 33 | // creation of true data point with parity bits attached 34 | int createData(int data) { 35 | int num = 0; 36 | // parity is at locations 1,2,4,8 so we need to grab values from data and store them at 3,5,6,7,9,10,11,12,13,14,15 37 | // first we place the bits 38 | placeBits(data, &num); 39 | createParity(&num); 40 | return num; 41 | } 42 | 43 | // return based off of passed in value, 0 is good (even parity), 1 is bad (we found odd parity) 44 | int checkRow(int* data, int loc) { 45 | int count = 0; 46 | int verifier = 1; 47 | for(int i = 1; i < INT_SIZE; i++) { 48 | // we have a parity location 49 | if((loc & i) != 0 ){ 50 | count += (*data & (verifier << (i - 1))) != 0 ? 1 : 0; 51 | } 52 | } 53 | return count % 2; 54 | } 55 | 56 | int checkAndVerifyData(int data) { 57 | int verify = 0; 58 | int parityChecks[4] = {1, 2, 8, 128}; 59 | for(int i = 0; i <= 4; i++) { 60 | verify = checkRow(&data, parityChecks[i]); 61 | if(verify != 0) { // we do not have even parity 62 | return -1; 63 | } 64 | } 65 | return 1; 66 | } -------------------------------------------------------------------------------- /Chapter13/hello_world.c: -------------------------------------------------------------------------------- 1 | #include 2 | int main() { 3 | printf("Hello, World!\n"); 4 | return 0; 5 | } -------------------------------------------------------------------------------- /Chapter13/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 27 | -------------------------------------------------------------------------------- /Chapter13/math.wat: -------------------------------------------------------------------------------- 1 | (module 2 | (import "math" "add" (func $externalAdd (param i32 i32) (result i32))) 3 | (func $add (param $p1 i32) (param $p2 i32) (result i32) 4 | local.get $p1 5 | local.get $p2 6 | i32.add 7 | ) 8 | (func $add2 (param $p1 i32) (param $p2 i32) (result i32) 9 | local.get $p1 10 | local.get $p2 11 | call $externalAdd 12 | ) 13 | (export "add" (func $add)) 14 | (export "add2" (func $add2)) 15 | ) -------------------------------------------------------------------------------- /Chapter13/sharing_resources.wat: -------------------------------------------------------------------------------- 1 | (module 2 | (import "js" "mem" (memory 1)) 3 | (func $storeNumber 4 | (i32.store (i32.const 0) (i32.const 100)) 5 | ) 6 | (func $readNumber (result i32) 7 | (i32.load (i32.const 0)) 8 | ) 9 | (export "readNumber" (func $readNumber)) 10 | (export "storeNumber" (func $storeNumber)) 11 | ) -------------------------------------------------------------------------------- /Chapter13/sqltest/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 55 | 56 | -------------------------------------------------------------------------------- /Chapter13/useless.wat: -------------------------------------------------------------------------------- 1 | (module) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Packt 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ## $5 Tech Unlocked 2021! 5 | [Buy and download this Book for only $5 on PacktPub.com](https://www.packtpub.com/product/hands-on-javascript-high-performance/9781838821098) 6 | ----- 7 | *If you have read this book, please leave a review on [Amazon.com](https://www.amazon.com/gp/product/1838821090). Potential readers can then use your unbiased opinion to help them make purchase decisions. Thank you. The $5 campaign runs from __December 15th 2020__ to __January 13th 2021.__* 8 | 9 | # Hands-On-High-Performance-Web-Development-with-JavaScript 10 | Hands-On High Performance Web Development with JavaScript, Published by Packt 11 | ### Download a free PDF 12 | 13 | If you have already purchased a print or Kindle version of this book, you can get a DRM-free PDF version at no cost.
    Simply click on the link to claim your free PDF.
    14 |

    https://packt.link/free-ebook/9781838821098

    -------------------------------------------------------------------------------- /newfile1.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Hands-On-High-Performance-Web-Development-with-JavaScript/b2fdd0a44074042406721b329d107bb0a428a88f/newfile1.txt --------------------------------------------------------------------------------