diff --git a/.github/workflows/node.yml b/.github/workflows/node.yml index 57f5c78..9becb99 100644 --- a/.github/workflows/node.yml +++ b/.github/workflows/node.yml @@ -2,28 +2,27 @@ name: Node.js CI on: push: - branches: [ "main" ] + branches: ["main"] pull_request: - branches: [ "main" ] + branches: ["main"] jobs: build: - runs-on: ubuntu-latest strategy: matrix: - node-version: [22.x] + node-version: [24.x] steps: - - uses: actions/checkout@v4 - - uses: pnpm/action-setup@v2 - with: - version: 9.5.0 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v4 - with: - node-version: ${{ matrix.node-version }} - cache: 'pnpm' - - run: pnpm install --frozen-lockfile - - run: pnpm test + - uses: actions/checkout@v4 + - uses: pnpm/action-setup@v2 + with: + version: 10.20.0 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + cache: "pnpm" + - run: pnpm install --frozen-lockfile + - run: pnpm test diff --git a/02-the-module-system/01-revealing-module-pattern/index.js b/02-the-module-system/01-revealing-module-pattern/index.js index 954dea7..30b21aa 100644 --- a/02-the-module-system/01-revealing-module-pattern/index.js +++ b/02-the-module-system/01-revealing-module-pattern/index.js @@ -1,16 +1,16 @@ const myModule = (() => { - const privateFoo = () => {} - const privateBar = [] + const privateFoo = () => {}; + const privateBar = []; - console.log('Inside:', privateFoo, privateBar) + console.log("Inside:", privateFoo, privateBar); const exported = { publicFoo: () => {}, publicBar: () => {}, - } + }; - return exported -})() // once the parenthesis here are parsed, the function will be invoked and the returned value assigned to myModule + return exported; +})(); // once the parenthesis here are parsed, the function will be invoked and the returned value assigned to myModule -console.log('Outside:', myModule.privateFoo, myModule.privateBar) -console.log('Module:', myModule) +console.log("Outside:", myModule.privateFoo, myModule.privateBar); +console.log("Module:", myModule); diff --git a/02-the-module-system/02-esm-syntax/logger.js b/02-the-module-system/02-esm-syntax/logger.js index 17a0910..0d7c3a3 100644 --- a/02-the-module-system/02-esm-syntax/logger.js +++ b/02-the-module-system/02-esm-syntax/logger.js @@ -1,10 +1,10 @@ // exports a function export function log(message) { - console.log(message) + console.log(message); } // exports a constant -export const DEFAULT_LEVEL = 'info' +export const DEFAULT_LEVEL = "info"; // exports an object export const LEVELS = { @@ -14,15 +14,15 @@ export const LEVELS = { data: 3, info: 4, verbose: 5, -} +}; // exports a class export class Logger { constructor(name) { - this.name = name + this.name = name; } log(message) { - console.log(`[${this.name}] ${message}`) + console.log(`[${this.name}] ${message}`); } } diff --git a/02-the-module-system/02-esm-syntax/main1.js b/02-the-module-system/02-esm-syntax/main1.js index e3ce6c9..c805060 100644 --- a/02-the-module-system/02-esm-syntax/main1.js +++ b/02-the-module-system/02-esm-syntax/main1.js @@ -1,6 +1,5 @@ // using a namespace import -// biome-ignore lint/style/noNamespaceImport: demonstrating namespace import syntax -import * as loggerModule from './logger.js' +import * as loggerModule from "./logger.js"; -console.log(loggerModule) +console.log(loggerModule); diff --git a/02-the-module-system/02-esm-syntax/main2.js b/02-the-module-system/02-esm-syntax/main2.js index 5fcde85..9a47c2c 100644 --- a/02-the-module-system/02-esm-syntax/main2.js +++ b/02-the-module-system/02-esm-syntax/main2.js @@ -1,4 +1,4 @@ // import a single member of the module -import { log } from './logger.js' +import { log } from "./logger.js"; -log('Hello World') +log("Hello World"); diff --git a/02-the-module-system/02-esm-syntax/main3.js b/02-the-module-system/02-esm-syntax/main3.js index b1f96f2..84b6829 100644 --- a/02-the-module-system/02-esm-syntax/main3.js +++ b/02-the-module-system/02-esm-syntax/main3.js @@ -1,6 +1,6 @@ // import multiple members of the module -import { Logger, log } from './logger.js' +import { Logger, log } from "./logger.js"; -log('Hello World') -const logger = new Logger('DEFAULT') -logger.log('Hello world') +log("Hello World"); +const logger = new Logger("DEFAULT"); +logger.log("Hello world"); diff --git a/02-the-module-system/02-esm-syntax/main4.js b/02-the-module-system/02-esm-syntax/main4.js index af94dab..6157b6a 100644 --- a/02-the-module-system/02-esm-syntax/main4.js +++ b/02-the-module-system/02-esm-syntax/main4.js @@ -1,5 +1,5 @@ // name clash -import { log } from './logger.js' +import { log } from "./logger.js"; // const log = console.log // <- this would generate a "SyntaxError: Identifier 'log' has already been declared" error -log('Hello world') +log("Hello world"); diff --git a/02-the-module-system/02-esm-syntax/main5.js b/02-the-module-system/02-esm-syntax/main5.js index 75e6fba..a1adf84 100644 --- a/02-the-module-system/02-esm-syntax/main5.js +++ b/02-the-module-system/02-esm-syntax/main5.js @@ -1,7 +1,7 @@ // avoid name clash -import { log as log2 } from './logger.js' +import { log as log2 } from "./logger.js"; -const log = console.log +const log = console.log; -log('message from log') -log2('message from log2') +log("message from log"); +log2("message from log2"); diff --git a/02-the-module-system/03-esm-default/logger.js b/02-the-module-system/03-esm-default/logger.js index 448a2a0..6b93341 100644 --- a/02-the-module-system/03-esm-default/logger.js +++ b/02-the-module-system/03-esm-default/logger.js @@ -1,10 +1,10 @@ // biome-ignore lint/style/noDefaultExport: demonstrating default export syntax export default class Logger { constructor(name) { - this.name = name + this.name = name; } log(message) { - console.log(`[${this.name}] ${message}`) + console.log(`[${this.name}] ${message}`); } } diff --git a/02-the-module-system/03-esm-default/main.js b/02-the-module-system/03-esm-default/main.js index 0ba15d9..b75b5ec 100644 --- a/02-the-module-system/03-esm-default/main.js +++ b/02-the-module-system/03-esm-default/main.js @@ -1,4 +1,4 @@ -import MyLogger from './logger.js' +import MyLogger from "./logger.js"; -const logger = new MyLogger('info') -logger.log('Hello World') +const logger = new MyLogger("info"); +logger.log("Hello World"); diff --git a/02-the-module-system/03-esm-default/showDefault.js b/02-the-module-system/03-esm-default/showDefault.js index 4de99c8..5ace00d 100644 --- a/02-the-module-system/03-esm-default/showDefault.js +++ b/02-the-module-system/03-esm-default/showDefault.js @@ -1,4 +1,3 @@ -// biome-ignore lint/style/noNamespaceImport: needs namespace import to show the default export -import * as loggerModule from './logger.js' +import * as loggerModule from "./logger.js"; -console.log(loggerModule.default) +console.log(loggerModule.default); diff --git a/02-the-module-system/04-esm-mixed-exports/logger.js b/02-the-module-system/04-esm-mixed-exports/logger.js index 90689e4..03e493e 100644 --- a/02-the-module-system/04-esm-mixed-exports/logger.js +++ b/02-the-module-system/04-esm-mixed-exports/logger.js @@ -1,8 +1,8 @@ // biome-ignore lint/style/noDefaultExport: showcases default exports export default function log(message) { - console.log(message) + console.log(message); } export function info(message) { - log(`info: ${message}`) + log(`info: ${message}`); } diff --git a/02-the-module-system/04-esm-mixed-exports/main.js b/02-the-module-system/04-esm-mixed-exports/main.js index d39ca82..f455976 100644 --- a/02-the-module-system/04-esm-mixed-exports/main.js +++ b/02-the-module-system/04-esm-mixed-exports/main.js @@ -1,4 +1,4 @@ -import mylog, { info } from './logger.js' +import mylog, { info } from "./logger.js"; -mylog('Hello') -info('World') +mylog("Hello"); +info("World"); diff --git a/02-the-module-system/05-esm-dynamic-imports/main.js b/02-the-module-system/05-esm-dynamic-imports/main.js index b4fda59..9819bf9 100644 --- a/02-the-module-system/05-esm-dynamic-imports/main.js +++ b/02-the-module-system/05-esm-dynamic-imports/main.js @@ -1,5 +1,5 @@ -const SUPPORTED_LANGUAGES = ['el', 'en', 'es', 'it', 'pl'] // (1) -const selectedLanguage = process.argv[2] // (2) +const SUPPORTED_LANGUAGES = ["el", "en", "es", "it", "pl"]; // (1) +const selectedLanguage = process.argv[2]; // (2) if (!selectedLanguage) { // (3) @@ -7,17 +7,17 @@ if (!selectedLanguage) { `Please specify a language Usage: node ${process.argv[1]} - Supported languages: ${SUPPORTED_LANGUAGES.join(', ')}` - ) - process.exit(1) + Supported languages: ${SUPPORTED_LANGUAGES.join(", ")}`, + ); + process.exit(1); } if (!SUPPORTED_LANGUAGES.includes(selectedLanguage)) { // (4) - console.error('The specified language is not supported') - process.exit(1) + console.error("The specified language is not supported"); + process.exit(1); } -const translationModule = `./strings-${selectedLanguage}.js` // (5) -const strings = await import(translationModule) // (6) -console.log(strings.HELLO) // (7) +const translationModule = `./strings-${selectedLanguage}.js`; // (5) +const strings = await import(translationModule); // (6) +console.log(strings.HELLO); // (7) diff --git a/02-the-module-system/05-esm-dynamic-imports/strings-el.js b/02-the-module-system/05-esm-dynamic-imports/strings-el.js index 995dddf..3e02914 100644 --- a/02-the-module-system/05-esm-dynamic-imports/strings-el.js +++ b/02-the-module-system/05-esm-dynamic-imports/strings-el.js @@ -1 +1 @@ -export const HELLO = 'Γεια σου κόσμε' +export const HELLO = "Γεια σου κόσμε"; diff --git a/02-the-module-system/05-esm-dynamic-imports/strings-en.js b/02-the-module-system/05-esm-dynamic-imports/strings-en.js index 91e760c..23de027 100644 --- a/02-the-module-system/05-esm-dynamic-imports/strings-en.js +++ b/02-the-module-system/05-esm-dynamic-imports/strings-en.js @@ -1 +1 @@ -export const HELLO = 'Hello World' +export const HELLO = "Hello World"; diff --git a/02-the-module-system/05-esm-dynamic-imports/strings-es.js b/02-the-module-system/05-esm-dynamic-imports/strings-es.js index 23c6168..698e75c 100644 --- a/02-the-module-system/05-esm-dynamic-imports/strings-es.js +++ b/02-the-module-system/05-esm-dynamic-imports/strings-es.js @@ -1 +1 @@ -export const HELLO = 'Hola mundo' +export const HELLO = "Hola mundo"; diff --git a/02-the-module-system/05-esm-dynamic-imports/strings-it.js b/02-the-module-system/05-esm-dynamic-imports/strings-it.js index 6713472..b13841a 100644 --- a/02-the-module-system/05-esm-dynamic-imports/strings-it.js +++ b/02-the-module-system/05-esm-dynamic-imports/strings-it.js @@ -1 +1 @@ -export const HELLO = 'Ciao mondo' +export const HELLO = "Ciao mondo"; diff --git a/02-the-module-system/05-esm-dynamic-imports/strings-pl.js b/02-the-module-system/05-esm-dynamic-imports/strings-pl.js index bf255be..6f4c1c2 100644 --- a/02-the-module-system/05-esm-dynamic-imports/strings-pl.js +++ b/02-the-module-system/05-esm-dynamic-imports/strings-pl.js @@ -1 +1 @@ -export const HELLO = 'Witaj świecie' +export const HELLO = "Witaj świecie"; diff --git a/02-the-module-system/06-esm-read-only-live-bindings/counter.js b/02-the-module-system/06-esm-read-only-live-bindings/counter.js index 1b7835f..6f024f0 100644 --- a/02-the-module-system/06-esm-read-only-live-bindings/counter.js +++ b/02-the-module-system/06-esm-read-only-live-bindings/counter.js @@ -1,5 +1,5 @@ -export let count = 0 +export let count = 0; export function increment() { - count++ + count++; } diff --git a/02-the-module-system/06-esm-read-only-live-bindings/main.js b/02-the-module-system/06-esm-read-only-live-bindings/main.js index ec399d1..ad50055 100644 --- a/02-the-module-system/06-esm-read-only-live-bindings/main.js +++ b/02-the-module-system/06-esm-read-only-live-bindings/main.js @@ -1,6 +1,6 @@ -import { count, increment } from './counter.js' +import { count, increment } from "./counter.js"; -console.log(count) // prints 0 -increment() -console.log(count) // prints 1 +console.log(count); // prints 0 +increment(); +console.log(count); // prints 1 // count++ // TypeError: Assignment to constant variable! diff --git a/02-the-module-system/07-esm-module-resolution/main.js b/02-the-module-system/07-esm-module-resolution/main.js index 8d14a0a..41044b4 100644 --- a/02-the-module-system/07-esm-module-resolution/main.js +++ b/02-the-module-system/07-esm-module-resolution/main.js @@ -1,5 +1,5 @@ -console.log(import.meta.resolve('./utils/example.js')) // file:///utils/example.js +console.log(import.meta.resolve("./utils/example.js")); // file:///utils/example.js -console.log(import.meta.resolve('assert')) // node:assert -console.log(import.meta.resolve('node:assert')) // node:assert -console.log(import.meta.resolve('fastify/lib/logger.js')) // file:///node_modules/fastify/lib/logger.js +console.log(import.meta.resolve("assert")); // node:assert +console.log(import.meta.resolve("node:assert")); // node:assert +console.log(import.meta.resolve("fastify/lib/logger.js")); // file:///node_modules/fastify/lib/logger.js diff --git a/02-the-module-system/08-esm-circular-dependencies/a.js b/02-the-module-system/08-esm-circular-dependencies/a.js index f3ab9d4..2069e1f 100644 --- a/02-the-module-system/08-esm-circular-dependencies/a.js +++ b/02-the-module-system/08-esm-circular-dependencies/a.js @@ -1,7 +1,6 @@ -// biome-ignore lint/style/noNamespaceImport: needs namespace import for simplicity -import * as bModule from './b.js' +import * as bModule from "./b.js"; -export let loaded = false -export const b = bModule +export let loaded = false; +export const b = bModule; -loaded = true +loaded = true; diff --git a/02-the-module-system/08-esm-circular-dependencies/b.js b/02-the-module-system/08-esm-circular-dependencies/b.js index ec685b9..7ce5088 100644 --- a/02-the-module-system/08-esm-circular-dependencies/b.js +++ b/02-the-module-system/08-esm-circular-dependencies/b.js @@ -1,7 +1,6 @@ -// biome-ignore lint/style/noNamespaceImport: needs namespace import for simplicity -import * as aModule from './a.js' +import * as aModule from "./a.js"; -export let loaded = false -export const a = aModule +export let loaded = false; +export const a = aModule; -loaded = true +loaded = true; diff --git a/02-the-module-system/08-esm-circular-dependencies/main.js b/02-the-module-system/08-esm-circular-dependencies/main.js index 708bed5..88d7bf6 100644 --- a/02-the-module-system/08-esm-circular-dependencies/main.js +++ b/02-the-module-system/08-esm-circular-dependencies/main.js @@ -1,7 +1,5 @@ -// biome-ignore lint/style/noNamespaceImport: needs namespace import for simplicity -import * as a from './a.js' -// biome-ignore lint/style/noNamespaceImport: needs namespace import for simplicity -import * as b from './b.js' +import * as a from "./a.js"; +import * as b from "./b.js"; -console.log('a ->', a) -console.log('b ->', b) +console.log("a ->", a); +console.log("b ->", b); diff --git a/02-the-module-system/09-esm-monkey-patching/colorizeLogger.js b/02-the-module-system/09-esm-monkey-patching/colorizeLogger.js index e8ed27d..457e502 100644 --- a/02-the-module-system/09-esm-monkey-patching/colorizeLogger.js +++ b/02-the-module-system/09-esm-monkey-patching/colorizeLogger.js @@ -1,17 +1,17 @@ -import { logger } from './logger.js' +import { logger } from "./logger.js"; -const RED = '\x1b[31m' -const YELLOW = '\x1b[33m' -const GREEN = '\x1b[32m' -const WHITE = '\x1b[37m' -const RESET = '\x1b[0m' +const RED = "\x1b[31m"; +const YELLOW = "\x1b[33m"; +const GREEN = "\x1b[32m"; +const WHITE = "\x1b[37m"; +const RESET = "\x1b[0m"; -const originalInfo = logger.info -const originalWarn = logger.warn -const originalError = logger.error -const originalDebug = logger.debug +const originalInfo = logger.info; +const originalWarn = logger.warn; +const originalError = logger.error; +const originalDebug = logger.debug; -logger.info = message => originalInfo(`${GREEN}${message}${RESET}`) -logger.warn = message => originalWarn(`${YELLOW}${message}${RESET}`) -logger.error = message => originalError(`${RED}${message}${RESET}`) -logger.debug = message => originalDebug(`${WHITE}${message}${RESET}`) +logger.info = (message) => originalInfo(`${GREEN}${message}${RESET}`); +logger.warn = (message) => originalWarn(`${YELLOW}${message}${RESET}`); +logger.error = (message) => originalError(`${RED}${message}${RESET}`); +logger.debug = (message) => originalDebug(`${WHITE}${message}${RESET}`); diff --git a/02-the-module-system/09-esm-monkey-patching/logger.js b/02-the-module-system/09-esm-monkey-patching/logger.js index bf1d292..768db21 100644 --- a/02-the-module-system/09-esm-monkey-patching/logger.js +++ b/02-the-module-system/09-esm-monkey-patching/logger.js @@ -1,19 +1,19 @@ export const logger = { info(message) { - console.log(`[INFO]\t${message}`) + console.log(`[INFO]\t${message}`); }, error(message) { - console.log(`[ERROR]\t${message}`) + console.log(`[ERROR]\t${message}`); }, warn(message) { - console.log(`[WARN]\t${message}`) + console.log(`[WARN]\t${message}`); }, debug(message) { - console.log(`[DEBUG]\t${message}`) + console.log(`[DEBUG]\t${message}`); }, -} +}; // biome-ignore lint/style/noDefaultExport: useful for monkey patching export default { logger, -} +}; diff --git a/02-the-module-system/09-esm-monkey-patching/main.js b/02-the-module-system/09-esm-monkey-patching/main.js index ba63847..2e13cc6 100644 --- a/02-the-module-system/09-esm-monkey-patching/main.js +++ b/02-the-module-system/09-esm-monkey-patching/main.js @@ -1,7 +1,7 @@ -import { logger } from './logger.js' -import './colorizeLogger.js' +import { logger } from "./logger.js"; +import "./colorizeLogger.js"; -logger.info('Hello, World!') -logger.warn('Free disk space is running low') -logger.error('Failed to connect to database') -logger.debug('main() is starting') +logger.info("Hello, World!"); +logger.warn("Free disk space is running low"); +logger.error("Failed to connect to database"); +logger.debug("main() is starting"); diff --git a/02-the-module-system/09-esm-monkey-patching/main2.js b/02-the-module-system/09-esm-monkey-patching/main2.js index df31afc..9d62265 100644 --- a/02-the-module-system/09-esm-monkey-patching/main2.js +++ b/02-the-module-system/09-esm-monkey-patching/main2.js @@ -1,7 +1,7 @@ -import loggerModule from './logger.js' -import './replaceLogger3.js' +import loggerModule from "./logger.js"; +import "./replaceLogger3.js"; -loggerModule.logger.info('Hello, World!') -loggerModule.logger.warn('Free disk space is running low') -loggerModule.logger.error('Failed to connect to database') -loggerModule.logger.debug('main() is starting') +loggerModule.logger.info("Hello, World!"); +loggerModule.logger.warn("Free disk space is running low"); +loggerModule.logger.error("Failed to connect to database"); +loggerModule.logger.debug("main() is starting"); diff --git a/02-the-module-system/09-esm-monkey-patching/main2Broken.js b/02-the-module-system/09-esm-monkey-patching/main2Broken.js index 9c8a517..4887692 100644 --- a/02-the-module-system/09-esm-monkey-patching/main2Broken.js +++ b/02-the-module-system/09-esm-monkey-patching/main2Broken.js @@ -1,7 +1,7 @@ -import { logger } from './logger.js' -import './replaceLogger3.js' +import { logger } from "./logger.js"; +import "./replaceLogger3.js"; -logger.info('Hello, World!') -logger.warn('Free disk space is running low') -logger.error('Failed to connect to database') -logger.debug('main() is starting') +logger.info("Hello, World!"); +logger.warn("Free disk space is running low"); +logger.error("Failed to connect to database"); +logger.debug("main() is starting"); diff --git a/02-the-module-system/09-esm-monkey-patching/replaceLogger3.js b/02-the-module-system/09-esm-monkey-patching/replaceLogger3.js index 1a56405..435ce82 100644 --- a/02-the-module-system/09-esm-monkey-patching/replaceLogger3.js +++ b/02-the-module-system/09-esm-monkey-patching/replaceLogger3.js @@ -1,22 +1,22 @@ -import loggerModule from './logger.js' +import loggerModule from "./logger.js"; -const RED = '\x1b[31m' -const YELLOW = '\x1b[33m' -const GREEN = '\x1b[32m' -const WHITE = '\x1b[37m' -const RESET = '\x1b[0m' +const RED = "\x1b[31m"; +const YELLOW = "\x1b[33m"; +const GREEN = "\x1b[32m"; +const WHITE = "\x1b[37m"; +const RESET = "\x1b[0m"; loggerModule.logger = { - info: message => { - console.log(`INFO: ${GREEN}${message}${RESET}`) + info: (message) => { + console.log(`INFO: ${GREEN}${message}${RESET}`); }, - warn: message => { - console.log(`WARN: ${YELLOW}${message}${RESET}`) + warn: (message) => { + console.log(`WARN: ${YELLOW}${message}${RESET}`); }, - debug: message => { - console.log(`DEBUG: ${WHITE}${message}${RESET}`) + debug: (message) => { + console.log(`DEBUG: ${WHITE}${message}${RESET}`); }, - error: message => { - console.log(`ERROR: ${RED}${message}${RESET}`) + error: (message) => { + console.log(`ERROR: ${RED}${message}${RESET}`); }, -} +}; diff --git a/02-the-module-system/10-commonjs-sample-module/main.cjs b/02-the-module-system/10-commonjs-sample-module/main.cjs index 2632656..54315a1 100644 --- a/02-the-module-system/10-commonjs-sample-module/main.cjs +++ b/02-the-module-system/10-commonjs-sample-module/main.cjs @@ -1,5 +1,5 @@ -'use strict' +"use strict"; -const { add } = require('./math.cjs') +const { add } = require("./math.cjs"); -console.log(add(2, 3)) // 5 +console.log(add(2, 3)); // 5 diff --git a/02-the-module-system/10-commonjs-sample-module/math.cjs b/02-the-module-system/10-commonjs-sample-module/math.cjs index b92a9f1..2a4b429 100644 --- a/02-the-module-system/10-commonjs-sample-module/math.cjs +++ b/02-the-module-system/10-commonjs-sample-module/math.cjs @@ -1,7 +1,7 @@ -'use strict' +"use strict"; function add(a, b) { - return a + b + return a + b; } -module.exports = { add } // exports.add = add +module.exports = { add }; // exports.add = add diff --git a/02-the-module-system/11-top-level-await/main.cjs b/02-the-module-system/11-top-level-await/main.cjs index 3139417..946f466 100644 --- a/02-the-module-system/11-top-level-await/main.cjs +++ b/02-the-module-system/11-top-level-await/main.cjs @@ -1,11 +1,11 @@ -'use strict' +"use strict"; -const { loadData } = require('./nobel.cjs') +const { loadData } = require("./nobel.cjs"); // console.log(await loadData()) // SyntaxError async function main() { - console.log(await loadData()) + console.log(await loadData()); } -main() +main(); diff --git a/02-the-module-system/11-top-level-await/main.mjs b/02-the-module-system/11-top-level-await/main.mjs index d90f921..cb9b6a7 100644 --- a/02-the-module-system/11-top-level-await/main.mjs +++ b/02-the-module-system/11-top-level-await/main.mjs @@ -1,3 +1,3 @@ -import { loadData } from './nobel.cjs' +import { loadData } from "./nobel.cjs"; -console.log(await loadData()) +console.log(await loadData()); diff --git a/02-the-module-system/11-top-level-await/nobel.cjs b/02-the-module-system/11-top-level-await/nobel.cjs index d17aa9c..1bedd86 100644 --- a/02-the-module-system/11-top-level-await/nobel.cjs +++ b/02-the-module-system/11-top-level-await/nobel.cjs @@ -1,6 +1,6 @@ -'use strict' +"use strict"; exports.loadData = async function loadData() { - const response = await fetch('https://api.nobelprize.org/v1/prize.json') - return response.json() -} + const response = await fetch("https://api.nobelprize.org/v1/prize.json"); + return response.json(); +}; diff --git a/02-the-module-system/12-behaviour-of-this/main.cjs b/02-the-module-system/12-behaviour-of-this/main.cjs index 0005ae8..ebb6937 100644 --- a/02-the-module-system/12-behaviour-of-this/main.cjs +++ b/02-the-module-system/12-behaviour-of-this/main.cjs @@ -1,4 +1,4 @@ -'use strict' +"use strict"; -console.log(this) -console.log(globalThis) +console.log(this); +console.log(globalThis); diff --git a/02-the-module-system/12-behaviour-of-this/main.mjs b/02-the-module-system/12-behaviour-of-this/main.mjs index 43116fb..a7c6b56 100644 --- a/02-the-module-system/12-behaviour-of-this/main.mjs +++ b/02-the-module-system/12-behaviour-of-this/main.mjs @@ -1,2 +1,2 @@ -console.log(this) // undefined -console.log(globalThis) // reference to the global object +console.log(this); // undefined +console.log(globalThis); // reference to the global object diff --git a/02-the-module-system/13-esm-missing-references/main.js b/02-the-module-system/13-esm-missing-references/main.js index 578b66a..266f2e3 100644 --- a/02-the-module-system/13-esm-missing-references/main.js +++ b/02-the-module-system/13-esm-missing-references/main.js @@ -1,10 +1,10 @@ -import { dirname } from 'node:path' -import { fileURLToPath } from 'node:url' +import { dirname } from "node:path"; +import { fileURLToPath } from "node:url"; -console.log(import.meta.filename) -console.log(import.meta.dirname) +console.log(import.meta.filename); +console.log(import.meta.dirname); -const __filename = fileURLToPath(import.meta.url) -const __dirname = dirname(__filename) -console.log(__filename) -console.log(__dirname) +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); +console.log(__filename); +console.log(__dirname); diff --git a/02-the-module-system/14-import-cjs-from-esm/main.mjs b/02-the-module-system/14-import-cjs-from-esm/main.mjs index c4fd785..c272747 100644 --- a/02-the-module-system/14-import-cjs-from-esm/main.mjs +++ b/02-the-module-system/14-import-cjs-from-esm/main.mjs @@ -1,3 +1,3 @@ -import someModule from './someModule.cjs' +import someModule from "./someModule.cjs"; -console.log(someModule) +console.log(someModule); diff --git a/02-the-module-system/14-import-cjs-from-esm/main2.mjs b/02-the-module-system/14-import-cjs-from-esm/main2.mjs index 36f584f..c8bb543 100644 --- a/02-the-module-system/14-import-cjs-from-esm/main2.mjs +++ b/02-the-module-system/14-import-cjs-from-esm/main2.mjs @@ -1,3 +1,3 @@ -import { someFeature } from './someModule.cjs' +import { someFeature } from "./someModule.cjs"; -console.log(someFeature) +console.log(someFeature); diff --git a/02-the-module-system/14-import-cjs-from-esm/main3.mjs b/02-the-module-system/14-import-cjs-from-esm/main3.mjs index 4c96acb..94835ad 100644 --- a/02-the-module-system/14-import-cjs-from-esm/main3.mjs +++ b/02-the-module-system/14-import-cjs-from-esm/main3.mjs @@ -1,4 +1,4 @@ -import someModule from './someModule.cjs' +import someModule from "./someModule.cjs"; -const { someFeature } = someModule -console.log(someFeature) +const { someFeature } = someModule; +console.log(someFeature); diff --git a/02-the-module-system/14-import-cjs-from-esm/main4.mjs b/02-the-module-system/14-import-cjs-from-esm/main4.mjs index f9de168..b4cf8b9 100644 --- a/02-the-module-system/14-import-cjs-from-esm/main4.mjs +++ b/02-the-module-system/14-import-cjs-from-esm/main4.mjs @@ -1,6 +1,6 @@ -import { createRequire } from 'node:module' +import { createRequire } from "node:module"; -const require = createRequire(import.meta.url) -const { someFeature } = require('./someModule.cjs') +const require = createRequire(import.meta.url); +const { someFeature } = require("./someModule.cjs"); -console.log(someFeature) +console.log(someFeature); diff --git a/02-the-module-system/14-import-cjs-from-esm/someModule.cjs b/02-the-module-system/14-import-cjs-from-esm/someModule.cjs index 1fd11ee..b71a58b 100644 --- a/02-the-module-system/14-import-cjs-from-esm/someModule.cjs +++ b/02-the-module-system/14-import-cjs-from-esm/someModule.cjs @@ -1,8 +1,8 @@ -'use strict' +"use strict"; module.exports = { - someFeature: 'someFeature', -} + someFeature: "someFeature", +}; // comment the following line and run main2.mjs to see the error -exports.someFeature = 'someFeature' +exports.someFeature = "someFeature"; diff --git a/02-the-module-system/15-import-esm-from-cjs/main.cjs b/02-the-module-system/15-import-esm-from-cjs/main.cjs index 163e51c..5fcf5e8 100644 --- a/02-the-module-system/15-import-esm-from-cjs/main.cjs +++ b/02-the-module-system/15-import-esm-from-cjs/main.cjs @@ -1,7 +1,7 @@ -'use strict' +"use strict"; // This will throw an ERR_REQUIRE_ESM error // unless you use the node flag `--experimental-require-module` -const { someFeature } = require('./someModule.mjs') +const { someFeature } = require("./someModule.mjs"); -console.log(someFeature) +console.log(someFeature); diff --git a/02-the-module-system/15-import-esm-from-cjs/main2.cjs b/02-the-module-system/15-import-esm-from-cjs/main2.cjs index 3e29b99..caa01d5 100644 --- a/02-the-module-system/15-import-esm-from-cjs/main2.cjs +++ b/02-the-module-system/15-import-esm-from-cjs/main2.cjs @@ -1,8 +1,8 @@ -'use strict' +"use strict"; async function main() { - const { someFeature } = await import('./someModule.mjs') - console.log(someFeature) + const { someFeature } = await import("./someModule.mjs"); + console.log(someFeature); } -main() +main(); diff --git a/02-the-module-system/15-import-esm-from-cjs/someModule.mjs b/02-the-module-system/15-import-esm-from-cjs/someModule.mjs index 732afff..e1c6152 100644 --- a/02-the-module-system/15-import-esm-from-cjs/someModule.mjs +++ b/02-the-module-system/15-import-esm-from-cjs/someModule.mjs @@ -1 +1 @@ -export const someFeature = 'someFeature' +export const someFeature = "someFeature"; diff --git a/02-the-module-system/16-import-json-files/main.cjs b/02-the-module-system/16-import-json-files/main.cjs index 540d729..d3e6e37 100644 --- a/02-the-module-system/16-import-json-files/main.cjs +++ b/02-the-module-system/16-import-json-files/main.cjs @@ -1,5 +1,5 @@ -'use strict' +"use strict"; -const data = require('./sample.json') +const data = require("./sample.json"); -console.log(data) +console.log(data); diff --git a/02-the-module-system/16-import-json-files/main.mjs b/02-the-module-system/16-import-json-files/main.mjs index 1eb6805..3859ea2 100644 --- a/02-the-module-system/16-import-json-files/main.mjs +++ b/02-the-module-system/16-import-json-files/main.mjs @@ -1,4 +1,4 @@ // This will fail with an ERR_IMPORT_ATTRIBUTE_MISSING error -import data from './sample.json' +import data from "./sample.json"; -console.log(data) +console.log(data); diff --git a/02-the-module-system/16-import-json-files/main2.mjs b/02-the-module-system/16-import-json-files/main2.mjs index e1dc3c6..d676d79 100644 --- a/02-the-module-system/16-import-json-files/main2.mjs +++ b/02-the-module-system/16-import-json-files/main2.mjs @@ -1,3 +1,3 @@ -import data from './sample.json' with { type: 'json' } +import data from "./sample.json" with { type: "json" }; -console.log(data) +console.log(data); diff --git a/02-the-module-system/16-import-json-files/main3.mjs b/02-the-module-system/16-import-json-files/main3.mjs index 983ab64..5204bce 100644 --- a/02-the-module-system/16-import-json-files/main3.mjs +++ b/02-the-module-system/16-import-json-files/main3.mjs @@ -1,4 +1,4 @@ -const { default: data } = await import('./sample.json', { - with: { type: 'json' }, -}) -console.log(data) +const { default: data } = await import("./sample.json", { + with: { type: "json" }, +}); +console.log(data); diff --git a/02-the-module-system/16-import-json-files/main4.mjs b/02-the-module-system/16-import-json-files/main4.mjs index 1ce49c6..03ebd56 100644 --- a/02-the-module-system/16-import-json-files/main4.mjs +++ b/02-the-module-system/16-import-json-files/main4.mjs @@ -1,11 +1,11 @@ -import { readFile } from 'node:fs/promises' -import { join } from 'node:path' +import { readFile } from "node:fs/promises"; +import { join } from "node:path"; -const jsonPath = join(import.meta.dirname, 'sample.json') +const jsonPath = join(import.meta.dirname, "sample.json"); try { - const dataRaw = await readFile(jsonPath, 'utf-8') - const data = JSON.parse(dataRaw) - console.log(data) + const dataRaw = await readFile(jsonPath, "utf-8"); + const data = JSON.parse(dataRaw); + console.log(data); } catch (error) { - console.error(error) + console.error(error); } diff --git a/02-the-module-system/16-import-json-files/main5.mjs b/02-the-module-system/16-import-json-files/main5.mjs index 944c2b9..8ffd3b9 100644 --- a/02-the-module-system/16-import-json-files/main5.mjs +++ b/02-the-module-system/16-import-json-files/main5.mjs @@ -1,6 +1,6 @@ -import { createRequire } from 'node:module' +import { createRequire } from "node:module"; -const require = createRequire(import.meta.url) -const data = require('./sample.json') +const require = createRequire(import.meta.url); +const data = require("./sample.json"); -console.log(data) +console.log(data); diff --git a/03-callbacks-and-events/01-sync-cps/index.js b/03-callbacks-and-events/01-sync-cps/index.js index 0f6a4a9..bb68e3d 100644 --- a/03-callbacks-and-events/01-sync-cps/index.js +++ b/03-callbacks-and-events/01-sync-cps/index.js @@ -1,7 +1,7 @@ function addCps(a, b, callback) { - callback(a + b) + callback(a + b); } -console.log('before') -addCps(1, 2, result => console.log(`Result: ${result}`)) -console.log('after') +console.log("before"); +addCps(1, 2, (result) => console.log(`Result: ${result}`)); +console.log("after"); diff --git a/03-callbacks-and-events/02-async-cps/index.js b/03-callbacks-and-events/02-async-cps/index.js index f4ddd6e..e79166f 100644 --- a/03-callbacks-and-events/02-async-cps/index.js +++ b/03-callbacks-and-events/02-async-cps/index.js @@ -1,7 +1,7 @@ function addAsync(a, b, callback) { - setTimeout(() => callback(a + b), 100) + setTimeout(() => callback(a + b), 100); } -console.log('before') -addAsync(1, 2, result => console.log(`Result: ${result}`)) -console.log('after') +console.log("before"); +addAsync(1, 2, (result) => console.log(`Result: ${result}`)); +console.log("after"); diff --git a/03-callbacks-and-events/03-non-cps/index.js b/03-callbacks-and-events/03-non-cps/index.js index f89d56d..0aa4e3d 100644 --- a/03-callbacks-and-events/03-non-cps/index.js +++ b/03-callbacks-and-events/03-non-cps/index.js @@ -1,2 +1,2 @@ -const result = [1, 5, 7].map(element => element - 1) -console.log(result) // [0, 4, 6] +const result = [1, 5, 7].map((element) => element - 1); +console.log(result); // [0, 4, 6] diff --git a/03-callbacks-and-events/04-an-unpredictable-function/index.js b/03-callbacks-and-events/04-an-unpredictable-function/index.js index 2474662..6d02331 100644 --- a/03-callbacks-and-events/04-an-unpredictable-function/index.js +++ b/03-callbacks-and-events/04-an-unpredictable-function/index.js @@ -1,49 +1,49 @@ -import { readFile } from 'node:fs' +import { readFile } from "node:fs"; -const cache = new Map() +const cache = new Map(); function inconsistentRead(filename, cb) { if (cache.has(filename)) { // invoked synchronously - cb(cache.get(filename)) + cb(cache.get(filename)); } else { // asynchronous function - readFile(filename, 'utf8', (_err, data) => { - cache.set(filename, data) - cb(data) - }) + readFile(filename, "utf8", (_err, data) => { + cache.set(filename, data); + cb(data); + }); } } function createFileReader(filename) { - const listeners = [] - inconsistentRead(filename, value => { + const listeners = []; + inconsistentRead(filename, (value) => { for (const listener of listeners) { - listener(value) + listener(value); } - }) + }); return { - onDataReady: listener => listeners.push(listener), - } + onDataReady: (listener) => listeners.push(listener), + }; } // this makes sure that the `data.txt` file is relative to // this JS file (and not the current working directory) -const filePath = new URL('data.txt', import.meta.url) +const filePath = new URL("data.txt", import.meta.url); -const reader1 = createFileReader(filePath) -reader1.onDataReady(data => { - console.log(`First call data: ${data}`) +const reader1 = createFileReader(filePath); +reader1.onDataReady((data) => { + console.log(`First call data: ${data}`); // ...sometime later we try to read again from // the same file - const reader2 = createFileReader(filePath) - reader2.onDataReady(data => { + const reader2 = createFileReader(filePath); + reader2.onDataReady((data) => { // this won't print because the data has been cached // and the previous call to `createFileReader` // has already invoked the callback before we have // the chance to add a new listener - console.log(`Second call data: ${data}`) - }) -}) + console.log(`Second call data: ${data}`); + }); +}); diff --git a/03-callbacks-and-events/05-fix-zalgo-with-sync-api/index.js b/03-callbacks-and-events/05-fix-zalgo-with-sync-api/index.js index 715de62..2379612 100644 --- a/03-callbacks-and-events/05-fix-zalgo-with-sync-api/index.js +++ b/03-callbacks-and-events/05-fix-zalgo-with-sync-api/index.js @@ -1,18 +1,18 @@ -import { readFileSync } from 'node:fs' +import { readFileSync } from "node:fs"; -const cache = new Map() +const cache = new Map(); function consistentReadSync(filename) { if (cache.has(filename)) { - return cache.get(filename) + return cache.get(filename); } - const data = readFileSync(filename, 'utf8') - cache.set(filename, data) - return data + const data = readFileSync(filename, "utf8"); + cache.set(filename, data); + return data; } -const filename = new URL('data.txt', import.meta.url) -console.log(consistentReadSync(filename)) +const filename = new URL("data.txt", import.meta.url); +console.log(consistentReadSync(filename)); // the next call will read from the cache -console.log(consistentReadSync(filename)) +console.log(consistentReadSync(filename)); diff --git a/03-callbacks-and-events/06-deferred-execution/index.js b/03-callbacks-and-events/06-deferred-execution/index.js index bd64132..ab11a12 100644 --- a/03-callbacks-and-events/06-deferred-execution/index.js +++ b/03-callbacks-and-events/06-deferred-execution/index.js @@ -1,42 +1,42 @@ -import { readFile } from 'node:fs' +import { readFile } from "node:fs"; -const cache = new Map() +const cache = new Map(); function consistentReadAsync(filename, callback) { if (cache.has(filename)) { // deferred callback invocation - process.nextTick(() => callback(cache.get(filename))) + process.nextTick(() => callback(cache.get(filename))); } else { // asynchronous function - readFile(filename, 'utf8', (_err, data) => { - cache.set(filename, data) - callback(data) - }) + readFile(filename, "utf8", (_err, data) => { + cache.set(filename, data); + callback(data); + }); } } function createFileReader(filename) { - const listeners = [] - consistentReadAsync(filename, value => { + const listeners = []; + consistentReadAsync(filename, (value) => { for (const listener of listeners) { - listener(value) + listener(value); } - }) + }); return { - onDataReady: listener => listeners.push(listener), - } + onDataReady: (listener) => listeners.push(listener), + }; } -const filename = new URL('data.txt', import.meta.url) -const reader1 = createFileReader(filename) -reader1.onDataReady(data => { - console.log(`First call data: ${data}`) +const filename = new URL("data.txt", import.meta.url); +const reader1 = createFileReader(filename); +reader1.onDataReady((data) => { + console.log(`First call data: ${data}`); // ...sometime later we try to read again from // the same file - const reader2 = createFileReader(filename) - reader2.onDataReady(data => { - console.log(`Second call data: ${data}`) - }) -}) + const reader2 = createFileReader(filename); + reader2.onDataReady((data) => { + console.log(`Second call data: ${data}`); + }); +}); diff --git a/03-callbacks-and-events/07-io-starvation/index.js b/03-callbacks-and-events/07-io-starvation/index.js index 213af44..b223055 100644 --- a/03-callbacks-and-events/07-io-starvation/index.js +++ b/03-callbacks-and-events/07-io-starvation/index.js @@ -1,16 +1,16 @@ -import { readFile } from 'node:fs' +import { readFile } from "node:fs"; -const filePath = new URL('data.txt', import.meta.url) -readFile(filePath, 'utf8', (_err, data) => { - console.log(`Data from file: ${data}`) -}) +const filePath = new URL("data.txt", import.meta.url); +readFile(filePath, "utf8", (_err, data) => { + console.log(`Data from file: ${data}`); +}); -let scheduledNextTicks = 0 +let scheduledNextTicks = 0; function recursiveNextTick() { if (scheduledNextTicks++ >= 1000) { - return + return; } - console.log('Keeping the event loop busy') - process.nextTick(() => recursiveNextTick()) + console.log("Keeping the event loop busy"); + process.nextTick(() => recursiveNextTick()); } -recursiveNextTick() +recursiveNextTick(); diff --git a/03-callbacks-and-events/08-scheduling-callbacks-methods/index.js b/03-callbacks-and-events/08-scheduling-callbacks-methods/index.js index 7d6f337..f1e0299 100644 --- a/03-callbacks-and-events/08-scheduling-callbacks-methods/index.js +++ b/03-callbacks-and-events/08-scheduling-callbacks-methods/index.js @@ -1,13 +1,13 @@ setImmediate(() => { - console.log('setImmediate(cb)') -}) + console.log("setImmediate(cb)"); +}); setTimeout(() => { - console.log('setTimeout(cb, 0)') -}, 0) + console.log("setTimeout(cb, 0)"); +}, 0); process.nextTick(() => { - console.log('process.nextTick(cb)') -}) + console.log("process.nextTick(cb)"); +}); -console.log('Sync operation') +console.log("Sync operation"); diff --git a/03-callbacks-and-events/09-propagating-errors/index.js b/03-callbacks-and-events/09-propagating-errors/index.js index a6d2c48..fad8728 100644 --- a/03-callbacks-and-events/09-propagating-errors/index.js +++ b/03-callbacks-and-events/09-propagating-errors/index.js @@ -1,32 +1,32 @@ -import { readFile } from 'node:fs' +import { readFile } from "node:fs"; function readJson(filename, callback) { - readFile(filename, 'utf8', (err, data) => { - let parsed + readFile(filename, "utf8", (err, data) => { + let parsed; if (err) { // error reading the file // propagate the error and exit the current function - return callback(err) + return callback(err); } try { // parse the file contents - parsed = JSON.parse(data) + parsed = JSON.parse(data); } catch (err) { // catch parsing errors - return callback(err) + return callback(err); } // no errors, propagate just the data - callback(null, parsed) - }) + callback(null, parsed); + }); } const cb = (err, data) => { if (err) { - return console.error(err) + return console.error(err); } - console.log(data) -} + console.log(data); +}; -readJson(new URL('valid_json.json', import.meta.url), cb) // dumps the content -readJson(new URL('invalid_json.json', import.meta.url), cb) // prints error (SyntaxError) +readJson(new URL("valid_json.json", import.meta.url), cb); // dumps the content +readJson(new URL("invalid_json.json", import.meta.url), cb); // prints error (SyntaxError) diff --git a/03-callbacks-and-events/10-uncaught-errors/index.js b/03-callbacks-and-events/10-uncaught-errors/index.js index 7c60f71..e94e8b3 100644 --- a/03-callbacks-and-events/10-uncaught-errors/index.js +++ b/03-callbacks-and-events/10-uncaught-errors/index.js @@ -1,30 +1,30 @@ -import { readFile } from 'node:fs' +import { readFile } from "node:fs"; function readJsonThrows(filename, callback) { - readFile(filename, 'utf8', (err, data) => { + readFile(filename, "utf8", (err, data) => { if (err) { - return callback(err) + return callback(err); } - callback(null, JSON.parse(data)) - }) + callback(null, JSON.parse(data)); + }); } // The error is not propagated to the final callback nor is caught // by a try/catch statement try { - readJsonThrows(new URL('invalid_json.json', import.meta.url), err => - console.error(err) - ) + readJsonThrows(new URL("invalid_json.json", import.meta.url), (err) => + console.error(err), + ); } catch (_err) { - console.log('This will NOT catch the JSON parsing exception') + console.log("This will NOT catch the JSON parsing exception"); } // Our last chance to intercept any uncaught error -process.on('uncaughtException', err => { +process.on("uncaughtException", (err) => { console.error( - `This will catch at last the JSON parsing exception: ${err.message}` - ) + `This will catch at last the JSON parsing exception: ${err.message}`, + ); // Terminates the application with 1 (error) as exit code. // Without the following line, the application would continue - process.exit(1) -}) + process.exit(1); +}); diff --git a/03-callbacks-and-events/11-eventemitter-find-regex/index.js b/03-callbacks-and-events/11-eventemitter-find-regex/index.js index ecc01c8..d6a90a0 100644 --- a/03-callbacks-and-events/11-eventemitter-find-regex/index.js +++ b/03-callbacks-and-events/11-eventemitter-find-regex/index.js @@ -1,31 +1,31 @@ -import { EventEmitter } from 'node:events' -import { readFile } from 'node:fs' +import { EventEmitter } from "node:events"; +import { readFile } from "node:fs"; function findRegex(files, regex) { - const emitter = new EventEmitter() + const emitter = new EventEmitter(); for (const file of files) { - readFile(file, 'utf8', (err, content) => { + readFile(file, "utf8", (err, content) => { if (err) { - return emitter.emit('error', err) + return emitter.emit("error", err); } - emitter.emit('fileread', file) - const match = content.match(regex) + emitter.emit("fileread", file); + const match = content.match(regex); if (match) { for (const elem of match) { - emitter.emit('found', file, elem) + emitter.emit("found", file, elem); } } - }) + }); } - return emitter + return emitter; } const files = [ - new URL('fileA.txt', import.meta.url), - new URL('fileB.json', import.meta.url), -] + new URL("fileA.txt", import.meta.url), + new URL("fileB.json", import.meta.url), +]; findRegex(files, /hello [\w.]+/) - .on('fileread', file => console.log(`${file} was read`)) - .on('found', (file, match) => console.log(`Matched "${match}" in ${file}`)) - .on('error', err => console.error(`Error emitted ${err.message}`)) + .on("fileread", (file) => console.log(`${file} was read`)) + .on("found", (file, match) => console.log(`Matched "${match}" in ${file}`)) + .on("error", (err) => console.error(`Error emitted ${err.message}`)); diff --git a/03-callbacks-and-events/12-eventemitter-extend/index.js b/03-callbacks-and-events/12-eventemitter-extend/index.js index bc23522..a9ce8ff 100644 --- a/03-callbacks-and-events/12-eventemitter-extend/index.js +++ b/03-callbacks-and-events/12-eventemitter-extend/index.js @@ -1,45 +1,45 @@ -import { EventEmitter } from 'node:events' -import { readFile } from 'node:fs' +import { EventEmitter } from "node:events"; +import { readFile } from "node:fs"; class FindRegex extends EventEmitter { constructor(regex) { - super() - this.regex = regex - this.files = [] + super(); + this.regex = regex; + this.files = []; } addFile(file) { - this.files.push(file) - return this + this.files.push(file); + return this; } find() { for (const file of this.files) { - readFile(file, 'utf8', (err, content) => { + readFile(file, "utf8", (err, content) => { if (err) { - return this.emit('error', err) + return this.emit("error", err); } - this.emit('fileread', file) + this.emit("fileread", file); - const match = content.match(this.regex) + const match = content.match(this.regex); if (match) { for (const elem of match) { - this.emit('found', file, elem) + this.emit("found", file, elem); } } - }) + }); } - return this + return this; } } -const findRegexInstance = new FindRegex(/hello [\w.]+/) +const findRegexInstance = new FindRegex(/hello [\w.]+/); findRegexInstance - .addFile(new URL('fileA.txt', import.meta.url)) - .addFile(new URL('fileB.json', import.meta.url)) + .addFile(new URL("fileA.txt", import.meta.url)) + .addFile(new URL("fileB.json", import.meta.url)) .find() - .on('found', (file, match) => - console.log(`Matched "${match}" in file ${file}`) + .on("found", (file, match) => + console.log(`Matched "${match}" in file ${file}`), ) - .on('error', err => console.error(`Error emitted ${err.message}`)) + .on("error", (err) => console.error(`Error emitted ${err.message}`)); diff --git a/03-callbacks-and-events/13-eventemitter-sync-emit/index.js b/03-callbacks-and-events/13-eventemitter-sync-emit/index.js index 9f95fd1..35bac0c 100644 --- a/03-callbacks-and-events/13-eventemitter-sync-emit/index.js +++ b/03-callbacks-and-events/13-eventemitter-sync-emit/index.js @@ -1,45 +1,45 @@ -import { EventEmitter } from 'node:events' -import { readFileSync } from 'node:fs' +import { EventEmitter } from "node:events"; +import { readFileSync } from "node:fs"; class FindRegexSync extends EventEmitter { constructor(regex) { - super() - this.regex = regex - this.files = [] + super(); + this.regex = regex; + this.files = []; } addFile(file) { - this.files.push(file) - return this + this.files.push(file); + return this; } find() { for (const file of this.files) { - let content + let content; try { - content = readFileSync(file, 'utf8') + content = readFileSync(file, "utf8"); } catch (err) { - this.emit('error', err) + this.emit("error", err); } - this.emit('fileread', file) - const match = content.match(this.regex) + this.emit("fileread", file); + const match = content.match(this.regex); if (match) { for (const elem of match) { - this.emit('found', file, elem) + this.emit("found", file, elem); } } } - return this + return this; } } -const findRegexSyncInstance = new FindRegexSync(/hello [\w.]+/) +const findRegexSyncInstance = new FindRegexSync(/hello [\w.]+/); findRegexSyncInstance - .addFile(new URL('fileA.txt', import.meta.url)) - .addFile(new URL('fileB.json', import.meta.url)) + .addFile(new URL("fileA.txt", import.meta.url)) + .addFile(new URL("fileB.json", import.meta.url)) // this listener is invoked - .on('found', (_file, match) => console.log(`[Before] Matched "${match}"`)) + .on("found", (_file, match) => console.log(`[Before] Matched "${match}"`)) .find() // this listener is never invoked - .on('found', (_file, match) => console.log(`[After] Matched "${match}"`)) + .on("found", (_file, match) => console.log(`[After] Matched "${match}"`)); diff --git a/03-callbacks-and-events/14-events-vs-callbacks/index.js b/03-callbacks-and-events/14-events-vs-callbacks/index.js index 5660c63..5d04efc 100644 --- a/03-callbacks-and-events/14-events-vs-callbacks/index.js +++ b/03-callbacks-and-events/14-events-vs-callbacks/index.js @@ -1,14 +1,14 @@ -import { EventEmitter } from 'node:events' +import { EventEmitter } from "node:events"; function helloEvents() { - const eventEmitter = new EventEmitter() - setTimeout(() => eventEmitter.emit('complete', 'hello world'), 100) - return eventEmitter + const eventEmitter = new EventEmitter(); + setTimeout(() => eventEmitter.emit("complete", "hello world"), 100); + return eventEmitter; } function helloCallback(cb) { - setTimeout(() => cb(null, 'hello world'), 100) + setTimeout(() => cb(null, "hello world"), 100); } -helloEvents().on('complete', message => console.log(message)) -helloCallback((_err, message) => console.log(message)) +helloEvents().on("complete", (message) => console.log(message)); +helloCallback((_err, message) => console.log(message)); diff --git a/03-callbacks-and-events/15-events-plus-callbacks/index.js b/03-callbacks-and-events/15-events-plus-callbacks/index.js index e1fe79d..3bc95fe 100644 --- a/03-callbacks-and-events/15-events-plus-callbacks/index.js +++ b/03-callbacks-and-events/15-events-plus-callbacks/index.js @@ -1,45 +1,45 @@ -import { EventEmitter } from 'node:events' -import { get } from 'node:https' +import { EventEmitter } from "node:events"; +import { get } from "node:https"; function download(url, cb) { - const eventEmitter = new EventEmitter() + const eventEmitter = new EventEmitter(); - const req = get(url, resp => { - const chunks = [] - let downloadedBytes = 0 - const fileSize = Number.parseInt(resp.headers['content-length'], 10) + const req = get(url, (resp) => { + const chunks = []; + let downloadedBytes = 0; + const fileSize = Number.parseInt(resp.headers["content-length"], 10); resp - .on('error', err => { - cb(err) + .on("error", (err) => { + cb(err); }) - .on('data', chunk => { - chunks.push(chunk) - downloadedBytes += chunk.length - eventEmitter.emit('progress', downloadedBytes, fileSize) + .on("data", (chunk) => { + chunks.push(chunk); + downloadedBytes += chunk.length; + eventEmitter.emit("progress", downloadedBytes, fileSize); }) - .on('end', () => { - const data = Buffer.concat(chunks) - cb(null, data) - }) - }) + .on("end", () => { + const data = Buffer.concat(chunks); + cb(null, data); + }); + }); - req.on('error', err => { - cb(err) - }) + req.on("error", (err) => { + cb(err); + }); - return eventEmitter + return eventEmitter; } download( - 'https://www.nodejsdesignpatterns.com/img/node-js-design-patterns.jpg', + "https://www.nodejsdesignpatterns.com/img/node-js-design-patterns.jpg", (err, data) => { if (err) { - return console.error(`Download failed: ${err.message}`) + return console.error(`Download failed: ${err.message}`); } - console.log('Download completed', data) - } -).on('progress', (downloaded, total) => { + console.log("Download completed", data); + }, +).on("progress", (downloaded, total) => { console.log( - `${downloaded}/${total} (${((downloaded / total) * 100).toFixed(2)}%)` - ) -}) + `${downloaded}/${total} (${((downloaded / total) * 100).toFixed(2)}%)`, + ); +}); diff --git a/04-asynchronous-control-flow-patterns-with-callbacks/01-web-spider/spider-cli.js b/04-asynchronous-control-flow-patterns-with-callbacks/01-web-spider/spider-cli.js index 682964e..ccaf72c 100644 --- a/04-asynchronous-control-flow-patterns-with-callbacks/01-web-spider/spider-cli.js +++ b/04-asynchronous-control-flow-patterns-with-callbacks/01-web-spider/spider-cli.js @@ -1,14 +1,14 @@ -import { spider } from './spider.js' +import { spider } from "./spider.js"; spider(process.argv[2], (err, filename, downloaded) => { if (err) { - console.error(err) - process.exit(1) + console.error(err); + process.exit(1); } if (downloaded) { - console.log(`Completed the download of "${filename}"`) + console.log(`Completed the download of "${filename}"`); } else { - console.log(`"${filename}" was already downloaded`) + console.log(`"${filename}" was already downloaded`); } -}) +}); diff --git a/04-asynchronous-control-flow-patterns-with-callbacks/01-web-spider/spider.js b/04-asynchronous-control-flow-patterns-with-callbacks/01-web-spider/spider.js index e5cfb3d..6e4379a 100644 --- a/04-asynchronous-control-flow-patterns-with-callbacks/01-web-spider/spider.js +++ b/04-asynchronous-control-flow-patterns-with-callbacks/01-web-spider/spider.js @@ -1,35 +1,35 @@ -import { writeFile } from 'node:fs' -import { dirname } from 'node:path' -import { exists, get, recursiveMkdir, urlToFilename } from './utils.js' +import { writeFile } from "node:fs"; +import { dirname } from "node:path"; +import { exists, get, recursiveMkdir, urlToFilename } from "./utils.js"; export function spider(url, cb) { - const filename = urlToFilename(url) + const filename = urlToFilename(url); exists(filename, (err, alreadyExists) => { if (err) { - cb(err) + cb(err); } else if (alreadyExists) { - cb(null, filename, false) + cb(null, filename, false); } else { - console.log(`Downloading ${url} into ${filename}`) + console.log(`Downloading ${url} into ${filename}`); get(url, (err, content) => { if (err) { - cb(err) + cb(err); } else { - recursiveMkdir(dirname(filename), err => { + recursiveMkdir(dirname(filename), (err) => { if (err) { - cb(err) + cb(err); } else { - writeFile(filename, content, err => { + writeFile(filename, content, (err) => { if (err) { - cb(err) + cb(err); } else { - cb(null, filename, true) + cb(null, filename, true); } - }) + }); } - }) + }); } - }) + }); } - }) + }); } diff --git a/04-asynchronous-control-flow-patterns-with-callbacks/01-web-spider/utils.js b/04-asynchronous-control-flow-patterns-with-callbacks/01-web-spider/utils.js index 63580c6..b46eba4 100644 --- a/04-asynchronous-control-flow-patterns-with-callbacks/01-web-spider/utils.js +++ b/04-asynchronous-control-flow-patterns-with-callbacks/01-web-spider/utils.js @@ -1,39 +1,40 @@ -import { constants, access } from 'node:fs' -import { extname, join } from 'node:path' -import { mkdirp } from 'mkdirp' -import slug from 'slug' +import { access, constants } from "node:fs"; +import { extname, join } from "node:path"; +import { mkdirp } from "mkdirp"; +import slug from "slug"; export function exists(filePath, cb) { - access(filePath, constants.F_OK, err => { + access(filePath, constants.F_OK, (err) => { if (err) { - if (err.code === 'ENOENT') { + if (err.code === "ENOENT") { // the file does not exist - return cb(null, false) + return cb(null, false); } // unexpected error checking the file - return cb(err) + return cb(err); } // the file exists - return cb(null, true) - }) + return cb(null, true); + }); } export function urlToFilename(url) { - const parsedUrl = new URL(url) - const urlComponents = parsedUrl.pathname.split('/') - const originalFileName = urlComponents.pop() + const parsedUrl = new URL(url); + const urlComponents = parsedUrl.pathname.split("/"); + const originalFileName = urlComponents.pop(); const urlPath = urlComponents - .filter(component => component !== '') - .map(component => slug(component, { remove: null })) - .join('/') - const basePath = join(parsedUrl.hostname, urlPath) - const missingExtension = !originalFileName || extname(originalFileName) === '' + .filter((component) => component !== "") + .map((component) => slug(component, { remove: null })) + .join("/"); + const basePath = join(parsedUrl.hostname, urlPath); + const missingExtension = + !originalFileName || extname(originalFileName) === ""; if (missingExtension) { - return join(basePath, originalFileName, 'index.html') + return join(basePath, originalFileName, "index.html"); } - return join(basePath, originalFileName) + return join(basePath, originalFileName); } // NOTE: this function is just for illustrative purposes. We are wrapping @@ -41,17 +42,17 @@ export function urlToFilename(url) { // to demonstrate callback based patterns export function get(url, cb) { fetch(url) - .then(response => { + .then((response) => { if (!response.ok) { - throw new Error(`Failed to fetch ${url}: ${response.statusText}`) + throw new Error(`Failed to fetch ${url}: ${response.statusText}`); } // NOTE: this loads all the content in memory and therefore is not suitable // to handle large payloads. // For large payloads, we would need to use a stream-based approach - return response.arrayBuffer() + return response.arrayBuffer(); }) - .then(content => cb(null, Buffer.from(content))) - .catch(err => cb(err)) + .then((content) => cb(null, Buffer.from(content))) + .catch((err) => cb(err)); } // NOTE: this function is just for illustrative purposes. We are wrapping @@ -60,5 +61,5 @@ export function get(url, cb) { export function recursiveMkdir(path, cb) { mkdirp(path) .then(() => cb(null)) - .catch(e => cb(e)) + .catch((e) => cb(e)); } diff --git a/04-asynchronous-control-flow-patterns-with-callbacks/01-web-spider/utils.test.js b/04-asynchronous-control-flow-patterns-with-callbacks/01-web-spider/utils.test.js index 449ac0f..194de92 100644 --- a/04-asynchronous-control-flow-patterns-with-callbacks/01-web-spider/utils.test.js +++ b/04-asynchronous-control-flow-patterns-with-callbacks/01-web-spider/utils.test.js @@ -1,16 +1,16 @@ -import { deepEqual } from 'node:assert/strict' -import { test } from 'node:test' -import { urlToFilename } from './utils.js' +import { deepEqual } from "node:assert/strict"; +import { test } from "node:test"; +import { urlToFilename } from "./utils.js"; -test('urlToFilename', () => { +test("urlToFilename", () => { const cases = [ - ['https://example.com', 'example.com/index.html'], - ['https://example.com/test', 'example.com/test/index.html'], - ['https://example.com/test/', 'example.com/test/index.html'], - ['https://example.com/image.jpg', 'example.com/image.jpg'], - ['https://example.com/image.jpg?size=md', 'example.com/image.jpg'], - ] + ["https://example.com", "example.com/index.html"], + ["https://example.com/test", "example.com/test/index.html"], + ["https://example.com/test/", "example.com/test/index.html"], + ["https://example.com/image.jpg", "example.com/image.jpg"], + ["https://example.com/image.jpg?size=md", "example.com/image.jpg"], + ]; for (const [url, expected] of cases) { - deepEqual(urlToFilename(url), expected) + deepEqual(urlToFilename(url), expected); } -}) +}); diff --git a/04-asynchronous-control-flow-patterns-with-callbacks/02-web-spider-callback-discipline/spider-cli.js b/04-asynchronous-control-flow-patterns-with-callbacks/02-web-spider-callback-discipline/spider-cli.js index 682964e..ccaf72c 100644 --- a/04-asynchronous-control-flow-patterns-with-callbacks/02-web-spider-callback-discipline/spider-cli.js +++ b/04-asynchronous-control-flow-patterns-with-callbacks/02-web-spider-callback-discipline/spider-cli.js @@ -1,14 +1,14 @@ -import { spider } from './spider.js' +import { spider } from "./spider.js"; spider(process.argv[2], (err, filename, downloaded) => { if (err) { - console.error(err) - process.exit(1) + console.error(err); + process.exit(1); } if (downloaded) { - console.log(`Completed the download of "${filename}"`) + console.log(`Completed the download of "${filename}"`); } else { - console.log(`"${filename}" was already downloaded`) + console.log(`"${filename}" was already downloaded`); } -}) +}); diff --git a/04-asynchronous-control-flow-patterns-with-callbacks/02-web-spider-callback-discipline/spider.js b/04-asynchronous-control-flow-patterns-with-callbacks/02-web-spider-callback-discipline/spider.js index 2a07da3..c568310 100644 --- a/04-asynchronous-control-flow-patterns-with-callbacks/02-web-spider-callback-discipline/spider.js +++ b/04-asynchronous-control-flow-patterns-with-callbacks/02-web-spider-callback-discipline/spider.js @@ -1,45 +1,45 @@ -import { writeFile } from 'node:fs' -import { dirname } from 'node:path' -import { exists, get, recursiveMkdir, urlToFilename } from './utils.js' +import { writeFile } from "node:fs"; +import { dirname } from "node:path"; +import { exists, get, recursiveMkdir, urlToFilename } from "./utils.js"; function saveFile(filename, content, cb) { - recursiveMkdir(dirname(filename), err => { + recursiveMkdir(dirname(filename), (err) => { if (err) { - return cb(err) + return cb(err); } - writeFile(filename, content, cb) - }) + writeFile(filename, content, cb); + }); } function download(url, filename, cb) { - console.log(`Downloading ${url} into ${filename}`) + console.log(`Downloading ${url} into ${filename}`); get(url, (err, content) => { if (err) { - return cb(err) + return cb(err); } - saveFile(filename, content, err => { + saveFile(filename, content, (err) => { if (err) { - return cb(err) + return cb(err); } - cb(null, content) - }) - }) + cb(null, content); + }); + }); } export function spider(url, cb) { - const filename = urlToFilename(url) + const filename = urlToFilename(url); exists(filename, (err, alreadyExists) => { if (err) { - return cb(err) + return cb(err); } if (alreadyExists) { - return cb(null, filename, false) + return cb(null, filename, false); } - download(url, filename, err => { + download(url, filename, (err) => { if (err) { - return cb(err) + return cb(err); } - cb(null, filename, true) - }) - }) + cb(null, filename, true); + }); + }); } diff --git a/04-asynchronous-control-flow-patterns-with-callbacks/02-web-spider-callback-discipline/utils.js b/04-asynchronous-control-flow-patterns-with-callbacks/02-web-spider-callback-discipline/utils.js index 63580c6..b46eba4 100644 --- a/04-asynchronous-control-flow-patterns-with-callbacks/02-web-spider-callback-discipline/utils.js +++ b/04-asynchronous-control-flow-patterns-with-callbacks/02-web-spider-callback-discipline/utils.js @@ -1,39 +1,40 @@ -import { constants, access } from 'node:fs' -import { extname, join } from 'node:path' -import { mkdirp } from 'mkdirp' -import slug from 'slug' +import { access, constants } from "node:fs"; +import { extname, join } from "node:path"; +import { mkdirp } from "mkdirp"; +import slug from "slug"; export function exists(filePath, cb) { - access(filePath, constants.F_OK, err => { + access(filePath, constants.F_OK, (err) => { if (err) { - if (err.code === 'ENOENT') { + if (err.code === "ENOENT") { // the file does not exist - return cb(null, false) + return cb(null, false); } // unexpected error checking the file - return cb(err) + return cb(err); } // the file exists - return cb(null, true) - }) + return cb(null, true); + }); } export function urlToFilename(url) { - const parsedUrl = new URL(url) - const urlComponents = parsedUrl.pathname.split('/') - const originalFileName = urlComponents.pop() + const parsedUrl = new URL(url); + const urlComponents = parsedUrl.pathname.split("/"); + const originalFileName = urlComponents.pop(); const urlPath = urlComponents - .filter(component => component !== '') - .map(component => slug(component, { remove: null })) - .join('/') - const basePath = join(parsedUrl.hostname, urlPath) - const missingExtension = !originalFileName || extname(originalFileName) === '' + .filter((component) => component !== "") + .map((component) => slug(component, { remove: null })) + .join("/"); + const basePath = join(parsedUrl.hostname, urlPath); + const missingExtension = + !originalFileName || extname(originalFileName) === ""; if (missingExtension) { - return join(basePath, originalFileName, 'index.html') + return join(basePath, originalFileName, "index.html"); } - return join(basePath, originalFileName) + return join(basePath, originalFileName); } // NOTE: this function is just for illustrative purposes. We are wrapping @@ -41,17 +42,17 @@ export function urlToFilename(url) { // to demonstrate callback based patterns export function get(url, cb) { fetch(url) - .then(response => { + .then((response) => { if (!response.ok) { - throw new Error(`Failed to fetch ${url}: ${response.statusText}`) + throw new Error(`Failed to fetch ${url}: ${response.statusText}`); } // NOTE: this loads all the content in memory and therefore is not suitable // to handle large payloads. // For large payloads, we would need to use a stream-based approach - return response.arrayBuffer() + return response.arrayBuffer(); }) - .then(content => cb(null, Buffer.from(content))) - .catch(err => cb(err)) + .then((content) => cb(null, Buffer.from(content))) + .catch((err) => cb(err)); } // NOTE: this function is just for illustrative purposes. We are wrapping @@ -60,5 +61,5 @@ export function get(url, cb) { export function recursiveMkdir(path, cb) { mkdirp(path) .then(() => cb(null)) - .catch(e => cb(e)) + .catch((e) => cb(e)); } diff --git a/04-asynchronous-control-flow-patterns-with-callbacks/03-sequential-execution/index.js b/04-asynchronous-control-flow-patterns-with-callbacks/03-sequential-execution/index.js index 9ffb44f..edbe38b 100644 --- a/04-asynchronous-control-flow-patterns-with-callbacks/03-sequential-execution/index.js +++ b/04-asynchronous-control-flow-patterns-with-callbacks/03-sequential-execution/index.js @@ -1,26 +1,26 @@ function asyncOperation(cb) { - process.nextTick(cb) + process.nextTick(cb); } function task1(cb) { asyncOperation(() => { - task2(cb) - }) + task2(cb); + }); } function task2(cb) { asyncOperation(() => { - task3(cb) - }) + task3(cb); + }); } function task3(cb) { asyncOperation(() => { - cb() // finally executes the callback - }) + cb(); // finally executes the callback + }); } task1(() => { // executed when task1, task2, and task3 are completed - console.log('tasks 1, 2, and 3 executed') -}) + console.log("tasks 1, 2, and 3 executed"); +}); diff --git a/04-asynchronous-control-flow-patterns-with-callbacks/04-web-spider-v2/fixtures/index.html b/04-asynchronous-control-flow-patterns-with-callbacks/04-web-spider-v2/fixtures/index.html index a3f1fd4..5347095 100644 --- a/04-asynchronous-control-flow-patterns-with-callbacks/04-web-spider-v2/fixtures/index.html +++ b/04-asynchronous-control-flow-patterns-with-callbacks/04-web-spider-v2/fixtures/index.html @@ -1061,7 +1061,7 @@ } .columns.is-gapless > .column { margin: 0; - padding: 0!important !important; + padding: 0; } .columns.is-gapless:not(:last-child) { margin-bottom: 1.5rem; @@ -1081,60 +1081,60 @@ } } .is-flex-direction-column { - flex-direction: column!important; + flex-direction: column; } .is-justify-content-center { - justify-content: center!important; + justify-content: center; } .mt-1 { - margin-top: 0.25rem!important; + margin-top: 0.25rem; } .mb-1 { - margin-bottom: 0.25rem!important; + margin-bottom: 0.25rem; } .mt-2 { - margin-top: 0.5rem!important; + margin-top: 0.5rem; } .mb-2 { - margin-bottom: 0.5rem!important; + margin-bottom: 0.5rem; } .mt-4 { - margin-top: 1rem!important; + margin-top: 1rem; } .mb-4 { - margin-bottom: 1rem!important; + margin-bottom: 1rem; } .mt-6 { - margin-top: 3rem!important; + margin-top: 3rem; } .mb-6 { - margin-bottom: 3rem!important; + margin-bottom: 3rem; } .pr-4 { - padding-right: 1rem!important; + padding-right: 1rem; } .pb-6 { - padding-bottom: 3rem!important; + padding-bottom: 3rem; } .px-6 { - padding-left: 3rem!important; - padding-right: 3rem!important; + padding-left: 3rem; + padding-right: 3rem; } .py-6 { - padding-top: 3rem!important; - padding-bottom: 3rem!important; + padding-top: 3rem; + padding-bottom: 3rem; } .is-size-7 { - font-size: 0.75rem!important; + font-size: 0.75rem; } .has-text-centered { - text-align: center!important; + text-align: center; } .has-text-right { - text-align: right!important; + text-align: right; } .is-flex { - display: flex!important; + display: flex; } .hero { align-items: stretch; @@ -1277,7 +1277,7 @@ } .chapter9 { transform: rotate(-10deg) translateY(30px); - margin-bottom: -20px!important; + margin-bottom: -20px; } @media screen and (min-width:769px) { .chapter9 { @@ -1286,7 +1286,7 @@ } } nav.navbar { - z-index: 99999!important; + z-index: 99999; } #hero h1 { line-height: 2.75rem; @@ -1507,7 +1507,7 @@ } #authors .avatar { border-radius: 50%; - margin-bottom: 1.5rem!important; + margin-bottom: 1.5rem; } #authors ul { margin: 1.5rem 0; @@ -2988,7 +2988,7 @@

Still in doubt?

} .formkit-form[data-uid="831f119429"] .ordered-list, .formkit-form[data-uid="831f119429"] .unordered-list { - list-style-position: outside !important; + list-style-position: outside; padding-left: 1em; } .formkit-form[data-uid="831f119429"] .list-item { diff --git a/04-asynchronous-control-flow-patterns-with-callbacks/04-web-spider-v2/spider-cli.js b/04-asynchronous-control-flow-patterns-with-callbacks/04-web-spider-v2/spider-cli.js index abe2be6..051ce30 100644 --- a/04-asynchronous-control-flow-patterns-with-callbacks/04-web-spider-v2/spider-cli.js +++ b/04-asynchronous-control-flow-patterns-with-callbacks/04-web-spider-v2/spider-cli.js @@ -1,13 +1,13 @@ -import { spider } from './spider.js' +import { spider } from "./spider.js"; -const url = process.argv[2] -const maxDepth = Number.parseInt(process.argv[3], 10) || 1 +const url = process.argv[2]; +const maxDepth = Number.parseInt(process.argv[3], 10) || 1; -spider(url, maxDepth, err => { +spider(url, maxDepth, (err) => { if (err) { - console.error(err) - process.exit(1) + console.error(err); + process.exit(1); } - console.log('Downloaded complete') -}) + console.log("Downloaded complete"); +}); diff --git a/04-asynchronous-control-flow-patterns-with-callbacks/04-web-spider-v2/spider.js b/04-asynchronous-control-flow-patterns-with-callbacks/04-web-spider-v2/spider.js index f664972..3948cf9 100644 --- a/04-asynchronous-control-flow-patterns-with-callbacks/04-web-spider-v2/spider.js +++ b/04-asynchronous-control-flow-patterns-with-callbacks/04-web-spider-v2/spider.js @@ -1,35 +1,35 @@ -import { readFile, writeFile } from 'node:fs' -import { dirname } from 'node:path' +import { readFile, writeFile } from "node:fs"; +import { dirname } from "node:path"; import { exists, get, getPageLinks, recursiveMkdir, urlToFilename, -} from './utils.js' +} from "./utils.js"; function saveFile(filename, content, cb) { - recursiveMkdir(dirname(filename), err => { + recursiveMkdir(dirname(filename), (err) => { if (err) { - return cb(err) + return cb(err); } - writeFile(filename, content, cb) - }) + writeFile(filename, content, cb); + }); } function download(url, filename, cb) { - console.log(`Downloading ${url} into ${filename}`) + console.log(`Downloading ${url} into ${filename}`); get(url, (err, content) => { if (err) { - return cb(err) + return cb(err); } - saveFile(filename, content, err => { + saveFile(filename, content, (err) => { if (err) { - return cb(err) + return cb(err); } - cb(null, content) - }) - }) + cb(null, content); + }); + }); } function spiderLinks(currentUrl, body, maxDepth, cb) { @@ -37,66 +37,66 @@ function spiderLinks(currentUrl, body, maxDepth, cb) { // Remember Zalgo from Chapter 3? // To prevent that, this function is designed to always // invoke its callback asynchronously. - return process.nextTick(cb) + return process.nextTick(cb); } - const links = getPageLinks(currentUrl, body) + const links = getPageLinks(currentUrl, body); if (links.length === 0) { - return process.nextTick(cb) + return process.nextTick(cb); } function iterate(index) { if (index === links.length) { - return cb() + return cb(); } - spider(links[index], maxDepth - 1, err => { + spider(links[index], maxDepth - 1, (err) => { if (err) { - return cb(err) + return cb(err); } - iterate(index + 1) - }) + iterate(index + 1); + }); } - iterate(0) + iterate(0); } export function spider(url, maxDepth, cb) { - const filename = urlToFilename(url) + const filename = urlToFilename(url); exists(filename, (err, alreadyExists) => { if (err) { // error checking the file - return cb(err) + return cb(err); } if (alreadyExists) { - if (!filename.endsWith('.html')) { + if (!filename.endsWith(".html")) { // ignoring non-HTML resources - return cb() + return cb(); } - return readFile(filename, 'utf8', (err, fileContent) => { + return readFile(filename, "utf8", (err, fileContent) => { if (err) { // error reading the file - return cb(err) + return cb(err); } - return spiderLinks(url, fileContent, maxDepth, cb) - }) + return spiderLinks(url, fileContent, maxDepth, cb); + }); } // The file does not exist, download it download(url, filename, (err, fileContent) => { if (err) { // error downloading the file - return cb(err) + return cb(err); } // if the file is an HTML file, spider it - if (filename.endsWith('.html')) { - return spiderLinks(url, fileContent.toString('utf8'), maxDepth, cb) + if (filename.endsWith(".html")) { + return spiderLinks(url, fileContent.toString("utf8"), maxDepth, cb); } // otherwise, stop here - return cb() - }) - }) + return cb(); + }); + }); } diff --git a/04-asynchronous-control-flow-patterns-with-callbacks/04-web-spider-v2/utils.js b/04-asynchronous-control-flow-patterns-with-callbacks/04-web-spider-v2/utils.js index be35ad1..9bd2157 100644 --- a/04-asynchronous-control-flow-patterns-with-callbacks/04-web-spider-v2/utils.js +++ b/04-asynchronous-control-flow-patterns-with-callbacks/04-web-spider-v2/utils.js @@ -1,40 +1,41 @@ -import { constants, access } from 'node:fs' -import { extname, join } from 'node:path' -import { Parser } from 'htmlparser2' -import { mkdirp } from 'mkdirp' -import slug from 'slug' +import { access, constants } from "node:fs"; +import { extname, join } from "node:path"; +import { Parser } from "htmlparser2"; +import { mkdirp } from "mkdirp"; +import slug from "slug"; export function exists(filePath, cb) { - access(filePath, constants.F_OK, err => { + access(filePath, constants.F_OK, (err) => { if (err) { - if (err.code === 'ENOENT') { + if (err.code === "ENOENT") { // the file does not exist - return cb(null, false) + return cb(null, false); } // unexpected error checking the file - return cb(err) + return cb(err); } // the file exists - return cb(null, true) - }) + return cb(null, true); + }); } export function urlToFilename(url) { - const parsedUrl = new URL(url) - const urlComponents = parsedUrl.pathname.split('/') - const originalFileName = urlComponents.pop() + const parsedUrl = new URL(url); + const urlComponents = parsedUrl.pathname.split("/"); + const originalFileName = urlComponents.pop(); const urlPath = urlComponents - .filter(component => component !== '') - .map(component => slug(component, { remove: null })) - .join('/') - const basePath = join(parsedUrl.hostname, urlPath) - const missingExtension = !originalFileName || extname(originalFileName) === '' + .filter((component) => component !== "") + .map((component) => slug(component, { remove: null })) + .join("/"); + const basePath = join(parsedUrl.hostname, urlPath); + const missingExtension = + !originalFileName || extname(originalFileName) === ""; if (missingExtension) { - return join(basePath, originalFileName, 'index.html') + return join(basePath, originalFileName, "index.html"); } - return join(basePath, originalFileName) + return join(basePath, originalFileName); } // NOTE: this function is just for illustrative purposes. We are wrapping @@ -42,17 +43,17 @@ export function urlToFilename(url) { // to demonstrate callback based patterns export function get(url, cb) { fetch(url) - .then(response => { + .then((response) => { if (!response.ok) { - throw new Error(`Failed to fetch ${url}: ${response.statusText}`) + throw new Error(`Failed to fetch ${url}: ${response.statusText}`); } // NOTE: this loads all the content in memory and therefore is not suitable // to handle large payloads. // For large payloads, we would need to use a stream-based approach - return response.arrayBuffer() + return response.arrayBuffer(); }) - .then(content => cb(null, Buffer.from(content))) - .catch(err => cb(err)) + .then((content) => cb(null, Buffer.from(content))) + .catch((err) => cb(err)); } // NOTE: this function is just for illustrative purposes. We are wrapping @@ -61,26 +62,26 @@ export function get(url, cb) { export function recursiveMkdir(path, cb) { mkdirp(path) .then(() => cb(null)) - .catch(e => cb(e)) + .catch((e) => cb(e)); } export function getPageLinks(currentUrl, body) { - const url = new URL(currentUrl) - const internalLinks = [] + const url = new URL(currentUrl); + const internalLinks = []; const parser = new Parser({ onopentag(name, attribs) { - if (name === 'a' && attribs.href) { - const newUrl = new URL(attribs.href, url) + if (name === "a" && attribs.href) { + const newUrl = new URL(attribs.href, url); if ( newUrl.hostname === url.hostname && newUrl.pathname !== url.pathname ) { - internalLinks.push(newUrl.toString()) + internalLinks.push(newUrl.toString()); } } }, - }) - parser.end(body) + }); + parser.end(body); - return internalLinks + return internalLinks; } diff --git a/04-asynchronous-control-flow-patterns-with-callbacks/04-web-spider-v2/utils.test.js b/04-asynchronous-control-flow-patterns-with-callbacks/04-web-spider-v2/utils.test.js index 312f03f..423abd8 100644 --- a/04-asynchronous-control-flow-patterns-with-callbacks/04-web-spider-v2/utils.test.js +++ b/04-asynchronous-control-flow-patterns-with-callbacks/04-web-spider-v2/utils.test.js @@ -1,18 +1,18 @@ -import { deepEqual } from 'node:assert/strict' -import { readFile } from 'node:fs/promises' -import { join } from 'node:path' -import { test } from 'node:test' -import { getPageLinks } from './utils.js' +import { deepEqual } from "node:assert/strict"; +import { readFile } from "node:fs/promises"; +import { join } from "node:path"; +import { test } from "node:test"; +import { getPageLinks } from "./utils.js"; -test('getPageLinks', async () => { - const sampleFile = join(import.meta.dirname, 'fixtures', 'index.html') - const rawHtml = await readFile(sampleFile, 'utf-8') - const links = getPageLinks('https://www.nodejsdesignpatterns.com', rawHtml) +test("getPageLinks", async () => { + const sampleFile = join(import.meta.dirname, "fixtures", "index.html"); + const rawHtml = await readFile(sampleFile, "utf-8"); + const links = getPageLinks("https://www.nodejsdesignpatterns.com", rawHtml); deepEqual(links, [ - 'https://www.nodejsdesignpatterns.com/blog/node-js-stream-consumer/', - 'https://www.nodejsdesignpatterns.com/blog/javascript-async-iterators/', - 'https://www.nodejsdesignpatterns.com/blog/node-js-development-with-docker-and-docker-compose/', - 'https://www.nodejsdesignpatterns.com/blog/node-js-race-conditions/', - 'https://www.nodejsdesignpatterns.com/blog/5-ways-to-install-node-js/', - ]) -}) + "https://www.nodejsdesignpatterns.com/blog/node-js-stream-consumer/", + "https://www.nodejsdesignpatterns.com/blog/javascript-async-iterators/", + "https://www.nodejsdesignpatterns.com/blog/node-js-development-with-docker-and-docker-compose/", + "https://www.nodejsdesignpatterns.com/blog/node-js-race-conditions/", + "https://www.nodejsdesignpatterns.com/blog/5-ways-to-install-node-js/", + ]); +}); diff --git a/04-asynchronous-control-flow-patterns-with-callbacks/05-callback-iteration-pattern/index.js b/04-asynchronous-control-flow-patterns-with-callbacks/05-callback-iteration-pattern/index.js index d7de206..412aab2 100644 --- a/04-asynchronous-control-flow-patterns-with-callbacks/05-callback-iteration-pattern/index.js +++ b/04-asynchronous-control-flow-patterns-with-callbacks/05-callback-iteration-pattern/index.js @@ -1,29 +1,33 @@ const tasks = [ - cb => { - console.log('Task 1') - setTimeout(cb, 1000) + (cb) => { + console.log("Task 1"); + setTimeout(cb, 1000); }, - cb => { - console.log('Task 2') - setTimeout(cb, 1000) + (cb) => { + console.log("Task 2"); + setTimeout(cb, 1000); }, - cb => { - console.log('Task 3') - setTimeout(cb, 1000) + (cb) => { + console.log("Task 3"); + setTimeout(cb, 1000); }, -] +]; -function iterate(index) { +function iterate(index, tasks, finalCallback) { if (index === tasks.length) { - return finish() + return finalCallback(); } - const task = tasks[index] - task(() => iterate(index + 1)) + const task = tasks[index]; + task(() => iterate(index + 1, tasks, finalCallback)); } function finish() { // iteration completed - console.log('All tasks executed') + console.log("All tasks executed"); } -iterate(0) +function iterateSeries(collection, iteratorCallback, finalCallback) { + iteratorCallback(0, collection, finalCallback); +} + +iterateSeries(tasks, iterate, finish); diff --git a/04-asynchronous-control-flow-patterns-with-callbacks/06-web-spider-v3/spider-cli.js b/04-asynchronous-control-flow-patterns-with-callbacks/06-web-spider-v3/spider-cli.js index abe2be6..051ce30 100644 --- a/04-asynchronous-control-flow-patterns-with-callbacks/06-web-spider-v3/spider-cli.js +++ b/04-asynchronous-control-flow-patterns-with-callbacks/06-web-spider-v3/spider-cli.js @@ -1,13 +1,13 @@ -import { spider } from './spider.js' +import { spider } from "./spider.js"; -const url = process.argv[2] -const maxDepth = Number.parseInt(process.argv[3], 10) || 1 +const url = process.argv[2]; +const maxDepth = Number.parseInt(process.argv[3], 10) || 1; -spider(url, maxDepth, err => { +spider(url, maxDepth, (err) => { if (err) { - console.error(err) - process.exit(1) + console.error(err); + process.exit(1); } - console.log('Downloaded complete') -}) + console.log("Downloaded complete"); +}); diff --git a/04-asynchronous-control-flow-patterns-with-callbacks/06-web-spider-v3/spider.js b/04-asynchronous-control-flow-patterns-with-callbacks/06-web-spider-v3/spider.js index cd1ea23..3a875df 100644 --- a/04-asynchronous-control-flow-patterns-with-callbacks/06-web-spider-v3/spider.js +++ b/04-asynchronous-control-flow-patterns-with-callbacks/06-web-spider-v3/spider.js @@ -1,102 +1,102 @@ -import { readFile, writeFile } from 'node:fs' -import { dirname } from 'node:path' +import { readFile, writeFile } from "node:fs"; +import { dirname } from "node:path"; import { exists, get, getPageLinks, recursiveMkdir, urlToFilename, -} from './utils.js' +} from "./utils.js"; function saveFile(filename, content, cb) { - recursiveMkdir(dirname(filename), err => { + recursiveMkdir(dirname(filename), (err) => { if (err) { - return cb(err) + return cb(err); } - writeFile(filename, content, cb) - }) + writeFile(filename, content, cb); + }); } function download(url, filename, cb) { - console.log(`Downloading ${url} into ${filename}`) + console.log(`Downloading ${url} into ${filename}`); get(url, (err, content) => { if (err) { - return cb(err) + return cb(err); } - saveFile(filename, content, err => { + saveFile(filename, content, (err) => { if (err) { - return cb(err) + return cb(err); } - cb(null, content) - }) - }) + cb(null, content); + }); + }); } function spiderLinks(currentUrl, body, maxDepth, cb) { if (maxDepth === 0) { // Remember Zalgo from Chapter 3? - return process.nextTick(cb) + return process.nextTick(cb); } - const links = getPageLinks(currentUrl, body) + const links = getPageLinks(currentUrl, body); if (links.length === 0) { - return process.nextTick(cb) + return process.nextTick(cb); } - let completed = 0 - let hasErrors = false + let completed = 0; + let hasErrors = false; function done(err) { if (err) { - hasErrors = true - return cb(err) + hasErrors = true; + return cb(err); } if (++completed === links.length && !hasErrors) { - return cb() + return cb(); } } for (const link of links) { - spider(link, maxDepth - 1, done) + spider(link, maxDepth - 1, done); } } export function spider(url, maxDepth, cb) { - const filename = urlToFilename(url) + const filename = urlToFilename(url); exists(filename, (err, alreadyExists) => { if (err) { // error checking the file - return cb(err) + return cb(err); } if (alreadyExists) { - if (!filename.endsWith('.html')) { + if (!filename.endsWith(".html")) { // ignoring non-HTML resources - return cb() + return cb(); } - readFile(filename, 'utf8', (err, fileContent) => { + readFile(filename, "utf8", (err, fileContent) => { if (err) { // error reading the file - return cb(err) + return cb(err); } - return spiderLinks(url, fileContent, maxDepth, cb) - }) + return spiderLinks(url, fileContent, maxDepth, cb); + }); } else { // The file does not exist, download it download(url, filename, (err, fileContent) => { if (err) { // error downloading the file - return cb(err) + return cb(err); } // if the file is an HTML file, spider it - if (filename.endsWith('.html')) { - return spiderLinks(url, fileContent.toString('utf8'), maxDepth, cb) + if (filename.endsWith(".html")) { + return spiderLinks(url, fileContent.toString("utf8"), maxDepth, cb); } // otherwise, stop here - return cb() - }) + return cb(); + }); } - }) + }); } diff --git a/04-asynchronous-control-flow-patterns-with-callbacks/06-web-spider-v3/utils.js b/04-asynchronous-control-flow-patterns-with-callbacks/06-web-spider-v3/utils.js index be35ad1..9bd2157 100644 --- a/04-asynchronous-control-flow-patterns-with-callbacks/06-web-spider-v3/utils.js +++ b/04-asynchronous-control-flow-patterns-with-callbacks/06-web-spider-v3/utils.js @@ -1,40 +1,41 @@ -import { constants, access } from 'node:fs' -import { extname, join } from 'node:path' -import { Parser } from 'htmlparser2' -import { mkdirp } from 'mkdirp' -import slug from 'slug' +import { access, constants } from "node:fs"; +import { extname, join } from "node:path"; +import { Parser } from "htmlparser2"; +import { mkdirp } from "mkdirp"; +import slug from "slug"; export function exists(filePath, cb) { - access(filePath, constants.F_OK, err => { + access(filePath, constants.F_OK, (err) => { if (err) { - if (err.code === 'ENOENT') { + if (err.code === "ENOENT") { // the file does not exist - return cb(null, false) + return cb(null, false); } // unexpected error checking the file - return cb(err) + return cb(err); } // the file exists - return cb(null, true) - }) + return cb(null, true); + }); } export function urlToFilename(url) { - const parsedUrl = new URL(url) - const urlComponents = parsedUrl.pathname.split('/') - const originalFileName = urlComponents.pop() + const parsedUrl = new URL(url); + const urlComponents = parsedUrl.pathname.split("/"); + const originalFileName = urlComponents.pop(); const urlPath = urlComponents - .filter(component => component !== '') - .map(component => slug(component, { remove: null })) - .join('/') - const basePath = join(parsedUrl.hostname, urlPath) - const missingExtension = !originalFileName || extname(originalFileName) === '' + .filter((component) => component !== "") + .map((component) => slug(component, { remove: null })) + .join("/"); + const basePath = join(parsedUrl.hostname, urlPath); + const missingExtension = + !originalFileName || extname(originalFileName) === ""; if (missingExtension) { - return join(basePath, originalFileName, 'index.html') + return join(basePath, originalFileName, "index.html"); } - return join(basePath, originalFileName) + return join(basePath, originalFileName); } // NOTE: this function is just for illustrative purposes. We are wrapping @@ -42,17 +43,17 @@ export function urlToFilename(url) { // to demonstrate callback based patterns export function get(url, cb) { fetch(url) - .then(response => { + .then((response) => { if (!response.ok) { - throw new Error(`Failed to fetch ${url}: ${response.statusText}`) + throw new Error(`Failed to fetch ${url}: ${response.statusText}`); } // NOTE: this loads all the content in memory and therefore is not suitable // to handle large payloads. // For large payloads, we would need to use a stream-based approach - return response.arrayBuffer() + return response.arrayBuffer(); }) - .then(content => cb(null, Buffer.from(content))) - .catch(err => cb(err)) + .then((content) => cb(null, Buffer.from(content))) + .catch((err) => cb(err)); } // NOTE: this function is just for illustrative purposes. We are wrapping @@ -61,26 +62,26 @@ export function get(url, cb) { export function recursiveMkdir(path, cb) { mkdirp(path) .then(() => cb(null)) - .catch(e => cb(e)) + .catch((e) => cb(e)); } export function getPageLinks(currentUrl, body) { - const url = new URL(currentUrl) - const internalLinks = [] + const url = new URL(currentUrl); + const internalLinks = []; const parser = new Parser({ onopentag(name, attribs) { - if (name === 'a' && attribs.href) { - const newUrl = new URL(attribs.href, url) + if (name === "a" && attribs.href) { + const newUrl = new URL(attribs.href, url); if ( newUrl.hostname === url.hostname && newUrl.pathname !== url.pathname ) { - internalLinks.push(newUrl.toString()) + internalLinks.push(newUrl.toString()); } } }, - }) - parser.end(body) + }); + parser.end(body); - return internalLinks + return internalLinks; } diff --git a/04-asynchronous-control-flow-patterns-with-callbacks/07-callback-unlimited-concurrent-execution-pattern/index.js b/04-asynchronous-control-flow-patterns-with-callbacks/07-callback-unlimited-concurrent-execution-pattern/index.js index 7d04abc..1cf81cf 100644 --- a/04-asynchronous-control-flow-patterns-with-callbacks/07-callback-unlimited-concurrent-execution-pattern/index.js +++ b/04-asynchronous-control-flow-patterns-with-callbacks/07-callback-unlimited-concurrent-execution-pattern/index.js @@ -1,30 +1,30 @@ function makeSampleTask(name) { - return cb => { - console.log(`${name} started`) + return (cb) => { + console.log(`${name} started`); setTimeout(() => { - console.log(`${name} completed`) - cb() - }, Math.random() * 2000) - } + console.log(`${name} completed`); + cb(); + }, Math.random() * 2000); + }; } const tasks = [ - makeSampleTask('Task 1'), - makeSampleTask('Task 2'), - makeSampleTask('Task 3'), - makeSampleTask('Task 4'), -] + makeSampleTask("Task 1"), + makeSampleTask("Task 2"), + makeSampleTask("Task 3"), + makeSampleTask("Task 4"), +]; -let completed = 0 +let completed = 0; for (const task of tasks) { task(() => { if (++completed === tasks.length) { - finish() + finish(); } - }) + }); } function finish() { // all the tasks completed - console.log('All tasks executed!') + console.log("All tasks executed!"); } diff --git a/04-asynchronous-control-flow-patterns-with-callbacks/08-web-spider-v3-no-race/spider-cli.js b/04-asynchronous-control-flow-patterns-with-callbacks/08-web-spider-v3-no-race/spider-cli.js index abe2be6..051ce30 100644 --- a/04-asynchronous-control-flow-patterns-with-callbacks/08-web-spider-v3-no-race/spider-cli.js +++ b/04-asynchronous-control-flow-patterns-with-callbacks/08-web-spider-v3-no-race/spider-cli.js @@ -1,13 +1,13 @@ -import { spider } from './spider.js' +import { spider } from "./spider.js"; -const url = process.argv[2] -const maxDepth = Number.parseInt(process.argv[3], 10) || 1 +const url = process.argv[2]; +const maxDepth = Number.parseInt(process.argv[3], 10) || 1; -spider(url, maxDepth, err => { +spider(url, maxDepth, (err) => { if (err) { - console.error(err) - process.exit(1) + console.error(err); + process.exit(1); } - console.log('Downloaded complete') -}) + console.log("Downloaded complete"); +}); diff --git a/04-asynchronous-control-flow-patterns-with-callbacks/08-web-spider-v3-no-race/spider.js b/04-asynchronous-control-flow-patterns-with-callbacks/08-web-spider-v3-no-race/spider.js index 472ac34..519f4c7 100644 --- a/04-asynchronous-control-flow-patterns-with-callbacks/08-web-spider-v3-no-race/spider.js +++ b/04-asynchronous-control-flow-patterns-with-callbacks/08-web-spider-v3-no-race/spider.js @@ -1,109 +1,109 @@ -import { readFile, writeFile } from 'node:fs' -import { dirname } from 'node:path' +import { readFile, writeFile } from "node:fs"; +import { dirname } from "node:path"; import { exists, get, getPageLinks, recursiveMkdir, urlToFilename, -} from './utils.js' +} from "./utils.js"; function saveFile(filename, content, cb) { - recursiveMkdir(dirname(filename), err => { + recursiveMkdir(dirname(filename), (err) => { if (err) { - return cb(err) + return cb(err); } - writeFile(filename, content, cb) - }) + writeFile(filename, content, cb); + }); } function download(url, filename, cb) { - console.log(`Downloading ${url} into ${filename}`) + console.log(`Downloading ${url} into ${filename}`); get(url, (err, content) => { if (err) { - return cb(err) + return cb(err); } - saveFile(filename, content, err => { + saveFile(filename, content, (err) => { if (err) { - return cb(err) + return cb(err); } - cb(null, content) - }) - }) + cb(null, content); + }); + }); } function spiderLinks(currentUrl, body, maxDepth, cb) { if (maxDepth === 0) { // Remember Zalgo from Chapter 3? - return process.nextTick(cb) + return process.nextTick(cb); } - const links = getPageLinks(currentUrl, body) + const links = getPageLinks(currentUrl, body); if (links.length === 0) { - return process.nextTick(cb) + return process.nextTick(cb); } - let completed = 0 - let hasErrors = false + let completed = 0; + let hasErrors = false; function done(err) { if (err) { - hasErrors = true - return cb(err) + hasErrors = true; + return cb(err); } if (++completed === links.length && !hasErrors) { - return cb() + return cb(); } } for (const link of links) { - spider(link, maxDepth - 1, done) + spider(link, maxDepth - 1, done); } } -const spidering = new Set() +const spidering = new Set(); export function spider(url, maxDepth, cb) { if (spidering.has(url)) { - return process.nextTick(cb) + return process.nextTick(cb); } - spidering.add(url) + spidering.add(url); - const filename = urlToFilename(url) + const filename = urlToFilename(url); exists(filename, (err, alreadyExists) => { if (err) { // error checking the file - return cb(err) + return cb(err); } if (alreadyExists) { - if (!filename.endsWith('.html')) { + if (!filename.endsWith(".html")) { // ignoring non-HTML resources - return cb() + return cb(); } - readFile(filename, 'utf8', (err, fileContent) => { + readFile(filename, "utf8", (err, fileContent) => { if (err) { // error reading the file - return cb(err) + return cb(err); } - return spiderLinks(url, fileContent, maxDepth, cb) - }) + return spiderLinks(url, fileContent, maxDepth, cb); + }); } else { // The file does not exist, download it download(url, filename, (err, fileContent) => { if (err) { // error downloading the file - return cb(err) + return cb(err); } // if the file is an HTML file, spider it - if (filename.endsWith('.html')) { - return spiderLinks(url, fileContent.toString('utf8'), maxDepth, cb) + if (filename.endsWith(".html")) { + return spiderLinks(url, fileContent.toString("utf8"), maxDepth, cb); } // otherwise, stop here - return cb() - }) + return cb(); + }); } - }) + }); } diff --git a/04-asynchronous-control-flow-patterns-with-callbacks/08-web-spider-v3-no-race/utils.js b/04-asynchronous-control-flow-patterns-with-callbacks/08-web-spider-v3-no-race/utils.js index be35ad1..9bd2157 100644 --- a/04-asynchronous-control-flow-patterns-with-callbacks/08-web-spider-v3-no-race/utils.js +++ b/04-asynchronous-control-flow-patterns-with-callbacks/08-web-spider-v3-no-race/utils.js @@ -1,40 +1,41 @@ -import { constants, access } from 'node:fs' -import { extname, join } from 'node:path' -import { Parser } from 'htmlparser2' -import { mkdirp } from 'mkdirp' -import slug from 'slug' +import { access, constants } from "node:fs"; +import { extname, join } from "node:path"; +import { Parser } from "htmlparser2"; +import { mkdirp } from "mkdirp"; +import slug from "slug"; export function exists(filePath, cb) { - access(filePath, constants.F_OK, err => { + access(filePath, constants.F_OK, (err) => { if (err) { - if (err.code === 'ENOENT') { + if (err.code === "ENOENT") { // the file does not exist - return cb(null, false) + return cb(null, false); } // unexpected error checking the file - return cb(err) + return cb(err); } // the file exists - return cb(null, true) - }) + return cb(null, true); + }); } export function urlToFilename(url) { - const parsedUrl = new URL(url) - const urlComponents = parsedUrl.pathname.split('/') - const originalFileName = urlComponents.pop() + const parsedUrl = new URL(url); + const urlComponents = parsedUrl.pathname.split("/"); + const originalFileName = urlComponents.pop(); const urlPath = urlComponents - .filter(component => component !== '') - .map(component => slug(component, { remove: null })) - .join('/') - const basePath = join(parsedUrl.hostname, urlPath) - const missingExtension = !originalFileName || extname(originalFileName) === '' + .filter((component) => component !== "") + .map((component) => slug(component, { remove: null })) + .join("/"); + const basePath = join(parsedUrl.hostname, urlPath); + const missingExtension = + !originalFileName || extname(originalFileName) === ""; if (missingExtension) { - return join(basePath, originalFileName, 'index.html') + return join(basePath, originalFileName, "index.html"); } - return join(basePath, originalFileName) + return join(basePath, originalFileName); } // NOTE: this function is just for illustrative purposes. We are wrapping @@ -42,17 +43,17 @@ export function urlToFilename(url) { // to demonstrate callback based patterns export function get(url, cb) { fetch(url) - .then(response => { + .then((response) => { if (!response.ok) { - throw new Error(`Failed to fetch ${url}: ${response.statusText}`) + throw new Error(`Failed to fetch ${url}: ${response.statusText}`); } // NOTE: this loads all the content in memory and therefore is not suitable // to handle large payloads. // For large payloads, we would need to use a stream-based approach - return response.arrayBuffer() + return response.arrayBuffer(); }) - .then(content => cb(null, Buffer.from(content))) - .catch(err => cb(err)) + .then((content) => cb(null, Buffer.from(content))) + .catch((err) => cb(err)); } // NOTE: this function is just for illustrative purposes. We are wrapping @@ -61,26 +62,26 @@ export function get(url, cb) { export function recursiveMkdir(path, cb) { mkdirp(path) .then(() => cb(null)) - .catch(e => cb(e)) + .catch((e) => cb(e)); } export function getPageLinks(currentUrl, body) { - const url = new URL(currentUrl) - const internalLinks = [] + const url = new URL(currentUrl); + const internalLinks = []; const parser = new Parser({ onopentag(name, attribs) { - if (name === 'a' && attribs.href) { - const newUrl = new URL(attribs.href, url) + if (name === "a" && attribs.href) { + const newUrl = new URL(attribs.href, url); if ( newUrl.hostname === url.hostname && newUrl.pathname !== url.pathname ) { - internalLinks.push(newUrl.toString()) + internalLinks.push(newUrl.toString()); } } }, - }) - parser.end(body) + }); + parser.end(body); - return internalLinks + return internalLinks; } diff --git a/04-asynchronous-control-flow-patterns-with-callbacks/09-callback-limited-concurrent-execution/index.js b/04-asynchronous-control-flow-patterns-with-callbacks/09-callback-limited-concurrent-execution/index.js index 9111462..5ce6c93 100644 --- a/04-asynchronous-control-flow-patterns-with-callbacks/09-callback-limited-concurrent-execution/index.js +++ b/04-asynchronous-control-flow-patterns-with-callbacks/09-callback-limited-concurrent-execution/index.js @@ -1,44 +1,44 @@ function makeSampleTask(name) { - return cb => { - console.log(`${name} started`) + return (cb) => { + console.log(`${name} started`); setTimeout(() => { - console.log(`${name} completed`) - cb() - }, Math.random() * 2000) - } + console.log(`${name} completed`); + cb(); + }, Math.random() * 2000); + }; } const tasks = [ - makeSampleTask('Task 1'), - makeSampleTask('Task 2'), - makeSampleTask('Task 3'), - makeSampleTask('Task 4'), - makeSampleTask('Task 5'), - makeSampleTask('Task 6'), - makeSampleTask('Task 7'), -] + makeSampleTask("Task 1"), + makeSampleTask("Task 2"), + makeSampleTask("Task 3"), + makeSampleTask("Task 4"), + makeSampleTask("Task 5"), + makeSampleTask("Task 6"), + makeSampleTask("Task 7"), +]; -const concurrency = 2 -let running = 0 -let completed = 0 -let nextTaskIndex = 0 +const concurrency = 2; +let running = 0; +let completed = 0; +let nextTaskIndex = 0; function next() { while (running < concurrency && nextTaskIndex < tasks.length) { - const task = tasks[nextTaskIndex++] + const task = tasks[nextTaskIndex++]; task(() => { if (++completed === tasks.length) { - return finish() + return finish(); } - running-- - next() - }) - running++ + running--; + next(); + }); + running++; } } -next() +next(); function finish() { // all the tasks completed - console.log('All tasks executed!') + console.log("All tasks executed!"); } diff --git a/04-asynchronous-control-flow-patterns-with-callbacks/10-queue-limited-concurrent-execution/TaskQueue.js b/04-asynchronous-control-flow-patterns-with-callbacks/10-queue-limited-concurrent-execution/TaskQueue.js index 8b7425c..86ede95 100644 --- a/04-asynchronous-control-flow-patterns-with-callbacks/10-queue-limited-concurrent-execution/TaskQueue.js +++ b/04-asynchronous-control-flow-patterns-with-callbacks/10-queue-limited-concurrent-execution/TaskQueue.js @@ -1,24 +1,24 @@ export class TaskQueue { constructor(concurrency) { - this.concurrency = concurrency - this.running = 0 - this.queue = [] + this.concurrency = concurrency; + this.running = 0; + this.queue = []; } pushTask(task) { - this.queue.push(task) - process.nextTick(this.next.bind(this)) - return this + this.queue.push(task); + process.nextTick(this.next.bind(this)); + return this; } next() { while (this.running < this.concurrency && this.queue.length > 0) { - const task = this.queue.shift() + const task = this.queue.shift(); task(() => { - this.running-- - process.nextTick(this.next.bind(this)) - }) - this.running++ + this.running--; + process.nextTick(this.next.bind(this)); + }); + this.running++; } } @@ -26,6 +26,6 @@ export class TaskQueue { return { running: this.running, scheduled: this.queue.length, - } + }; } } diff --git a/04-asynchronous-control-flow-patterns-with-callbacks/10-queue-limited-concurrent-execution/index.js b/04-asynchronous-control-flow-patterns-with-callbacks/10-queue-limited-concurrent-execution/index.js index afc0c30..7a98ec9 100644 --- a/04-asynchronous-control-flow-patterns-with-callbacks/10-queue-limited-concurrent-execution/index.js +++ b/04-asynchronous-control-flow-patterns-with-callbacks/10-queue-limited-concurrent-execution/index.js @@ -1,40 +1,40 @@ -import { TaskQueue } from './TaskQueue.js' +import { TaskQueue } from "./TaskQueue.js"; -const queue = new TaskQueue(2) +const queue = new TaskQueue(2); function makeSampleTask(name) { - return cb => { - console.log(`${name} started`, queue.stats()) + return (cb) => { + console.log(`${name} started`, queue.stats()); setTimeout(() => { - console.log(`${name} completed`, queue.stats()) - cb() - }, Math.random() * 2000) - } + console.log(`${name} completed`, queue.stats()); + cb(); + }, Math.random() * 2000); + }; } // A task that spawns other 2 sub tasks function task1(cb) { - console.log('Task 1 started', queue.stats()) + console.log("Task 1 started", queue.stats()); queue - .pushTask(makeSampleTask('task1 -> subtask 1')) - .pushTask(makeSampleTask('task1 -> subtask 2')) + .pushTask(makeSampleTask("task1 -> subtask 1")) + .pushTask(makeSampleTask("task1 -> subtask 2")); setTimeout(() => { - console.log('Task 1 completed', queue.stats()) - cb() - }, Math.random() * 2000) + console.log("Task 1 completed", queue.stats()); + cb(); + }, Math.random() * 2000); } // A task that spawns other 3 sub tasks function task2(cb) { - console.log('Task 2 started', queue.stats()) + console.log("Task 2 started", queue.stats()); queue - .pushTask(makeSampleTask('task2 -> subtask 1')) - .pushTask(makeSampleTask('task2 -> subtask 2')) - .pushTask(makeSampleTask('task2 -> subtask 3')) + .pushTask(makeSampleTask("task2 -> subtask 1")) + .pushTask(makeSampleTask("task2 -> subtask 2")) + .pushTask(makeSampleTask("task2 -> subtask 3")); setTimeout(() => { - console.log('Task 2 completed', queue.stats()) - cb() - }, Math.random() * 2000) + console.log("Task 2 completed", queue.stats()); + cb(); + }, Math.random() * 2000); } -queue.pushTask(task1).pushTask(task2) +queue.pushTask(task1).pushTask(task2); diff --git a/04-asynchronous-control-flow-patterns-with-callbacks/11-queue-limited-concurret-execution-with-events/TaskQueue.js b/04-asynchronous-control-flow-patterns-with-callbacks/11-queue-limited-concurret-execution-with-events/TaskQueue.js index 1c780b5..f010e65 100644 --- a/04-asynchronous-control-flow-patterns-with-callbacks/11-queue-limited-concurret-execution-with-events/TaskQueue.js +++ b/04-asynchronous-control-flow-patterns-with-callbacks/11-queue-limited-concurret-execution-with-events/TaskQueue.js @@ -1,34 +1,34 @@ -import { EventEmitter } from 'node:events' +import { EventEmitter } from "node:events"; export class TaskQueue extends EventEmitter { constructor(concurrency) { - super() - this.concurrency = concurrency - this.running = 0 - this.queue = [] + super(); + this.concurrency = concurrency; + this.running = 0; + this.queue = []; } pushTask(task) { - this.queue.push(task) - process.nextTick(this.next.bind(this)) - return this + this.queue.push(task); + process.nextTick(this.next.bind(this)); + return this; } next() { if (this.running === 0 && this.queue.length === 0) { - return this.emit('empty') + return this.emit("empty"); } while (this.running < this.concurrency && this.queue.length > 0) { - const task = this.queue.shift() - task(err => { + const task = this.queue.shift(); + task((err) => { if (err) { - this.emit('error', err) + this.emit("error", err); } - this.running-- - process.nextTick(this.next.bind(this)) - }) - this.running++ + this.running--; + process.nextTick(this.next.bind(this)); + }); + this.running++; } } @@ -36,6 +36,6 @@ export class TaskQueue extends EventEmitter { return { running: this.running, scheduled: this.queue.length, - } + }; } } diff --git a/04-asynchronous-control-flow-patterns-with-callbacks/11-queue-limited-concurret-execution-with-events/index.js b/04-asynchronous-control-flow-patterns-with-callbacks/11-queue-limited-concurret-execution-with-events/index.js index 2adf091..027d6c9 100644 --- a/04-asynchronous-control-flow-patterns-with-callbacks/11-queue-limited-concurret-execution-with-events/index.js +++ b/04-asynchronous-control-flow-patterns-with-callbacks/11-queue-limited-concurret-execution-with-events/index.js @@ -1,44 +1,44 @@ -import { TaskQueue } from './TaskQueue.js' +import { TaskQueue } from "./TaskQueue.js"; -const queue = new TaskQueue(2) -queue.on('error', console.error) -queue.on('empty', () => console.log('Queue drained')) +const queue = new TaskQueue(2); +queue.on("error", console.error); +queue.on("empty", () => console.log("Queue drained")); function makeSampleTask(name) { - return cb => { - console.log(`${name} started`, queue.stats()) + return (cb) => { + console.log(`${name} started`, queue.stats()); setTimeout(() => { - console.log(`${name} completed`, queue.stats()) - const failed = Math.random() > 0.75 - const err = failed ? new Error(`Task ${name} failed randomly!`) : null - cb(err) - }, Math.random() * 2000) - } + console.log(`${name} completed`, queue.stats()); + const failed = Math.random() > 0.75; + const err = failed ? new Error(`Task ${name} failed randomly!`) : null; + cb(err); + }, Math.random() * 2000); + }; } // A task that spawns other 2 sub tasks function task1(cb) { - console.log('Task 1 started', queue.stats()) + console.log("Task 1 started", queue.stats()); queue - .pushTask(makeSampleTask('task1 -> subtask 1')) - .pushTask(makeSampleTask('task1 -> subtask 2')) + .pushTask(makeSampleTask("task1 -> subtask 1")) + .pushTask(makeSampleTask("task1 -> subtask 2")); setTimeout(() => { - console.log('Task 1 completed', queue.stats()) - cb() - }, Math.random() * 2000) + console.log("Task 1 completed", queue.stats()); + cb(); + }, Math.random() * 2000); } // A task that spawns other 3 sub tasks function task2(cb) { - console.log('Task 2 started', queue.stats()) + console.log("Task 2 started", queue.stats()); queue - .pushTask(makeSampleTask('task2 -> subtask 1')) - .pushTask(makeSampleTask('task2 -> subtask 2')) - .pushTask(makeSampleTask('task2 -> subtask 3')) + .pushTask(makeSampleTask("task2 -> subtask 1")) + .pushTask(makeSampleTask("task2 -> subtask 2")) + .pushTask(makeSampleTask("task2 -> subtask 3")); setTimeout(() => { - console.log('Task 2 completed', queue.stats()) - cb() - }, Math.random() * 2000) + console.log("Task 2 completed", queue.stats()); + cb(); + }, Math.random() * 2000); } -queue.pushTask(task1).pushTask(task2) +queue.pushTask(task1).pushTask(task2); diff --git a/04-asynchronous-control-flow-patterns-with-callbacks/12-web-spider-v4/TaskQueue.js b/04-asynchronous-control-flow-patterns-with-callbacks/12-web-spider-v4/TaskQueue.js index 1c780b5..f010e65 100644 --- a/04-asynchronous-control-flow-patterns-with-callbacks/12-web-spider-v4/TaskQueue.js +++ b/04-asynchronous-control-flow-patterns-with-callbacks/12-web-spider-v4/TaskQueue.js @@ -1,34 +1,34 @@ -import { EventEmitter } from 'node:events' +import { EventEmitter } from "node:events"; export class TaskQueue extends EventEmitter { constructor(concurrency) { - super() - this.concurrency = concurrency - this.running = 0 - this.queue = [] + super(); + this.concurrency = concurrency; + this.running = 0; + this.queue = []; } pushTask(task) { - this.queue.push(task) - process.nextTick(this.next.bind(this)) - return this + this.queue.push(task); + process.nextTick(this.next.bind(this)); + return this; } next() { if (this.running === 0 && this.queue.length === 0) { - return this.emit('empty') + return this.emit("empty"); } while (this.running < this.concurrency && this.queue.length > 0) { - const task = this.queue.shift() - task(err => { + const task = this.queue.shift(); + task((err) => { if (err) { - this.emit('error', err) + this.emit("error", err); } - this.running-- - process.nextTick(this.next.bind(this)) - }) - this.running++ + this.running--; + process.nextTick(this.next.bind(this)); + }); + this.running++; } } @@ -36,6 +36,6 @@ export class TaskQueue extends EventEmitter { return { running: this.running, scheduled: this.queue.length, - } + }; } } diff --git a/04-asynchronous-control-flow-patterns-with-callbacks/12-web-spider-v4/spider-cli.js b/04-asynchronous-control-flow-patterns-with-callbacks/12-web-spider-v4/spider-cli.js index da59a92..c4c2355 100644 --- a/04-asynchronous-control-flow-patterns-with-callbacks/12-web-spider-v4/spider-cli.js +++ b/04-asynchronous-control-flow-patterns-with-callbacks/12-web-spider-v4/spider-cli.js @@ -1,12 +1,12 @@ -import { TaskQueue } from './TaskQueue.js' -import { spider } from './spider.js' +import { spider } from "./spider.js"; +import { TaskQueue } from "./TaskQueue.js"; -const url = process.argv[2] -const maxDepth = Number.parseInt(process.argv[3], 10) || 1 -const concurrency = Number.parseInt(process.argv[4], 10) || 2 +const url = process.argv[2]; +const maxDepth = Number.parseInt(process.argv[3], 10) || 1; +const concurrency = Number.parseInt(process.argv[4], 10) || 2; -const spiderQueue = new TaskQueue(concurrency) -spiderQueue.on('error', console.error) -spiderQueue.on('empty', () => console.log('Download complete')) +const spiderQueue = new TaskQueue(concurrency); +spiderQueue.on("error", console.error); +spiderQueue.on("empty", () => console.log("Download complete")); -spider(url, maxDepth, spiderQueue) +spider(url, maxDepth, spiderQueue); diff --git a/04-asynchronous-control-flow-patterns-with-callbacks/12-web-spider-v4/spider.js b/04-asynchronous-control-flow-patterns-with-callbacks/12-web-spider-v4/spider.js index c96145c..77b2be2 100644 --- a/04-asynchronous-control-flow-patterns-with-callbacks/12-web-spider-v4/spider.js +++ b/04-asynchronous-control-flow-patterns-with-callbacks/12-web-spider-v4/spider.js @@ -1,102 +1,102 @@ -import { readFile, writeFile } from 'node:fs' -import { dirname } from 'node:path' +import { readFile, writeFile } from "node:fs"; +import { dirname } from "node:path"; import { exists, get, getPageLinks, recursiveMkdir, urlToFilename, -} from './utils.js' +} from "./utils.js"; function saveFile(filename, content, cb) { - recursiveMkdir(dirname(filename), err => { + recursiveMkdir(dirname(filename), (err) => { if (err) { - return cb(err) + return cb(err); } - writeFile(filename, content, cb) - }) + writeFile(filename, content, cb); + }); } function download(url, filename, cb) { - console.log(`Downloading ${url} into ${filename}`) + console.log(`Downloading ${url} into ${filename}`); get(url, (err, content) => { if (err) { - return cb(err) + return cb(err); } - saveFile(filename, content, err => { + saveFile(filename, content, (err) => { if (err) { - return cb(err) + return cb(err); } - cb(null, content) - }) - }) + cb(null, content); + }); + }); } function spiderLinks(currentUrl, body, maxDepth, queue) { if (maxDepth === 0) { - return + return; } - const links = getPageLinks(currentUrl, body) + const links = getPageLinks(currentUrl, body); if (links.length === 0) { - return + return; } for (const link of links) { - spider(link, maxDepth - 1, queue) + spider(link, maxDepth - 1, queue); } } function spiderTask(url, maxDepth, queue, cb) { - const filename = urlToFilename(url) + const filename = urlToFilename(url); exists(filename, (err, alreadyExists) => { if (err) { // error checking the file - return cb(err) + return cb(err); } if (alreadyExists) { - if (!filename.endsWith('.html')) { + if (!filename.endsWith(".html")) { // ignoring non-HTML resources - return cb() + return cb(); } - return readFile(filename, 'utf8', (err, fileContent) => { + return readFile(filename, "utf8", (err, fileContent) => { if (err) { // error reading the file - return cb(err) + return cb(err); } - spiderLinks(url, fileContent, maxDepth, queue) - return cb() - }) + spiderLinks(url, fileContent, maxDepth, queue); + return cb(); + }); } // The file does not exist, download it download(url, filename, (err, fileContent) => { if (err) { // error downloading the file - return cb(err) + return cb(err); } // if the file is an HTML file, spider it - if (filename.endsWith('.html')) { - spiderLinks(url, fileContent.toString('utf8'), maxDepth, queue) - return cb() + if (filename.endsWith(".html")) { + spiderLinks(url, fileContent.toString("utf8"), maxDepth, queue); + return cb(); } // otherwise, stop here - return cb() - }) - }) + return cb(); + }); + }); } -const spidering = new Set() +const spidering = new Set(); export function spider(url, maxDepth, queue) { if (spidering.has(url)) { - return + return; } - spidering.add(url) + spidering.add(url); - queue.pushTask(done => { - spiderTask(url, maxDepth, queue, done) - }) + queue.pushTask((done) => { + spiderTask(url, maxDepth, queue, done); + }); } diff --git a/04-asynchronous-control-flow-patterns-with-callbacks/12-web-spider-v4/utils.js b/04-asynchronous-control-flow-patterns-with-callbacks/12-web-spider-v4/utils.js index be35ad1..9bd2157 100644 --- a/04-asynchronous-control-flow-patterns-with-callbacks/12-web-spider-v4/utils.js +++ b/04-asynchronous-control-flow-patterns-with-callbacks/12-web-spider-v4/utils.js @@ -1,40 +1,41 @@ -import { constants, access } from 'node:fs' -import { extname, join } from 'node:path' -import { Parser } from 'htmlparser2' -import { mkdirp } from 'mkdirp' -import slug from 'slug' +import { access, constants } from "node:fs"; +import { extname, join } from "node:path"; +import { Parser } from "htmlparser2"; +import { mkdirp } from "mkdirp"; +import slug from "slug"; export function exists(filePath, cb) { - access(filePath, constants.F_OK, err => { + access(filePath, constants.F_OK, (err) => { if (err) { - if (err.code === 'ENOENT') { + if (err.code === "ENOENT") { // the file does not exist - return cb(null, false) + return cb(null, false); } // unexpected error checking the file - return cb(err) + return cb(err); } // the file exists - return cb(null, true) - }) + return cb(null, true); + }); } export function urlToFilename(url) { - const parsedUrl = new URL(url) - const urlComponents = parsedUrl.pathname.split('/') - const originalFileName = urlComponents.pop() + const parsedUrl = new URL(url); + const urlComponents = parsedUrl.pathname.split("/"); + const originalFileName = urlComponents.pop(); const urlPath = urlComponents - .filter(component => component !== '') - .map(component => slug(component, { remove: null })) - .join('/') - const basePath = join(parsedUrl.hostname, urlPath) - const missingExtension = !originalFileName || extname(originalFileName) === '' + .filter((component) => component !== "") + .map((component) => slug(component, { remove: null })) + .join("/"); + const basePath = join(parsedUrl.hostname, urlPath); + const missingExtension = + !originalFileName || extname(originalFileName) === ""; if (missingExtension) { - return join(basePath, originalFileName, 'index.html') + return join(basePath, originalFileName, "index.html"); } - return join(basePath, originalFileName) + return join(basePath, originalFileName); } // NOTE: this function is just for illustrative purposes. We are wrapping @@ -42,17 +43,17 @@ export function urlToFilename(url) { // to demonstrate callback based patterns export function get(url, cb) { fetch(url) - .then(response => { + .then((response) => { if (!response.ok) { - throw new Error(`Failed to fetch ${url}: ${response.statusText}`) + throw new Error(`Failed to fetch ${url}: ${response.statusText}`); } // NOTE: this loads all the content in memory and therefore is not suitable // to handle large payloads. // For large payloads, we would need to use a stream-based approach - return response.arrayBuffer() + return response.arrayBuffer(); }) - .then(content => cb(null, Buffer.from(content))) - .catch(err => cb(err)) + .then((content) => cb(null, Buffer.from(content))) + .catch((err) => cb(err)); } // NOTE: this function is just for illustrative purposes. We are wrapping @@ -61,26 +62,26 @@ export function get(url, cb) { export function recursiveMkdir(path, cb) { mkdirp(path) .then(() => cb(null)) - .catch(e => cb(e)) + .catch((e) => cb(e)); } export function getPageLinks(currentUrl, body) { - const url = new URL(currentUrl) - const internalLinks = [] + const url = new URL(currentUrl); + const internalLinks = []; const parser = new Parser({ onopentag(name, attribs) { - if (name === 'a' && attribs.href) { - const newUrl = new URL(attribs.href, url) + if (name === "a" && attribs.href) { + const newUrl = new URL(attribs.href, url); if ( newUrl.hostname === url.hostname && newUrl.pathname !== url.pathname ) { - internalLinks.push(newUrl.toString()) + internalLinks.push(newUrl.toString()); } } }, - }) - parser.end(body) + }); + parser.end(body); - return internalLinks + return internalLinks; } diff --git a/05-asynchronous-control-flow-patterns-with-promises-and-async-await/01-delay-with-promises/index.js b/05-asynchronous-control-flow-patterns-with-promises-and-async-await/01-delay-with-promises/index.js index 922d743..58d33fd 100644 --- a/05-asynchronous-control-flow-patterns-with-promises-and-async-await/01-delay-with-promises/index.js +++ b/05-asynchronous-control-flow-patterns-with-promises-and-async-await/01-delay-with-promises/index.js @@ -1,13 +1,13 @@ function delay(milliseconds) { return new Promise((resolve, _reject) => { setTimeout(() => { - resolve(Date.now()) - }, milliseconds) - }) + resolve(Date.now()); + }, milliseconds); + }); } -console.log(`Delaying... (${Date.now()})`) +console.log(`Delaying... (${Date.now()})`); -delay(1000).then(newDate => { - console.log(`Done (${newDate})`) -}) +delay(1000).then((newDate) => { + console.log(`Done (${newDate})`); +}); diff --git a/05-asynchronous-control-flow-patterns-with-promises-and-async-await/02-promisify/index.js b/05-asynchronous-control-flow-patterns-with-promises-and-async-await/02-promisify/index.js index fede4ac..253b94b 100644 --- a/05-asynchronous-control-flow-patterns-with-promises-and-async-await/02-promisify/index.js +++ b/05-asynchronous-control-flow-patterns-with-promises-and-async-await/02-promisify/index.js @@ -1,4 +1,4 @@ -import { randomBytes } from 'node:crypto' +import { randomBytes } from "node:crypto"; function promisify(callbackBasedFn) { return function promisifiedFn(...args) { @@ -7,18 +7,18 @@ function promisify(callbackBasedFn) { ...args, (err, result) => { if (err) { - return reject(err) + return reject(err); } - resolve(result) + resolve(result); }, - ] - callbackBasedFn(...newArgs) - }) - } + ]; + callbackBasedFn(...newArgs); + }); + }; } -const randomBytesP = promisify(randomBytes) -randomBytesP(32).then(buffer => { - console.log(`Random bytes: ${buffer.toString()}`) -}) +const randomBytesP = promisify(randomBytes); +randomBytesP(32).then((buffer) => { + console.log(`Random bytes: ${buffer.toString()}`); +}); diff --git a/05-asynchronous-control-flow-patterns-with-promises-and-async-await/03-promises-web-spider-v2/spider-cli.js b/05-asynchronous-control-flow-patterns-with-promises-and-async-await/03-promises-web-spider-v2/spider-cli.js index 5ffc35a..9633a21 100644 --- a/05-asynchronous-control-flow-patterns-with-promises-and-async-await/03-promises-web-spider-v2/spider-cli.js +++ b/05-asynchronous-control-flow-patterns-with-promises-and-async-await/03-promises-web-spider-v2/spider-cli.js @@ -1,11 +1,11 @@ -import { spider } from './spider.js' +import { spider } from "./spider.js"; -const url = process.argv[2] -const maxDepth = Number.parseInt(process.argv[3], 10) || 1 +const url = process.argv[2]; +const maxDepth = Number.parseInt(process.argv[3], 10) || 1; spider(url, maxDepth) - .then(() => console.log('Downloaded complete')) - .catch(err => { - console.error(err) - process.exit(1) - }) + .then(() => console.log("Downloaded complete")) + .catch((err) => { + console.error(err); + process.exit(1); + }); diff --git a/05-asynchronous-control-flow-patterns-with-promises-and-async-await/03-promises-web-spider-v2/spider.js b/05-asynchronous-control-flow-patterns-with-promises-and-async-await/03-promises-web-spider-v2/spider.js index ce980ce..0c5c0f3 100644 --- a/05-asynchronous-control-flow-patterns-with-promises-and-async-await/03-promises-web-spider-v2/spider.js +++ b/05-asynchronous-control-flow-patterns-with-promises-and-async-await/03-promises-web-spider-v2/spider.js @@ -1,61 +1,61 @@ -import { readFile, writeFile } from 'node:fs/promises' -import { dirname } from 'node:path' +import { readFile, writeFile } from "node:fs/promises"; +import { dirname } from "node:path"; import { exists, get, getPageLinks, recursiveMkdir, urlToFilename, -} from './utils.js' +} from "./utils.js"; function saveFile(filename, content) { return recursiveMkdir(dirname(filename)) .then(() => writeFile(filename, content)) - .then(() => content) + .then(() => content); } function download(url, filename) { - console.log(`Downloading ${url} into ${filename}`) - return get(url).then(content => saveFile(filename, content)) + console.log(`Downloading ${url} into ${filename}`); + return get(url).then((content) => saveFile(filename, content)); } function spiderLinks(currentUrl, body, maxDepth) { - let promise = Promise.resolve() + let promise = Promise.resolve(); if (maxDepth === 0) { - return promise + return promise; } - const links = getPageLinks(currentUrl, body) + const links = getPageLinks(currentUrl, body); for (const link of links) { - promise = promise.then(() => spider(link, maxDepth - 1)) + promise = promise.then(() => spider(link, maxDepth - 1)); } - return promise + return promise; } export function spider(url, maxDepth) { - const filename = urlToFilename(url) + const filename = urlToFilename(url); - return exists(filename).then(alreadyExists => { + return exists(filename).then((alreadyExists) => { if (alreadyExists) { - if (!filename.endsWith('.html')) { + if (!filename.endsWith(".html")) { // ignoring non-HTML resources - return + return; } - return readFile(filename, 'utf8').then(fileContent => - spiderLinks(url, fileContent, maxDepth) - ) + return readFile(filename, "utf8").then((fileContent) => + spiderLinks(url, fileContent, maxDepth), + ); } // if file does not exist, download it - return download(url, filename).then(fileContent => { + return download(url, filename).then((fileContent) => { // if the file is an HTML file, spider it - if (filename.endsWith('.html')) { - return spiderLinks(url, fileContent.toString('utf8'), maxDepth) + if (filename.endsWith(".html")) { + return spiderLinks(url, fileContent.toString("utf8"), maxDepth); } // otherwise, stop here - return - }) - }) + return; + }); + }); } diff --git a/05-asynchronous-control-flow-patterns-with-promises-and-async-await/03-promises-web-spider-v2/utils.js b/05-asynchronous-control-flow-patterns-with-promises-and-async-await/03-promises-web-spider-v2/utils.js index c15c194..c9b4d01 100644 --- a/05-asynchronous-control-flow-patterns-with-promises-and-async-await/03-promises-web-spider-v2/utils.js +++ b/05-asynchronous-control-flow-patterns-with-promises-and-async-await/03-promises-web-spider-v2/utils.js @@ -1,37 +1,38 @@ -import { constants } from 'node:fs' -import { access } from 'node:fs/promises' -import { extname, join } from 'node:path' -import { Parser } from 'htmlparser2' -import { mkdirp } from 'mkdirp' -import slug from 'slug' +import { constants } from "node:fs"; +import { access } from "node:fs/promises"; +import { extname, join } from "node:path"; +import { Parser } from "htmlparser2"; +import { mkdirp } from "mkdirp"; +import slug from "slug"; export function exists(filePath) { return access(filePath, constants.F_OK) .then(() => true) - .catch(err => { - if (err.code === 'ENOENT') { - return false + .catch((err) => { + if (err.code === "ENOENT") { + return false; } - throw err - }) + throw err; + }); } export function urlToFilename(url) { - const parsedUrl = new URL(url) - const urlComponents = parsedUrl.pathname.split('/') - const originalFileName = urlComponents.pop() + const parsedUrl = new URL(url); + const urlComponents = parsedUrl.pathname.split("/"); + const originalFileName = urlComponents.pop(); const urlPath = urlComponents - .filter(component => component !== '') - .map(component => slug(component, { remove: null })) - .join('/') - const basePath = join(parsedUrl.hostname, urlPath) - const missingExtension = !originalFileName || extname(originalFileName) === '' + .filter((component) => component !== "") + .map((component) => slug(component, { remove: null })) + .join("/"); + const basePath = join(parsedUrl.hostname, urlPath); + const missingExtension = + !originalFileName || extname(originalFileName) === ""; if (missingExtension) { - return join(basePath, originalFileName, 'index.html') + return join(basePath, originalFileName, "index.html"); } - return join(basePath, originalFileName) + return join(basePath, originalFileName); } // NOTE: this function is just for illustrative purposes. We are wrapping @@ -39,40 +40,40 @@ export function urlToFilename(url) { // to demonstrate some promise based patterns export function get(url) { return fetch(url) - .then(response => { + .then((response) => { if (!response.ok) { - throw new Error(`Failed to fetch ${url}: ${response.statusText}`) + throw new Error(`Failed to fetch ${url}: ${response.statusText}`); } // NOTE: this loads all the content in memory and therefore is not suitable // to handle large payloads. // For large payloads, we would need to use a stream-based approach - return response.arrayBuffer() + return response.arrayBuffer(); }) - .then(content => Buffer.from(content)) + .then((content) => Buffer.from(content)); } // NOTE: this function is just for illustrative purposes. We are aliasing // `mkdirp` just to keep the same naming conventions as the ones we had in the callback-based // version of this example -export const recursiveMkdir = mkdirp +export const recursiveMkdir = mkdirp; export function getPageLinks(currentUrl, body) { - const url = new URL(currentUrl) - const internalLinks = [] + const url = new URL(currentUrl); + const internalLinks = []; const parser = new Parser({ onopentag(name, attribs) { - if (name === 'a' && attribs.href) { - const newUrl = new URL(attribs.href, url) + if (name === "a" && attribs.href) { + const newUrl = new URL(attribs.href, url); if ( newUrl.hostname === url.hostname && newUrl.pathname !== url.pathname ) { - internalLinks.push(newUrl.toString()) + internalLinks.push(newUrl.toString()); } } }, - }) - parser.end(body) + }); + parser.end(body); - return internalLinks + return internalLinks; } diff --git a/05-asynchronous-control-flow-patterns-with-promises-and-async-await/04-promises-web-spider-v3/spider-cli.js b/05-asynchronous-control-flow-patterns-with-promises-and-async-await/04-promises-web-spider-v3/spider-cli.js index 5ffc35a..9633a21 100644 --- a/05-asynchronous-control-flow-patterns-with-promises-and-async-await/04-promises-web-spider-v3/spider-cli.js +++ b/05-asynchronous-control-flow-patterns-with-promises-and-async-await/04-promises-web-spider-v3/spider-cli.js @@ -1,11 +1,11 @@ -import { spider } from './spider.js' +import { spider } from "./spider.js"; -const url = process.argv[2] -const maxDepth = Number.parseInt(process.argv[3], 10) || 1 +const url = process.argv[2]; +const maxDepth = Number.parseInt(process.argv[3], 10) || 1; spider(url, maxDepth) - .then(() => console.log('Downloaded complete')) - .catch(err => { - console.error(err) - process.exit(1) - }) + .then(() => console.log("Downloaded complete")) + .catch((err) => { + console.error(err); + process.exit(1); + }); diff --git a/05-asynchronous-control-flow-patterns-with-promises-and-async-await/04-promises-web-spider-v3/spider.js b/05-asynchronous-control-flow-patterns-with-promises-and-async-await/04-promises-web-spider-v3/spider.js index f0788a7..d82a81b 100644 --- a/05-asynchronous-control-flow-patterns-with-promises-and-async-await/04-promises-web-spider-v3/spider.js +++ b/05-asynchronous-control-flow-patterns-with-promises-and-async-await/04-promises-web-spider-v3/spider.js @@ -1,58 +1,58 @@ -import { readFile, writeFile } from 'node:fs/promises' -import { dirname } from 'node:path' +import { readFile, writeFile } from "node:fs/promises"; +import { dirname } from "node:path"; import { exists, get, getPageLinks, recursiveMkdir, urlToFilename, -} from './utils.js' +} from "./utils.js"; function saveFile(filename, content) { return recursiveMkdir(dirname(filename)) .then(() => writeFile(filename, content)) - .then(() => content) + .then(() => content); } function download(url, filename) { - console.log(`Downloading ${url} into ${filename}`) - return get(url).then(content => saveFile(filename, content)) + console.log(`Downloading ${url} into ${filename}`); + return get(url).then((content) => saveFile(filename, content)); } function spiderLinks(currentUrl, body, maxDepth) { if (maxDepth === 0) { - return Promise.resolve() + return Promise.resolve(); } - const links = getPageLinks(currentUrl, body) - const promises = links.map(link => spider(link, maxDepth - 1)) + const links = getPageLinks(currentUrl, body); + const promises = links.map((link) => spider(link, maxDepth - 1)); - return Promise.all(promises) + return Promise.all(promises); } export function spider(url, maxDepth) { - const filename = urlToFilename(url) + const filename = urlToFilename(url); - return exists(filename).then(alreadyExists => { + return exists(filename).then((alreadyExists) => { if (alreadyExists) { - if (!filename.endsWith('.html')) { + if (!filename.endsWith(".html")) { // ignoring non-HTML resources - return + return; } - return readFile(filename, 'utf8').then(fileContent => - spiderLinks(url, fileContent, maxDepth) - ) + return readFile(filename, "utf8").then((fileContent) => + spiderLinks(url, fileContent, maxDepth), + ); } // if file does not exist, download it - return download(url, filename).then(fileContent => { + return download(url, filename).then((fileContent) => { // if the file is an HTML file, spider it - if (filename.endsWith('.html')) { - return spiderLinks(url, fileContent.toString('utf8'), maxDepth) + if (filename.endsWith(".html")) { + return spiderLinks(url, fileContent.toString("utf8"), maxDepth); } // otherwise, stop here - return - }) - }) + return; + }); + }); } diff --git a/05-asynchronous-control-flow-patterns-with-promises-and-async-await/04-promises-web-spider-v3/utils.js b/05-asynchronous-control-flow-patterns-with-promises-and-async-await/04-promises-web-spider-v3/utils.js index c15c194..c9b4d01 100644 --- a/05-asynchronous-control-flow-patterns-with-promises-and-async-await/04-promises-web-spider-v3/utils.js +++ b/05-asynchronous-control-flow-patterns-with-promises-and-async-await/04-promises-web-spider-v3/utils.js @@ -1,37 +1,38 @@ -import { constants } from 'node:fs' -import { access } from 'node:fs/promises' -import { extname, join } from 'node:path' -import { Parser } from 'htmlparser2' -import { mkdirp } from 'mkdirp' -import slug from 'slug' +import { constants } from "node:fs"; +import { access } from "node:fs/promises"; +import { extname, join } from "node:path"; +import { Parser } from "htmlparser2"; +import { mkdirp } from "mkdirp"; +import slug from "slug"; export function exists(filePath) { return access(filePath, constants.F_OK) .then(() => true) - .catch(err => { - if (err.code === 'ENOENT') { - return false + .catch((err) => { + if (err.code === "ENOENT") { + return false; } - throw err - }) + throw err; + }); } export function urlToFilename(url) { - const parsedUrl = new URL(url) - const urlComponents = parsedUrl.pathname.split('/') - const originalFileName = urlComponents.pop() + const parsedUrl = new URL(url); + const urlComponents = parsedUrl.pathname.split("/"); + const originalFileName = urlComponents.pop(); const urlPath = urlComponents - .filter(component => component !== '') - .map(component => slug(component, { remove: null })) - .join('/') - const basePath = join(parsedUrl.hostname, urlPath) - const missingExtension = !originalFileName || extname(originalFileName) === '' + .filter((component) => component !== "") + .map((component) => slug(component, { remove: null })) + .join("/"); + const basePath = join(parsedUrl.hostname, urlPath); + const missingExtension = + !originalFileName || extname(originalFileName) === ""; if (missingExtension) { - return join(basePath, originalFileName, 'index.html') + return join(basePath, originalFileName, "index.html"); } - return join(basePath, originalFileName) + return join(basePath, originalFileName); } // NOTE: this function is just for illustrative purposes. We are wrapping @@ -39,40 +40,40 @@ export function urlToFilename(url) { // to demonstrate some promise based patterns export function get(url) { return fetch(url) - .then(response => { + .then((response) => { if (!response.ok) { - throw new Error(`Failed to fetch ${url}: ${response.statusText}`) + throw new Error(`Failed to fetch ${url}: ${response.statusText}`); } // NOTE: this loads all the content in memory and therefore is not suitable // to handle large payloads. // For large payloads, we would need to use a stream-based approach - return response.arrayBuffer() + return response.arrayBuffer(); }) - .then(content => Buffer.from(content)) + .then((content) => Buffer.from(content)); } // NOTE: this function is just for illustrative purposes. We are aliasing // `mkdirp` just to keep the same naming conventions as the ones we had in the callback-based // version of this example -export const recursiveMkdir = mkdirp +export const recursiveMkdir = mkdirp; export function getPageLinks(currentUrl, body) { - const url = new URL(currentUrl) - const internalLinks = [] + const url = new URL(currentUrl); + const internalLinks = []; const parser = new Parser({ onopentag(name, attribs) { - if (name === 'a' && attribs.href) { - const newUrl = new URL(attribs.href, url) + if (name === "a" && attribs.href) { + const newUrl = new URL(attribs.href, url); if ( newUrl.hostname === url.hostname && newUrl.pathname !== url.pathname ) { - internalLinks.push(newUrl.toString()) + internalLinks.push(newUrl.toString()); } } }, - }) - parser.end(body) + }); + parser.end(body); - return internalLinks + return internalLinks; } diff --git a/05-asynchronous-control-flow-patterns-with-promises-and-async-await/05-promises-web-spider-v4/TaskQueue.js b/05-asynchronous-control-flow-patterns-with-promises-and-async-await/05-promises-web-spider-v4/TaskQueue.js index b45fcf8..93374e8 100644 --- a/05-asynchronous-control-flow-patterns-with-promises-and-async-await/05-promises-web-spider-v4/TaskQueue.js +++ b/05-asynchronous-control-flow-patterns-with-promises-and-async-await/05-promises-web-spider-v4/TaskQueue.js @@ -1,35 +1,35 @@ -import { EventEmitter } from 'node:events' +import { EventEmitter } from "node:events"; export class TaskQueue extends EventEmitter { constructor(concurrency) { - super() - this.concurrency = concurrency - this.running = 0 - this.queue = [] + super(); + this.concurrency = concurrency; + this.running = 0; + this.queue = []; } pushTask(task) { - this.queue.push(task) - process.nextTick(this.next.bind(this)) - return this + this.queue.push(task); + process.nextTick(this.next.bind(this)); + return this; } next() { if (this.running === 0 && this.queue.length === 0) { - return this.emit('empty') + return this.emit("empty"); } while (this.running < this.concurrency && this.queue.length > 0) { - const task = this.queue.shift() + const task = this.queue.shift(); task() - .catch(err => { - this.emit('error', err) + .catch((err) => { + this.emit("error", err); }) .finally(() => { - this.running-- - this.next() - }) - this.running++ + this.running--; + this.next(); + }); + this.running++; } } @@ -37,6 +37,6 @@ export class TaskQueue extends EventEmitter { return { running: this.running, scheduled: this.queue.length, - } + }; } } diff --git a/05-asynchronous-control-flow-patterns-with-promises-and-async-await/05-promises-web-spider-v4/spider-cli.js b/05-asynchronous-control-flow-patterns-with-promises-and-async-await/05-promises-web-spider-v4/spider-cli.js index 3dcfe96..2846d10 100644 --- a/05-asynchronous-control-flow-patterns-with-promises-and-async-await/05-promises-web-spider-v4/spider-cli.js +++ b/05-asynchronous-control-flow-patterns-with-promises-and-async-await/05-promises-web-spider-v4/spider-cli.js @@ -1,13 +1,13 @@ -import { TaskQueue } from './TaskQueue.js' -import { spider } from './spider.js' +import { spider } from "./spider.js"; +import { TaskQueue } from "./TaskQueue.js"; -const url = process.argv[2] -const maxDepth = Number.parseInt(process.argv[3], 10) || 1 -const concurrency = Number.parseInt(process.argv[4], 10) || 2 +const url = process.argv[2]; +const maxDepth = Number.parseInt(process.argv[3], 10) || 1; +const concurrency = Number.parseInt(process.argv[4], 10) || 2; -const queue = new TaskQueue(concurrency) -queue.pushTask(() => spider(url, maxDepth, queue)) -queue.on('error', console.error) -queue.on('empty', () => { - console.log('Download complete') -}) +const queue = new TaskQueue(concurrency); +queue.pushTask(() => spider(url, maxDepth, queue)); +queue.on("error", console.error); +queue.on("empty", () => { + console.log("Download complete"); +}); diff --git a/05-asynchronous-control-flow-patterns-with-promises-and-async-await/05-promises-web-spider-v4/spider.js b/05-asynchronous-control-flow-patterns-with-promises-and-async-await/05-promises-web-spider-v4/spider.js index 25581fc..3b3f254 100644 --- a/05-asynchronous-control-flow-patterns-with-promises-and-async-await/05-promises-web-spider-v4/spider.js +++ b/05-asynchronous-control-flow-patterns-with-promises-and-async-await/05-promises-web-spider-v4/spider.js @@ -1,63 +1,63 @@ -import { readFile, writeFile } from 'node:fs/promises' -import { dirname } from 'node:path' +import { readFile, writeFile } from "node:fs/promises"; +import { dirname } from "node:path"; import { exists, get, getPageLinks, recursiveMkdir, urlToFilename, -} from './utils.js' +} from "./utils.js"; -const spidering = new Set() +const spidering = new Set(); function saveFile(filename, content) { return recursiveMkdir(dirname(filename)) .then(() => writeFile(filename, content)) - .then(() => content) + .then(() => content); } function download(url, filename) { - console.log(`Downloading ${url} into ${filename}`) - return get(url).then(content => saveFile(filename, content)) + console.log(`Downloading ${url} into ${filename}`); + return get(url).then((content) => saveFile(filename, content)); } function spiderLinks(currentUrl, body, maxDepth, queue) { if (maxDepth === 0) { - return + return; } - const links = getPageLinks(currentUrl, body) + const links = getPageLinks(currentUrl, body); for (const link of links) { if (!spidering.has(link)) { - queue.pushTask(() => spider(link, maxDepth - 1, queue)) - spidering.add(link) + queue.pushTask(() => spider(link, maxDepth - 1, queue)); + spidering.add(link); } } } export function spider(url, maxDepth, queue) { - const filename = urlToFilename(url) + const filename = urlToFilename(url); - return exists(filename).then(alreadyExists => { + return exists(filename).then((alreadyExists) => { if (alreadyExists) { - if (!filename.endsWith('.html')) { + if (!filename.endsWith(".html")) { // ignoring non-HTML resources - return + return; } - return readFile(filename, 'utf8').then(fileContent => - spiderLinks(url, fileContent, maxDepth, queue) - ) + return readFile(filename, "utf8").then((fileContent) => + spiderLinks(url, fileContent, maxDepth, queue), + ); } // if file does not exist, download it - return download(url, filename).then(fileContent => { + return download(url, filename).then((fileContent) => { // if the file is an HTML file, spider it - if (filename.endsWith('.html')) { - return spiderLinks(url, fileContent.toString('utf8'), maxDepth, queue) + if (filename.endsWith(".html")) { + return spiderLinks(url, fileContent.toString("utf8"), maxDepth, queue); } // otherwise, stop here - return - }) - }) + return; + }); + }); } diff --git a/05-asynchronous-control-flow-patterns-with-promises-and-async-await/05-promises-web-spider-v4/utils.js b/05-asynchronous-control-flow-patterns-with-promises-and-async-await/05-promises-web-spider-v4/utils.js index c15c194..c9b4d01 100644 --- a/05-asynchronous-control-flow-patterns-with-promises-and-async-await/05-promises-web-spider-v4/utils.js +++ b/05-asynchronous-control-flow-patterns-with-promises-and-async-await/05-promises-web-spider-v4/utils.js @@ -1,37 +1,38 @@ -import { constants } from 'node:fs' -import { access } from 'node:fs/promises' -import { extname, join } from 'node:path' -import { Parser } from 'htmlparser2' -import { mkdirp } from 'mkdirp' -import slug from 'slug' +import { constants } from "node:fs"; +import { access } from "node:fs/promises"; +import { extname, join } from "node:path"; +import { Parser } from "htmlparser2"; +import { mkdirp } from "mkdirp"; +import slug from "slug"; export function exists(filePath) { return access(filePath, constants.F_OK) .then(() => true) - .catch(err => { - if (err.code === 'ENOENT') { - return false + .catch((err) => { + if (err.code === "ENOENT") { + return false; } - throw err - }) + throw err; + }); } export function urlToFilename(url) { - const parsedUrl = new URL(url) - const urlComponents = parsedUrl.pathname.split('/') - const originalFileName = urlComponents.pop() + const parsedUrl = new URL(url); + const urlComponents = parsedUrl.pathname.split("/"); + const originalFileName = urlComponents.pop(); const urlPath = urlComponents - .filter(component => component !== '') - .map(component => slug(component, { remove: null })) - .join('/') - const basePath = join(parsedUrl.hostname, urlPath) - const missingExtension = !originalFileName || extname(originalFileName) === '' + .filter((component) => component !== "") + .map((component) => slug(component, { remove: null })) + .join("/"); + const basePath = join(parsedUrl.hostname, urlPath); + const missingExtension = + !originalFileName || extname(originalFileName) === ""; if (missingExtension) { - return join(basePath, originalFileName, 'index.html') + return join(basePath, originalFileName, "index.html"); } - return join(basePath, originalFileName) + return join(basePath, originalFileName); } // NOTE: this function is just for illustrative purposes. We are wrapping @@ -39,40 +40,40 @@ export function urlToFilename(url) { // to demonstrate some promise based patterns export function get(url) { return fetch(url) - .then(response => { + .then((response) => { if (!response.ok) { - throw new Error(`Failed to fetch ${url}: ${response.statusText}`) + throw new Error(`Failed to fetch ${url}: ${response.statusText}`); } // NOTE: this loads all the content in memory and therefore is not suitable // to handle large payloads. // For large payloads, we would need to use a stream-based approach - return response.arrayBuffer() + return response.arrayBuffer(); }) - .then(content => Buffer.from(content)) + .then((content) => Buffer.from(content)); } // NOTE: this function is just for illustrative purposes. We are aliasing // `mkdirp` just to keep the same naming conventions as the ones we had in the callback-based // version of this example -export const recursiveMkdir = mkdirp +export const recursiveMkdir = mkdirp; export function getPageLinks(currentUrl, body) { - const url = new URL(currentUrl) - const internalLinks = [] + const url = new URL(currentUrl); + const internalLinks = []; const parser = new Parser({ onopentag(name, attribs) { - if (name === 'a' && attribs.href) { - const newUrl = new URL(attribs.href, url) + if (name === "a" && attribs.href) { + const newUrl = new URL(attribs.href, url); if ( newUrl.hostname === url.hostname && newUrl.pathname !== url.pathname ) { - internalLinks.push(newUrl.toString()) + internalLinks.push(newUrl.toString()); } } }, - }) - parser.end(body) + }); + parser.end(body); - return internalLinks + return internalLinks; } diff --git a/05-asynchronous-control-flow-patterns-with-promises-and-async-await/06-lazy-promise/LazyPromise.js b/05-asynchronous-control-flow-patterns-with-promises-and-async-await/06-lazy-promise/LazyPromise.js index 043a13e..0a4176a 100644 --- a/05-asynchronous-control-flow-patterns-with-promises-and-async-await/06-lazy-promise/LazyPromise.js +++ b/05-asynchronous-control-flow-patterns-with-promises-and-async-await/06-lazy-promise/LazyPromise.js @@ -1,45 +1,45 @@ export class LazyPromise extends Promise { - #resolve - #reject - #executor - #promise + #resolve; + #reject; + #executor; + #promise; constructor(executor) { - let _resolve - let _reject + let _resolve; + let _reject; super((resolve, reject) => { - _resolve = resolve - _reject = reject - }) - this.#resolve = _resolve - this.#reject = _reject - this.#executor = executor - this.#promise = null + _resolve = resolve; + _reject = reject; + }); + this.#resolve = _resolve; + this.#reject = _reject; + this.#executor = executor; + this.#promise = null; } #ensureInit() { if (!this.#promise) { - this.#promise = new Promise(this.#executor) + this.#promise = new Promise(this.#executor); this.#promise.then( - v => this.#resolve(v), - e => this.#reject(e) - ) + (v) => this.#resolve(v), + (e) => this.#reject(e), + ); } } // biome-ignore lint/suspicious/noThenProperty: re-implementing the Promise interface then(onFulfilled, onRejected) { - this.#ensureInit() - return this.#promise.then(onFulfilled, onRejected) + this.#ensureInit(); + return this.#promise.then(onFulfilled, onRejected); } catch(onRejected) { - this.#ensureInit() - return this.#promise.catch(onRejected) + this.#ensureInit(); + return this.#promise.catch(onRejected); } finally(onFinally) { - this.#ensureInit() - return this.#promise.finally(onFinally) + this.#ensureInit(); + return this.#promise.finally(onFinally); } } diff --git a/05-asynchronous-control-flow-patterns-with-promises-and-async-await/06-lazy-promise/index.js b/05-asynchronous-control-flow-patterns-with-promises-and-async-await/06-lazy-promise/index.js index 007b850..1ac3e7e 100644 --- a/05-asynchronous-control-flow-patterns-with-promises-and-async-await/06-lazy-promise/index.js +++ b/05-asynchronous-control-flow-patterns-with-promises-and-async-await/06-lazy-promise/index.js @@ -1,16 +1,16 @@ -import { LazyPromise } from './LazyPromise.js' +import { LazyPromise } from "./LazyPromise.js"; -const lazyPromise = new LazyPromise(resolve => { - console.log('Executor Started!') +const lazyPromise = new LazyPromise((resolve) => { + console.log("Executor Started!"); // simulate some async work to be done setTimeout(() => { - resolve('Completed!') - }, 1000) -}) + resolve("Completed!"); + }, 1000); +}); -console.log('Lazy Promise instance created!') -console.log(lazyPromise) -lazyPromise.then(value => { - console.log(value) - console.log(lazyPromise) -}) +console.log("Lazy Promise instance created!"); +console.log(lazyPromise); +lazyPromise.then((value) => { + console.log(value); + console.log(lazyPromise); +}); diff --git a/05-asynchronous-control-flow-patterns-with-promises-and-async-await/07-async-await-delay/index.js b/05-asynchronous-control-flow-patterns-with-promises-and-async-await/07-async-await-delay/index.js index 3d2a40b..35a83ba 100644 --- a/05-asynchronous-control-flow-patterns-with-promises-and-async-await/07-async-await-delay/index.js +++ b/05-asynchronous-control-flow-patterns-with-promises-and-async-await/07-async-await-delay/index.js @@ -1,23 +1,23 @@ function delay(milliseconds) { return new Promise((resolve, _reject) => { setTimeout(() => { - resolve(Date.now()) - }, milliseconds) - }) + resolve(Date.now()); + }, milliseconds); + }); } async function playingWithDelays() { - console.log('Delaying...', Date.now()) + console.log("Delaying...", Date.now()); - const timeAfterOneSecond = await delay(1000) - console.log(timeAfterOneSecond) + const timeAfterOneSecond = await delay(1000); + console.log(timeAfterOneSecond); - const timeAfterThreeSeconds = await delay(3000) - console.log(timeAfterThreeSeconds) + const timeAfterThreeSeconds = await delay(3000); + console.log(timeAfterThreeSeconds); - return 'done' + return "done"; } -playingWithDelays().then(result => { - console.log(`After 4 seconds: ${result}`) -}) +playingWithDelays().then((result) => { + console.log(`After 4 seconds: ${result}`); +}); diff --git a/05-asynchronous-control-flow-patterns-with-promises-and-async-await/08-async-await-top-level-await/index.js b/05-asynchronous-control-flow-patterns-with-promises-and-async-await/08-async-await-top-level-await/index.js index dddba21..46d235b 100644 --- a/05-asynchronous-control-flow-patterns-with-promises-and-async-await/08-async-await-top-level-await/index.js +++ b/05-asynchronous-control-flow-patterns-with-promises-and-async-await/08-async-await-top-level-await/index.js @@ -1,21 +1,21 @@ function delay(milliseconds) { return new Promise((resolve, _reject) => { setTimeout(() => { - resolve(Date.now()) - }, milliseconds) - }) + resolve(Date.now()); + }, milliseconds); + }); } async function playingWithDelays() { - console.log('Delaying...', Date.now()) + console.log("Delaying...", Date.now()); - const timeAfterOneSecond = await delay(1000) - console.log(timeAfterOneSecond) + const timeAfterOneSecond = await delay(1000); + console.log(timeAfterOneSecond); - const timeAfterThreeSeconds = await delay(3000) - console.log(timeAfterThreeSeconds) + const timeAfterThreeSeconds = await delay(3000); + console.log(timeAfterThreeSeconds); - return 'done' + return "done"; } // without top-level await, we need to wrap the code in an async IIFE @@ -24,5 +24,5 @@ async function playingWithDelays() { // console.log(`After 4 seconds: ${result}`); // })(); -const result = await playingWithDelays() -console.log(`After 4 seconds: ${result}`) +const result = await playingWithDelays(); +console.log(`After 4 seconds: ${result}`); diff --git a/05-asynchronous-control-flow-patterns-with-promises-and-async-await/09-async-await-errors/index.js b/05-asynchronous-control-flow-patterns-with-promises-and-async-await/09-async-await-errors/index.js index b5d596a..4de6fa2 100644 --- a/05-asynchronous-control-flow-patterns-with-promises-and-async-await/09-async-await-errors/index.js +++ b/05-asynchronous-control-flow-patterns-with-promises-and-async-await/09-async-await-errors/index.js @@ -1,25 +1,25 @@ function delayError(milliseconds) { return new Promise((_resolve, reject) => { setTimeout(() => { - reject(new Error(`Error after ${milliseconds}ms`)) - }, milliseconds) - }) + reject(new Error(`Error after ${milliseconds}ms`)); + }, milliseconds); + }); } async function playingWithErrors(throwSyncError) { try { if (throwSyncError) { - throw new Error('This is a synchronous error') + throw new Error("This is a synchronous error"); } - await delayError(1000) + await delayError(1000); } catch (err) { - console.error(`We have an error: ${err.message}`) + console.error(`We have an error: ${err.message}`); } finally { - console.log('Done') + console.log("Done"); } } // throws a synchronous error -playingWithErrors(true) +playingWithErrors(true); // awaited Promise will reject -playingWithErrors(false) +playingWithErrors(false); diff --git a/05-asynchronous-control-flow-patterns-with-promises-and-async-await/10-async-await-return-await-trap/errorCaught.js b/05-asynchronous-control-flow-patterns-with-promises-and-async-await/10-async-await-return-await-trap/errorCaught.js index 7b716fc..ec6be5b 100644 --- a/05-asynchronous-control-flow-patterns-with-promises-and-async-await/10-async-await-return-await-trap/errorCaught.js +++ b/05-asynchronous-control-flow-patterns-with-promises-and-async-await/10-async-await-return-await-trap/errorCaught.js @@ -1,19 +1,19 @@ function delayError(milliseconds) { return new Promise((_resolve, reject) => { setTimeout(() => { - reject(new Error(`Error after ${milliseconds}ms`)) - }, milliseconds) - }) + reject(new Error(`Error after ${milliseconds}ms`)); + }, milliseconds); + }); } async function errorCaught() { try { - return await delayError(1000) + return await delayError(1000); } catch (err) { - console.error(`Error caught by the async function: ${err.message}`) + console.error(`Error caught by the async function: ${err.message}`); } } -errorCaught().catch(err => - console.error(`Error caught by the caller: ${err.message}`) -) +errorCaught().catch((err) => + console.error(`Error caught by the caller: ${err.message}`), +); diff --git a/05-asynchronous-control-flow-patterns-with-promises-and-async-await/10-async-await-return-await-trap/errorNotCaught.js b/05-asynchronous-control-flow-patterns-with-promises-and-async-await/10-async-await-return-await-trap/errorNotCaught.js index 075371a..8b3818e 100644 --- a/05-asynchronous-control-flow-patterns-with-promises-and-async-await/10-async-await-return-await-trap/errorNotCaught.js +++ b/05-asynchronous-control-flow-patterns-with-promises-and-async-await/10-async-await-return-await-trap/errorNotCaught.js @@ -1,20 +1,20 @@ function delayError(milliseconds) { return new Promise((_resolve, reject) => { setTimeout(() => { - reject(new Error(`Error after ${milliseconds}ms`)) - }, milliseconds) - }) + reject(new Error(`Error after ${milliseconds}ms`)); + }, milliseconds); + }); } // biome-ignore lint/suspicious/useAwait: We are showing the trap of not using await async function errorNotCaught() { try { - return delayError(1000) + return delayError(1000); } catch (err) { - console.error(`Error caught by the async function: ${err.message}`) + console.error(`Error caught by the async function: ${err.message}`); } } -errorNotCaught().catch(err => - console.error(`Error caught by the caller: ${err.message}`) -) +errorNotCaught().catch((err) => + console.error(`Error caught by the caller: ${err.message}`), +); diff --git a/05-asynchronous-control-flow-patterns-with-promises-and-async-await/11-async-await-web-spider-v2/spider-cli.js b/05-asynchronous-control-flow-patterns-with-promises-and-async-await/11-async-await-web-spider-v2/spider-cli.js index 643baa1..9f7ed9c 100644 --- a/05-asynchronous-control-flow-patterns-with-promises-and-async-await/11-async-await-web-spider-v2/spider-cli.js +++ b/05-asynchronous-control-flow-patterns-with-promises-and-async-await/11-async-await-web-spider-v2/spider-cli.js @@ -1,12 +1,12 @@ -import { spider } from './spider.js' +import { spider } from "./spider.js"; -const url = process.argv[2] -const maxDepth = Number.parseInt(process.argv[3], 10) || 1 +const url = process.argv[2]; +const maxDepth = Number.parseInt(process.argv[3], 10) || 1; try { - await spider(url, maxDepth) - console.log('Downloaded complete') + await spider(url, maxDepth); + console.log("Downloaded complete"); } catch (err) { - console.error(err) - process.exit(1) + console.error(err); + process.exit(1); } diff --git a/05-asynchronous-control-flow-patterns-with-promises-and-async-await/11-async-await-web-spider-v2/spider.js b/05-asynchronous-control-flow-patterns-with-promises-and-async-await/11-async-await-web-spider-v2/spider.js index 97ec837..3767c11 100644 --- a/05-asynchronous-control-flow-patterns-with-promises-and-async-await/11-async-await-web-spider-v2/spider.js +++ b/05-asynchronous-control-flow-patterns-with-promises-and-async-await/11-async-await-web-spider-v2/spider.js @@ -1,55 +1,55 @@ -import { readFile, writeFile } from 'node:fs/promises' -import { dirname } from 'node:path' +import { readFile, writeFile } from "node:fs/promises"; +import { dirname } from "node:path"; import { exists, get, getPageLinks, recursiveMkdir, urlToFilename, -} from './utils.js' +} from "./utils.js"; async function saveFile(filename, content) { - await recursiveMkdir(dirname(filename)) - return writeFile(filename, content) + await recursiveMkdir(dirname(filename)); + return writeFile(filename, content); } async function download(url, filename) { - console.log(`Downloading ${url} into ${filename}`) - const content = await get(url) - await saveFile(filename, content) - return content + console.log(`Downloading ${url} into ${filename}`); + const content = await get(url); + await saveFile(filename, content); + return content; } async function spiderLinks(currentUrl, body, maxDepth) { if (maxDepth === 0) { - return + return; } - const links = getPageLinks(currentUrl, body) + const links = getPageLinks(currentUrl, body); for (const link of links) { - await spider(link, maxDepth - 1) + await spider(link, maxDepth - 1); } } export async function spider(url, maxDepth) { - const filename = urlToFilename(url) + const filename = urlToFilename(url); - let content + let content; if (!(await exists(filename))) { // if the file does not exist, download it - content = await download(url, filename) + content = await download(url, filename); } // if the file is not an HTML file, stop here - if (!filename.endsWith('.html')) { - return + if (!filename.endsWith(".html")) { + return; } // if file content is not already loaded, load it from disk if (!content) { - content = await readFile(filename) + content = await readFile(filename); } // spider the links in the file - return spiderLinks(url, content.toString('utf8'), maxDepth) + return spiderLinks(url, content.toString("utf8"), maxDepth); } diff --git a/05-asynchronous-control-flow-patterns-with-promises-and-async-await/11-async-await-web-spider-v2/utils.js b/05-asynchronous-control-flow-patterns-with-promises-and-async-await/11-async-await-web-spider-v2/utils.js index c15c194..c9b4d01 100644 --- a/05-asynchronous-control-flow-patterns-with-promises-and-async-await/11-async-await-web-spider-v2/utils.js +++ b/05-asynchronous-control-flow-patterns-with-promises-and-async-await/11-async-await-web-spider-v2/utils.js @@ -1,37 +1,38 @@ -import { constants } from 'node:fs' -import { access } from 'node:fs/promises' -import { extname, join } from 'node:path' -import { Parser } from 'htmlparser2' -import { mkdirp } from 'mkdirp' -import slug from 'slug' +import { constants } from "node:fs"; +import { access } from "node:fs/promises"; +import { extname, join } from "node:path"; +import { Parser } from "htmlparser2"; +import { mkdirp } from "mkdirp"; +import slug from "slug"; export function exists(filePath) { return access(filePath, constants.F_OK) .then(() => true) - .catch(err => { - if (err.code === 'ENOENT') { - return false + .catch((err) => { + if (err.code === "ENOENT") { + return false; } - throw err - }) + throw err; + }); } export function urlToFilename(url) { - const parsedUrl = new URL(url) - const urlComponents = parsedUrl.pathname.split('/') - const originalFileName = urlComponents.pop() + const parsedUrl = new URL(url); + const urlComponents = parsedUrl.pathname.split("/"); + const originalFileName = urlComponents.pop(); const urlPath = urlComponents - .filter(component => component !== '') - .map(component => slug(component, { remove: null })) - .join('/') - const basePath = join(parsedUrl.hostname, urlPath) - const missingExtension = !originalFileName || extname(originalFileName) === '' + .filter((component) => component !== "") + .map((component) => slug(component, { remove: null })) + .join("/"); + const basePath = join(parsedUrl.hostname, urlPath); + const missingExtension = + !originalFileName || extname(originalFileName) === ""; if (missingExtension) { - return join(basePath, originalFileName, 'index.html') + return join(basePath, originalFileName, "index.html"); } - return join(basePath, originalFileName) + return join(basePath, originalFileName); } // NOTE: this function is just for illustrative purposes. We are wrapping @@ -39,40 +40,40 @@ export function urlToFilename(url) { // to demonstrate some promise based patterns export function get(url) { return fetch(url) - .then(response => { + .then((response) => { if (!response.ok) { - throw new Error(`Failed to fetch ${url}: ${response.statusText}`) + throw new Error(`Failed to fetch ${url}: ${response.statusText}`); } // NOTE: this loads all the content in memory and therefore is not suitable // to handle large payloads. // For large payloads, we would need to use a stream-based approach - return response.arrayBuffer() + return response.arrayBuffer(); }) - .then(content => Buffer.from(content)) + .then((content) => Buffer.from(content)); } // NOTE: this function is just for illustrative purposes. We are aliasing // `mkdirp` just to keep the same naming conventions as the ones we had in the callback-based // version of this example -export const recursiveMkdir = mkdirp +export const recursiveMkdir = mkdirp; export function getPageLinks(currentUrl, body) { - const url = new URL(currentUrl) - const internalLinks = [] + const url = new URL(currentUrl); + const internalLinks = []; const parser = new Parser({ onopentag(name, attribs) { - if (name === 'a' && attribs.href) { - const newUrl = new URL(attribs.href, url) + if (name === "a" && attribs.href) { + const newUrl = new URL(attribs.href, url); if ( newUrl.hostname === url.hostname && newUrl.pathname !== url.pathname ) { - internalLinks.push(newUrl.toString()) + internalLinks.push(newUrl.toString()); } } }, - }) - parser.end(body) + }); + parser.end(body); - return internalLinks + return internalLinks; } diff --git a/05-asynchronous-control-flow-patterns-with-promises-and-async-await/12-async-await-web-spider-v3/spider-cli.js b/05-asynchronous-control-flow-patterns-with-promises-and-async-await/12-async-await-web-spider-v3/spider-cli.js index 643baa1..9f7ed9c 100644 --- a/05-asynchronous-control-flow-patterns-with-promises-and-async-await/12-async-await-web-spider-v3/spider-cli.js +++ b/05-asynchronous-control-flow-patterns-with-promises-and-async-await/12-async-await-web-spider-v3/spider-cli.js @@ -1,12 +1,12 @@ -import { spider } from './spider.js' +import { spider } from "./spider.js"; -const url = process.argv[2] -const maxDepth = Number.parseInt(process.argv[3], 10) || 1 +const url = process.argv[2]; +const maxDepth = Number.parseInt(process.argv[3], 10) || 1; try { - await spider(url, maxDepth) - console.log('Downloaded complete') + await spider(url, maxDepth); + console.log("Downloaded complete"); } catch (err) { - console.error(err) - process.exit(1) + console.error(err); + process.exit(1); } diff --git a/05-asynchronous-control-flow-patterns-with-promises-and-async-await/12-async-await-web-spider-v3/spider.js b/05-asynchronous-control-flow-patterns-with-promises-and-async-await/12-async-await-web-spider-v3/spider.js index 80ac76f..eba1576 100644 --- a/05-asynchronous-control-flow-patterns-with-promises-and-async-await/12-async-await-web-spider-v3/spider.js +++ b/05-asynchronous-control-flow-patterns-with-promises-and-async-await/12-async-await-web-spider-v3/spider.js @@ -1,55 +1,55 @@ -import { readFile, writeFile } from 'node:fs/promises' -import { dirname } from 'node:path' +import { readFile, writeFile } from "node:fs/promises"; +import { dirname } from "node:path"; import { exists, get, getPageLinks, recursiveMkdir, urlToFilename, -} from './utils.js' +} from "./utils.js"; async function saveFile(filename, content) { - await recursiveMkdir(dirname(filename)) - return writeFile(filename, content) + await recursiveMkdir(dirname(filename)); + return writeFile(filename, content); } async function download(url, filename) { - console.log(`Downloading ${url} into ${filename}`) - const content = await get(url) - await saveFile(filename, content) - return content + console.log(`Downloading ${url} into ${filename}`); + const content = await get(url); + await saveFile(filename, content); + return content; } async function spiderLinks(currentUrl, body, maxDepth) { if (maxDepth === 0) { - return + return; } - const links = getPageLinks(currentUrl, body) - const promises = links.map(link => spider(link, maxDepth - 1)) + const links = getPageLinks(currentUrl, body); + const promises = links.map((link) => spider(link, maxDepth - 1)); - return Promise.all(promises) + return Promise.all(promises); } export async function spider(url, maxDepth) { - const filename = urlToFilename(url) + const filename = urlToFilename(url); - let content + let content; if (!(await exists(filename))) { // if the file does not exist, download it - content = await download(url, filename) + content = await download(url, filename); } // if the file is not an HTML file, stop here - if (!filename.endsWith('.html')) { - return + if (!filename.endsWith(".html")) { + return; } // if file content is not already loaded, load it from disk if (!content) { - content = await readFile(filename) + content = await readFile(filename); } // spider the links in the file - return spiderLinks(url, content.toString('utf8'), maxDepth) + return spiderLinks(url, content.toString("utf8"), maxDepth); } diff --git a/05-asynchronous-control-flow-patterns-with-promises-and-async-await/12-async-await-web-spider-v3/utils.js b/05-asynchronous-control-flow-patterns-with-promises-and-async-await/12-async-await-web-spider-v3/utils.js index c15c194..c9b4d01 100644 --- a/05-asynchronous-control-flow-patterns-with-promises-and-async-await/12-async-await-web-spider-v3/utils.js +++ b/05-asynchronous-control-flow-patterns-with-promises-and-async-await/12-async-await-web-spider-v3/utils.js @@ -1,37 +1,38 @@ -import { constants } from 'node:fs' -import { access } from 'node:fs/promises' -import { extname, join } from 'node:path' -import { Parser } from 'htmlparser2' -import { mkdirp } from 'mkdirp' -import slug from 'slug' +import { constants } from "node:fs"; +import { access } from "node:fs/promises"; +import { extname, join } from "node:path"; +import { Parser } from "htmlparser2"; +import { mkdirp } from "mkdirp"; +import slug from "slug"; export function exists(filePath) { return access(filePath, constants.F_OK) .then(() => true) - .catch(err => { - if (err.code === 'ENOENT') { - return false + .catch((err) => { + if (err.code === "ENOENT") { + return false; } - throw err - }) + throw err; + }); } export function urlToFilename(url) { - const parsedUrl = new URL(url) - const urlComponents = parsedUrl.pathname.split('/') - const originalFileName = urlComponents.pop() + const parsedUrl = new URL(url); + const urlComponents = parsedUrl.pathname.split("/"); + const originalFileName = urlComponents.pop(); const urlPath = urlComponents - .filter(component => component !== '') - .map(component => slug(component, { remove: null })) - .join('/') - const basePath = join(parsedUrl.hostname, urlPath) - const missingExtension = !originalFileName || extname(originalFileName) === '' + .filter((component) => component !== "") + .map((component) => slug(component, { remove: null })) + .join("/"); + const basePath = join(parsedUrl.hostname, urlPath); + const missingExtension = + !originalFileName || extname(originalFileName) === ""; if (missingExtension) { - return join(basePath, originalFileName, 'index.html') + return join(basePath, originalFileName, "index.html"); } - return join(basePath, originalFileName) + return join(basePath, originalFileName); } // NOTE: this function is just for illustrative purposes. We are wrapping @@ -39,40 +40,40 @@ export function urlToFilename(url) { // to demonstrate some promise based patterns export function get(url) { return fetch(url) - .then(response => { + .then((response) => { if (!response.ok) { - throw new Error(`Failed to fetch ${url}: ${response.statusText}`) + throw new Error(`Failed to fetch ${url}: ${response.statusText}`); } // NOTE: this loads all the content in memory and therefore is not suitable // to handle large payloads. // For large payloads, we would need to use a stream-based approach - return response.arrayBuffer() + return response.arrayBuffer(); }) - .then(content => Buffer.from(content)) + .then((content) => Buffer.from(content)); } // NOTE: this function is just for illustrative purposes. We are aliasing // `mkdirp` just to keep the same naming conventions as the ones we had in the callback-based // version of this example -export const recursiveMkdir = mkdirp +export const recursiveMkdir = mkdirp; export function getPageLinks(currentUrl, body) { - const url = new URL(currentUrl) - const internalLinks = [] + const url = new URL(currentUrl); + const internalLinks = []; const parser = new Parser({ onopentag(name, attribs) { - if (name === 'a' && attribs.href) { - const newUrl = new URL(attribs.href, url) + if (name === "a" && attribs.href) { + const newUrl = new URL(attribs.href, url); if ( newUrl.hostname === url.hostname && newUrl.pathname !== url.pathname ) { - internalLinks.push(newUrl.toString()) + internalLinks.push(newUrl.toString()); } } }, - }) - parser.end(body) + }); + parser.end(body); - return internalLinks + return internalLinks; } diff --git a/05-asynchronous-control-flow-patterns-with-promises-and-async-await/13-async-await-web-spider-v4/TaskQueue.js b/05-asynchronous-control-flow-patterns-with-promises-and-async-await/13-async-await-web-spider-v4/TaskQueue.js index c1bd52f..4395f37 100644 --- a/05-asynchronous-control-flow-patterns-with-promises-and-async-await/13-async-await-web-spider-v4/TaskQueue.js +++ b/05-asynchronous-control-flow-patterns-with-promises-and-async-await/13-async-await-web-spider-v4/TaskQueue.js @@ -1,35 +1,35 @@ -import { EventEmitter } from 'node:events' +import { EventEmitter } from "node:events"; export class TaskQueue extends EventEmitter { constructor(concurrency) { - super() - this.concurrency = concurrency - this.running = 0 - this.queue = [] + super(); + this.concurrency = concurrency; + this.running = 0; + this.queue = []; } pushTask(task) { - this.queue.push(task) - process.nextTick(this.next.bind(this)) - return this + this.queue.push(task); + process.nextTick(this.next.bind(this)); + return this; } next() { if (this.running === 0 && this.queue.length === 0) { - return this.emit('empty') + return this.emit("empty"); } while (this.running < this.concurrency && this.queue.length > 0) { - const task = this.queue.shift() + const task = this.queue.shift(); task() - .catch(err => { - this.emit('taskError', err) + .catch((err) => { + this.emit("taskError", err); }) .finally(() => { - this.running-- - this.next() - }) - this.running++ + this.running--; + this.next(); + }); + this.running++; } } @@ -37,6 +37,6 @@ export class TaskQueue extends EventEmitter { return { running: this.running, scheduled: this.queue.length, - } + }; } } diff --git a/05-asynchronous-control-flow-patterns-with-promises-and-async-await/13-async-await-web-spider-v4/spider-cli.js b/05-asynchronous-control-flow-patterns-with-promises-and-async-await/13-async-await-web-spider-v4/spider-cli.js index 4a16ded..b545252 100644 --- a/05-asynchronous-control-flow-patterns-with-promises-and-async-await/13-async-await-web-spider-v4/spider-cli.js +++ b/05-asynchronous-control-flow-patterns-with-promises-and-async-await/13-async-await-web-spider-v4/spider-cli.js @@ -1,14 +1,14 @@ -import { once } from 'node:events' -import { TaskQueue } from './TaskQueue.js' -import { spider } from './spider.js' +import { once } from "node:events"; +import { spider } from "./spider.js"; +import { TaskQueue } from "./TaskQueue.js"; -const url = process.argv[2] -const maxDepth = Number.parseInt(process.argv[3], 10) || 1 -const concurrency = Number.parseInt(process.argv[4], 10) || 2 +const url = process.argv[2]; +const maxDepth = Number.parseInt(process.argv[3], 10) || 1; +const concurrency = Number.parseInt(process.argv[4], 10) || 2; -const queue = new TaskQueue(concurrency) -queue.pushTask(() => spider(url, maxDepth, queue)) -queue.on('taskError', console.error) +const queue = new TaskQueue(concurrency); +queue.pushTask(() => spider(url, maxDepth, queue)); +queue.on("taskError", console.error); -await once(queue, 'empty') -console.log('Download complete') +await once(queue, "empty"); +console.log("Download complete"); diff --git a/05-asynchronous-control-flow-patterns-with-promises-and-async-await/13-async-await-web-spider-v4/spider.js b/05-asynchronous-control-flow-patterns-with-promises-and-async-await/13-async-await-web-spider-v4/spider.js index 0bb0ec7..ae2657b 100644 --- a/05-asynchronous-control-flow-patterns-with-promises-and-async-await/13-async-await-web-spider-v4/spider.js +++ b/05-asynchronous-control-flow-patterns-with-promises-and-async-await/13-async-await-web-spider-v4/spider.js @@ -1,60 +1,60 @@ -import { readFile, writeFile } from 'node:fs/promises' -import { dirname } from 'node:path' +import { readFile, writeFile } from "node:fs/promises"; +import { dirname } from "node:path"; import { exists, get, getPageLinks, recursiveMkdir, urlToFilename, -} from './utils.js' +} from "./utils.js"; -const spidering = new Set() +const spidering = new Set(); async function saveFile(filename, content) { - await recursiveMkdir(dirname(filename)) - return writeFile(filename, content) + await recursiveMkdir(dirname(filename)); + return writeFile(filename, content); } async function download(url, filename) { - console.log(`Downloading ${url} into ${filename}`) - const content = await get(url) - await saveFile(filename, content) - return content + console.log(`Downloading ${url} into ${filename}`); + const content = await get(url); + await saveFile(filename, content); + return content; } function spiderLinks(currentUrl, body, maxDepth, queue) { if (maxDepth === 0) { - return + return; } - const links = getPageLinks(currentUrl, body) + const links = getPageLinks(currentUrl, body); for (const link of links) { if (!spidering.has(link)) { - queue.pushTask(() => spider(link, maxDepth - 1, queue)) - spidering.add(link) + queue.pushTask(() => spider(link, maxDepth - 1, queue)); + spidering.add(link); } } } export async function spider(url, maxDepth, queue) { - const filename = urlToFilename(url) + const filename = urlToFilename(url); - let content + let content; if (!(await exists(filename))) { // if the file does not exist, download it - content = await download(url, filename) + content = await download(url, filename); } // if the file is not an HTML file, stop here - if (!filename.endsWith('.html')) { - return + if (!filename.endsWith(".html")) { + return; } // if file content is not already loaded, load it from disk if (!content) { - content = await readFile(filename) + content = await readFile(filename); } // spider the links in the file - return spiderLinks(url, content.toString('utf8'), maxDepth, queue) + return spiderLinks(url, content.toString("utf8"), maxDepth, queue); } diff --git a/05-asynchronous-control-flow-patterns-with-promises-and-async-await/13-async-await-web-spider-v4/utils.js b/05-asynchronous-control-flow-patterns-with-promises-and-async-await/13-async-await-web-spider-v4/utils.js index c15c194..c9b4d01 100644 --- a/05-asynchronous-control-flow-patterns-with-promises-and-async-await/13-async-await-web-spider-v4/utils.js +++ b/05-asynchronous-control-flow-patterns-with-promises-and-async-await/13-async-await-web-spider-v4/utils.js @@ -1,37 +1,38 @@ -import { constants } from 'node:fs' -import { access } from 'node:fs/promises' -import { extname, join } from 'node:path' -import { Parser } from 'htmlparser2' -import { mkdirp } from 'mkdirp' -import slug from 'slug' +import { constants } from "node:fs"; +import { access } from "node:fs/promises"; +import { extname, join } from "node:path"; +import { Parser } from "htmlparser2"; +import { mkdirp } from "mkdirp"; +import slug from "slug"; export function exists(filePath) { return access(filePath, constants.F_OK) .then(() => true) - .catch(err => { - if (err.code === 'ENOENT') { - return false + .catch((err) => { + if (err.code === "ENOENT") { + return false; } - throw err - }) + throw err; + }); } export function urlToFilename(url) { - const parsedUrl = new URL(url) - const urlComponents = parsedUrl.pathname.split('/') - const originalFileName = urlComponents.pop() + const parsedUrl = new URL(url); + const urlComponents = parsedUrl.pathname.split("/"); + const originalFileName = urlComponents.pop(); const urlPath = urlComponents - .filter(component => component !== '') - .map(component => slug(component, { remove: null })) - .join('/') - const basePath = join(parsedUrl.hostname, urlPath) - const missingExtension = !originalFileName || extname(originalFileName) === '' + .filter((component) => component !== "") + .map((component) => slug(component, { remove: null })) + .join("/"); + const basePath = join(parsedUrl.hostname, urlPath); + const missingExtension = + !originalFileName || extname(originalFileName) === ""; if (missingExtension) { - return join(basePath, originalFileName, 'index.html') + return join(basePath, originalFileName, "index.html"); } - return join(basePath, originalFileName) + return join(basePath, originalFileName); } // NOTE: this function is just for illustrative purposes. We are wrapping @@ -39,40 +40,40 @@ export function urlToFilename(url) { // to demonstrate some promise based patterns export function get(url) { return fetch(url) - .then(response => { + .then((response) => { if (!response.ok) { - throw new Error(`Failed to fetch ${url}: ${response.statusText}`) + throw new Error(`Failed to fetch ${url}: ${response.statusText}`); } // NOTE: this loads all the content in memory and therefore is not suitable // to handle large payloads. // For large payloads, we would need to use a stream-based approach - return response.arrayBuffer() + return response.arrayBuffer(); }) - .then(content => Buffer.from(content)) + .then((content) => Buffer.from(content)); } // NOTE: this function is just for illustrative purposes. We are aliasing // `mkdirp` just to keep the same naming conventions as the ones we had in the callback-based // version of this example -export const recursiveMkdir = mkdirp +export const recursiveMkdir = mkdirp; export function getPageLinks(currentUrl, body) { - const url = new URL(currentUrl) - const internalLinks = [] + const url = new URL(currentUrl); + const internalLinks = []; const parser = new Parser({ onopentag(name, attribs) { - if (name === 'a' && attribs.href) { - const newUrl = new URL(attribs.href, url) + if (name === "a" && attribs.href) { + const newUrl = new URL(attribs.href, url); if ( newUrl.hostname === url.hostname && newUrl.pathname !== url.pathname ) { - internalLinks.push(newUrl.toString()) + internalLinks.push(newUrl.toString()); } } }, - }) - parser.end(body) + }); + parser.end(body); - return internalLinks + return internalLinks; } diff --git a/05-asynchronous-control-flow-patterns-with-promises-and-async-await/14-promises-recursion-leak/index.js b/05-asynchronous-control-flow-patterns-with-promises-and-async-await/14-promises-recursion-leak/index.js index 938705a..ce4ada1 100644 --- a/05-asynchronous-control-flow-patterns-with-promises-and-async-await/14-promises-recursion-leak/index.js +++ b/05-asynchronous-control-flow-patterns-with-promises-and-async-await/14-promises-recursion-leak/index.js @@ -1,59 +1,59 @@ function delay(milliseconds) { return new Promise((resolve, _reject) => { setTimeout(() => { - resolve(new Date()) - }, milliseconds) - }) + resolve(new Date()); + }, milliseconds); + }); } function leakingLoop() { return delay(1).then(() => { - console.log(`Tick ${Date.now()}`) - return leakingLoop() - }) + console.log(`Tick ${Date.now()}`); + return leakingLoop(); + }); } // biome-ignore lint/correctness/noUnusedVariables: function nonLeakingLoop() { delay(1).then(() => { - console.log(`Tick ${Date.now()}`) - nonLeakingLoop() - }) + console.log(`Tick ${Date.now()}`); + nonLeakingLoop(); + }); } // biome-ignore lint/correctness/noUnusedVariables: function nonLeakingLoopWithErrors() { return new Promise((_resolve, reject) => { - ;(function internalLoop() { + (function internalLoop() { delay(1) .then(() => { - console.log(`Tick ${Date.now()}`) - internalLoop() + console.log(`Tick ${Date.now()}`); + internalLoop(); }) - .catch(err => { - reject(err) - }) - })() - }) + .catch((err) => { + reject(err); + }); + })(); + }); } // biome-ignore lint/correctness/noUnusedVariables: async function nonLeakingLoopAsync() { while (true) { - await delay(1) - console.log(`Tick ${Date.now()}`) + await delay(1); + console.log(`Tick ${Date.now()}`); } } // biome-ignore lint/correctness/noUnusedVariables: async function leakingLoopAsync() { - await delay(1) - console.log(`Tick ${Date.now()}`) - return leakingLoopAsync() + await delay(1); + console.log(`Tick ${Date.now()}`); + return leakingLoopAsync(); } for (let i = 0; i < 1e6; i++) { - leakingLoop() + leakingLoop(); // nonLeakingLoop() // nonLeakingLoopWithErrors() // nonLeakingLoopAsync() diff --git a/06-coding-with-streams/01-gzip-buffer-vs-stream/gzip-buffer.js b/06-coding-with-streams/01-gzip-buffer-vs-stream/gzip-buffer.js index 0ba7572..e6c7db3 100644 --- a/06-coding-with-streams/01-gzip-buffer-vs-stream/gzip-buffer.js +++ b/06-coding-with-streams/01-gzip-buffer-vs-stream/gzip-buffer.js @@ -1,12 +1,13 @@ -import { readFile, writeFile } from 'node:fs/promises' -import { promisify } from 'node:util' -import { gzip } from 'node:zlib' -const gzipPromise = promisify(gzip) // note: gzip is a callback-based function +import { readFile, writeFile } from "node:fs/promises"; +import { promisify } from "node:util"; +import { gzip } from "node:zlib"; -const filename = process.argv[2] +const gzipPromise = promisify(gzip); // note: gzip is a callback-based function -const data = await readFile(filename) -const gzippedData = await gzipPromise(data) -await writeFile(`${filename}.gz`, gzippedData) +const filename = process.argv[2]; -console.log('File successfully compressed') +const data = await readFile(filename); +const gzippedData = await gzipPromise(data); +await writeFile(`${filename}.gz`, gzippedData); + +console.log("File successfully compressed"); diff --git a/06-coding-with-streams/01-gzip-buffer-vs-stream/gzip-stream.js b/06-coding-with-streams/01-gzip-buffer-vs-stream/gzip-stream.js index 33b356e..0b1aa84 100644 --- a/06-coding-with-streams/01-gzip-buffer-vs-stream/gzip-stream.js +++ b/06-coding-with-streams/01-gzip-buffer-vs-stream/gzip-stream.js @@ -1,9 +1,9 @@ -import { createReadStream, createWriteStream } from 'node:fs' -import { createGzip } from 'node:zlib' +import { createReadStream, createWriteStream } from "node:fs"; +import { createGzip } from "node:zlib"; -const filename = process.argv[2] +const filename = process.argv[2]; createReadStream(filename) .pipe(createGzip()) .pipe(createWriteStream(`${filename}.gz`)) - .on('finish', () => console.log('File successfully compressed')) + .on("finish", () => console.log("File successfully compressed")); diff --git a/06-coding-with-streams/02-gzip-server/gzip-receive.js b/06-coding-with-streams/02-gzip-server/gzip-receive.js index 1493910..2b0890c 100644 --- a/06-coding-with-streams/02-gzip-server/gzip-receive.js +++ b/06-coding-with-streams/02-gzip-server/gzip-receive.js @@ -1,20 +1,20 @@ -import { createWriteStream } from 'node:fs' -import { createServer } from 'node:http' -import { basename, join } from 'node:path' -import { createGunzip } from 'node:zlib' +import { createWriteStream } from "node:fs"; +import { createServer } from "node:http"; +import { basename, join } from "node:path"; +import { createGunzip } from "node:zlib"; const server = createServer((req, res) => { - const filename = basename(req.headers['x-filename']) - const destFilename = join(import.meta.dirname, 'received_files', filename) - console.log(`File request received: ${filename}`) + const filename = basename(req.headers["x-filename"]); + const destFilename = join(import.meta.dirname, "received_files", filename); + console.log(`File request received: ${filename}`); req .pipe(createGunzip()) .pipe(createWriteStream(destFilename)) - .on('finish', () => { - res.writeHead(201, { 'content-type': 'text/plain' }) - res.end('OK\n') - console.log(`File saved: ${destFilename}`) - }) -}) + .on("finish", () => { + res.writeHead(201, { "content-type": "text/plain" }); + res.end("OK\n"); + console.log(`File saved: ${destFilename}`); + }); +}); -server.listen(3000, () => console.log('Listening on http://localhost:3000')) +server.listen(3000, () => console.log("Listening on http://localhost:3000")); diff --git a/06-coding-with-streams/02-gzip-server/gzip-send.js b/06-coding-with-streams/02-gzip-server/gzip-send.js index 3d1e72f..4570de0 100644 --- a/06-coding-with-streams/02-gzip-server/gzip-send.js +++ b/06-coding-with-streams/02-gzip-server/gzip-send.js @@ -1,30 +1,30 @@ -import { createReadStream } from 'node:fs' -import { request } from 'node:http' -import { basename } from 'node:path' -import { createGzip } from 'node:zlib' +import { createReadStream } from "node:fs"; +import { request } from "node:http"; +import { basename } from "node:path"; +import { createGzip } from "node:zlib"; -const filename = process.argv[2] -const serverHost = process.argv[3] +const filename = process.argv[2]; +const serverHost = process.argv[3]; const httpRequestOptions = { hostname: serverHost, port: 3000, - path: '/', - method: 'POST', + path: "/", + method: "POST", headers: { - 'content-type': 'application/octet-stream', - 'content-encoding': 'gzip', - 'x-filename': basename(filename), + "content-type": "application/octet-stream", + "content-encoding": "gzip", + "x-filename": basename(filename), }, -} +}; -const req = request(httpRequestOptions, res => { - console.log(`Server response: ${res.statusCode}`) -}) +const req = request(httpRequestOptions, (res) => { + console.log(`Server response: ${res.statusCode}`); +}); createReadStream(filename) .pipe(createGzip()) .pipe(req) - .on('finish', () => { - console.log('File successfully sent') - }) + .on("finish", () => { + console.log("File successfully sent"); + }); diff --git a/06-coding-with-streams/03-crypto-gzip-server/crypto-gzip-receive.js b/06-coding-with-streams/03-crypto-gzip-server/crypto-gzip-receive.js index 72a498a..8e0e1fe 100644 --- a/06-coding-with-streams/03-crypto-gzip-server/crypto-gzip-receive.js +++ b/06-coding-with-streams/03-crypto-gzip-server/crypto-gzip-receive.js @@ -1,26 +1,26 @@ -import { createDecipheriv, randomBytes } from 'node:crypto' -import { createWriteStream } from 'node:fs' -import { createServer } from 'node:http' -import { basename, join } from 'node:path' -import { createGunzip } from 'node:zlib' +import { createDecipheriv, randomBytes } from "node:crypto"; +import { createWriteStream } from "node:fs"; +import { createServer } from "node:http"; +import { basename, join } from "node:path"; +import { createGunzip } from "node:zlib"; -const secret = randomBytes(24) -console.log(`Generated secret: ${secret.toString('hex')}`) +const secret = randomBytes(24); +console.log(`Generated secret: ${secret.toString("hex")}`); const server = createServer((req, res) => { - const filename = basename(req.headers['x-filename']) - const destFilename = join(import.meta.dirname, 'received_files', filename) - const iv = Buffer.from(req.headers['x-initialization-vector'], 'hex') - console.log(`File request received: ${filename}`) + const filename = basename(req.headers["x-filename"]); + const destFilename = join(import.meta.dirname, "received_files", filename); + const iv = Buffer.from(req.headers["x-initialization-vector"], "hex"); + console.log(`File request received: ${filename}`); req - .pipe(createDecipheriv('aes192', secret, iv)) + .pipe(createDecipheriv("aes192", secret, iv)) .pipe(createGunzip()) .pipe(createWriteStream(destFilename)) - .on('finish', () => { - res.writeHead(201, { 'content-type': 'text/plain' }) - res.end('OK\n') - console.log(`File saved: ${destFilename}`) - }) -}) + .on("finish", () => { + res.writeHead(201, { "content-type": "text/plain" }); + res.end("OK\n"); + console.log(`File saved: ${destFilename}`); + }); +}); -server.listen(3000, () => console.log('Listening on http://localhost:3000')) +server.listen(3000, () => console.log("Listening on http://localhost:3000")); diff --git a/06-coding-with-streams/03-crypto-gzip-server/crypto-gzip-send.js b/06-coding-with-streams/03-crypto-gzip-server/crypto-gzip-send.js index ab01b6e..7ae54da 100644 --- a/06-coding-with-streams/03-crypto-gzip-server/crypto-gzip-send.js +++ b/06-coding-with-streams/03-crypto-gzip-server/crypto-gzip-send.js @@ -1,35 +1,35 @@ -import { createCipheriv, randomBytes } from 'node:crypto' -import { createReadStream } from 'node:fs' -import { request } from 'node:http' -import { basename } from 'node:path' -import { createGzip } from 'node:zlib' +import { createCipheriv, randomBytes } from "node:crypto"; +import { createReadStream } from "node:fs"; +import { request } from "node:http"; +import { basename } from "node:path"; +import { createGzip } from "node:zlib"; -const filename = process.argv[2] -const serverHost = process.argv[3] -const secret = Buffer.from(process.argv[4], 'hex') -const iv = randomBytes(16) +const filename = process.argv[2]; +const serverHost = process.argv[3]; +const secret = Buffer.from(process.argv[4], "hex"); +const iv = randomBytes(16); const httpRequestOptions = { hostname: serverHost, port: 3000, - path: '/', - method: 'POST', + path: "/", + method: "POST", headers: { - 'content-type': 'application/octet-stream', - 'content-encoding': 'gzip', - 'x-filename': basename(filename), - 'x-initialization-vector': iv.toString('hex'), + "content-type": "application/octet-stream", + "content-encoding": "gzip", + "x-filename": basename(filename), + "x-initialization-vector": iv.toString("hex"), }, -} +}; -const req = request(httpRequestOptions, res => { - console.log(`Server response: ${res.statusCode}`) -}) +const req = request(httpRequestOptions, (res) => { + console.log(`Server response: ${res.statusCode}`); +}); createReadStream(filename) .pipe(createGzip()) - .pipe(createCipheriv('aes192', secret, iv)) + .pipe(createCipheriv("aes192", secret, iv)) .pipe(req) - .on('finish', () => { - console.log('File successfully sent') - }) + .on("finish", () => { + console.log("File successfully sent"); + }); diff --git a/06-coding-with-streams/04-readable-non-flowing/read-stdin.js b/06-coding-with-streams/04-readable-non-flowing/read-stdin.js index 3508904..f6e5ddc 100644 --- a/06-coding-with-streams/04-readable-non-flowing/read-stdin.js +++ b/06-coding-with-streams/04-readable-non-flowing/read-stdin.js @@ -1,10 +1,10 @@ process.stdin - .on('readable', () => { - let chunk - console.log('New data available') + .on("readable", () => { + let chunk; + console.log("New data available"); // biome-ignore lint/suspicious/noAssignInExpressions: idiomatic while ((chunk = process.stdin.read()) !== null) { - console.log(`Chunk read (${chunk.length} bytes): "${chunk.toString()}"`) + console.log(`Chunk read (${chunk.length} bytes): "${chunk.toString()}"`); } }) - .on('end', () => console.log('End of stream')) + .on("end", () => console.log("End of stream")); diff --git a/06-coding-with-streams/05-readable-flowing/read-stdin.js b/06-coding-with-streams/05-readable-flowing/read-stdin.js index 7b1b364..b91bb32 100644 --- a/06-coding-with-streams/05-readable-flowing/read-stdin.js +++ b/06-coding-with-streams/05-readable-flowing/read-stdin.js @@ -1,6 +1,6 @@ process.stdin - .on('data', chunk => { - console.log('New data available') - console.log(`Chunk read (${chunk.length} bytes): "${chunk.toString()}"`) + .on("data", (chunk) => { + console.log("New data available"); + console.log(`Chunk read (${chunk.length} bytes): "${chunk.toString()}"`); }) - .on('end', () => console.log('End of stream')) + .on("end", () => console.log("End of stream")); diff --git a/06-coding-with-streams/06-readable-async-iterator/read-stdin.js b/06-coding-with-streams/06-readable-async-iterator/read-stdin.js index fc2dac1..3ff2ed8 100644 --- a/06-coding-with-streams/06-readable-async-iterator/read-stdin.js +++ b/06-coding-with-streams/06-readable-async-iterator/read-stdin.js @@ -1,6 +1,6 @@ for await (const chunk of process.stdin) { - console.log('New data available') - console.log(`Chunk read (${chunk.length} bytes): "${chunk.toString()}"`) + console.log("New data available"); + console.log(`Chunk read (${chunk.length} bytes): "${chunk.toString()}"`); } -console.log('End of stream') +console.log("End of stream"); diff --git a/06-coding-with-streams/07-custom-readable/index.js b/06-coding-with-streams/07-custom-readable/index.js index 6bf6491..75b560a 100644 --- a/06-coding-with-streams/07-custom-readable/index.js +++ b/06-coding-with-streams/07-custom-readable/index.js @@ -1,10 +1,10 @@ -import { RandomStream } from './random-stream.js' +import { RandomStream } from "./random-stream.js"; -const randomStream = new RandomStream() +const randomStream = new RandomStream(); randomStream - .on('data', chunk => { - console.log(`Chunk received (${chunk.length} bytes): ${chunk.toString()}`) - }) - .on('end', () => { - console.log(`Produced ${randomStream.emittedBytes} bytes of random data`) + .on("data", (chunk) => { + console.log(`Chunk received (${chunk.length} bytes): ${chunk.toString()}`); }) + .on("end", () => { + console.log(`Produced ${randomStream.emittedBytes} bytes of random data`); + }); diff --git a/06-coding-with-streams/07-custom-readable/random-stream.js b/06-coding-with-streams/07-custom-readable/random-stream.js index 9a332da..a91bbda 100644 --- a/06-coding-with-streams/07-custom-readable/random-stream.js +++ b/06-coding-with-streams/07-custom-readable/random-stream.js @@ -1,20 +1,20 @@ -import { Readable } from 'node:stream' -import Chance from 'chance' // v1.1.12 +import { Readable } from "node:stream"; +import Chance from "chance"; // v1.1.12 -const chance = new Chance() +const chance = new Chance(); export class RandomStream extends Readable { constructor(options) { - super(options) - this.emittedBytes = 0 + super(options); + this.emittedBytes = 0; } _read(size) { - const chunk = chance.string({ length: size }) - this.push(chunk, 'utf8') - this.emittedBytes += chunk.length + const chunk = chance.string({ length: size }); + this.push(chunk, "utf8"); + this.emittedBytes += chunk.length; if (chance.bool({ likelihood: 5 })) { - this.push(null) + this.push(null); } } } diff --git a/06-coding-with-streams/07-custom-readable/simplified-construction.js b/06-coding-with-streams/07-custom-readable/simplified-construction.js index b7611f1..fdcd60f 100644 --- a/06-coding-with-streams/07-custom-readable/simplified-construction.js +++ b/06-coding-with-streams/07-custom-readable/simplified-construction.js @@ -1,24 +1,24 @@ -import { Readable } from 'node:stream' -import Chance from 'chance' // v1.1.12 +import { Readable } from "node:stream"; +import Chance from "chance"; // v1.1.12 -const chance = new Chance() -let emittedBytes = 0 +const chance = new Chance(); +let emittedBytes = 0; const randomStream = new Readable({ read(size) { - const chunk = chance.string({ length: size }) - this.push(chunk, 'utf8') - emittedBytes += chunk.length + const chunk = chance.string({ length: size }); + this.push(chunk, "utf8"); + emittedBytes += chunk.length; if (chance.bool({ likelihood: 5 })) { - this.push(null) + this.push(null); } }, -}) +}); randomStream - .on('data', chunk => { - console.log(`Chunk received (${chunk.length} bytes): ${chunk.toString()}`) - }) - .on('end', () => { - console.log(`Produced ${emittedBytes} bytes of random data`) + .on("data", (chunk) => { + console.log(`Chunk received (${chunk.length} bytes): ${chunk.toString()}`); }) + .on("end", () => { + console.log(`Produced ${emittedBytes} bytes of random data`); + }); diff --git a/06-coding-with-streams/08-custom-readable-from-iterable/index.js b/06-coding-with-streams/08-custom-readable-from-iterable/index.js index 52b6e0d..e68490d 100644 --- a/06-coding-with-streams/08-custom-readable-from-iterable/index.js +++ b/06-coding-with-streams/08-custom-readable-from-iterable/index.js @@ -1,14 +1,14 @@ -import { Readable } from 'node:stream' +import { Readable } from "node:stream"; const mountains = [ - { name: 'Everest', height: 8848 }, - { name: 'K2', height: 8611 }, - { name: 'Kangchenjunga', height: 8586 }, - { name: 'Lhotse', height: 8516 }, - { name: 'Makalu', height: 8481 }, -] + { name: "Everest", height: 8848 }, + { name: "K2", height: 8611 }, + { name: "Kangchenjunga", height: 8586 }, + { name: "Lhotse", height: 8516 }, + { name: "Makalu", height: 8481 }, +]; -const mountainsStream = Readable.from(mountains) -mountainsStream.on('data', mountain => { - console.log(`${mountain.name.padStart(14)}\t${mountain.height}m`) -}) +const mountainsStream = Readable.from(mountains); +mountainsStream.on("data", (mountain) => { + console.log(`${mountain.name.padStart(14)}\t${mountain.height}m`); +}); diff --git a/06-coding-with-streams/09-writable-http-entropy-server/entropy-server.js b/06-coding-with-streams/09-writable-http-entropy-server/entropy-server.js index 710d96d..afd9cd7 100644 --- a/06-coding-with-streams/09-writable-http-entropy-server/entropy-server.js +++ b/06-coding-with-streams/09-writable-http-entropy-server/entropy-server.js @@ -1,17 +1,17 @@ -import { createServer } from 'node:http' -import Chance from 'chance' // 1.1.12 +import { createServer } from "node:http"; +import Chance from "chance"; // 1.1.12 -const chance = new Chance() +const chance = new Chance(); const server = createServer((_req, res) => { - res.writeHead(200, { 'Content-Type': 'text/plain' }) + res.writeHead(200, { "Content-Type": "text/plain" }); do { - res.write(`${chance.string()}\n`) - } while (chance.bool({ likelihood: 95 })) - res.end('\n\n') - res.on('finish', () => console.log('All data sent')) -}) + res.write(`${chance.string()}\n`); + } while (chance.bool({ likelihood: 95 })); + res.end("\n\n"); + res.on("finish", () => console.log("All data sent")); +}); server.listen(3000, () => { - console.log('listening on http://localhost:3000') -}) + console.log("listening on http://localhost:3000"); +}); diff --git a/06-coding-with-streams/10-writable-http-entropy-server-backpressure/entropy-server.js b/06-coding-with-streams/10-writable-http-entropy-server-backpressure/entropy-server.js index 08245cd..f85a5b7 100644 --- a/06-coding-with-streams/10-writable-http-entropy-server-backpressure/entropy-server.js +++ b/06-coding-with-streams/10-writable-http-entropy-server-backpressure/entropy-server.js @@ -1,32 +1,32 @@ -import { createServer } from 'node:http' -import Chance from 'chance' // 1.1.12 +import { createServer } from "node:http"; +import Chance from "chance"; // 1.1.12 -const CHUNK_SIZE = 16 * 1024 - 1 -const chance = new Chance() +const CHUNK_SIZE = 16 * 1024 - 1; +const chance = new Chance(); const server = createServer((_req, res) => { - res.writeHead(200, { 'Content-Type': 'text/plain' }) + res.writeHead(200, { "Content-Type": "text/plain" }); - let backPressureCount = 0 - let bytesSent = 0 + let backPressureCount = 0; + let bytesSent = 0; function generateMore() { do { - const randomChunk = chance.string({ length: CHUNK_SIZE }) - const shouldContinue = res.write(`${randomChunk}\n`) - bytesSent += CHUNK_SIZE + const randomChunk = chance.string({ length: CHUNK_SIZE }); + const shouldContinue = res.write(`${randomChunk}\n`); + bytesSent += CHUNK_SIZE; if (!shouldContinue) { - console.warn(`back-pressure x${++backPressureCount}`) - return res.once('drain', generateMore) + console.warn(`back-pressure x${++backPressureCount}`); + return res.once("drain", generateMore); } - } while (chance.bool({ likelihood: 95 })) - res.end('\n\n') + } while (chance.bool({ likelihood: 95 })); + res.end("\n\n"); } - generateMore() + generateMore(); - res.on('finish', () => console.log(`Sent ${bytesSent} bytes`)) -}) + res.on("finish", () => console.log(`Sent ${bytesSent} bytes`)); +}); server.listen(3000, () => { - console.log('listening on http://localhost:3000') -}) + console.log("listening on http://localhost:3000"); +}); diff --git a/06-coding-with-streams/11-custom-writable-to-file-stream/index.js b/06-coding-with-streams/11-custom-writable-to-file-stream/index.js index 9728eac..3172f76 100644 --- a/06-coding-with-streams/11-custom-writable-to-file-stream/index.js +++ b/06-coding-with-streams/11-custom-writable-to-file-stream/index.js @@ -1,10 +1,11 @@ -import { join } from 'node:path' -import { ToFileStream } from './to-file-stream.js' -const tfs = new ToFileStream() +import { join } from "node:path"; +import { ToFileStream } from "./to-file-stream.js"; -const outDir = join(import.meta.dirname, 'files') +const tfs = new ToFileStream(); -tfs.write({ path: join(outDir, 'file1.txt'), content: 'Hello' }) -tfs.write({ path: join(outDir, 'file2.txt'), content: 'Node.js' }) -tfs.write({ path: join(outDir, 'file3.txt'), content: 'streams' }) -tfs.end(() => console.log('All files created')) +const outDir = join(import.meta.dirname, "files"); + +tfs.write({ path: join(outDir, "file1.txt"), content: "Hello" }); +tfs.write({ path: join(outDir, "file2.txt"), content: "Node.js" }); +tfs.write({ path: join(outDir, "file3.txt"), content: "streams" }); +tfs.end(() => console.log("All files created")); diff --git a/06-coding-with-streams/11-custom-writable-to-file-stream/simplified-construction.js b/06-coding-with-streams/11-custom-writable-to-file-stream/simplified-construction.js index c94142a..922f7cd 100644 --- a/06-coding-with-streams/11-custom-writable-to-file-stream/simplified-construction.js +++ b/06-coding-with-streams/11-custom-writable-to-file-stream/simplified-construction.js @@ -1,7 +1,7 @@ -import { promises as fs } from 'node:fs' -import { dirname, join } from 'node:path' -import { Writable } from 'node:stream' -import { mkdirp } from 'mkdirp' +import { promises as fs } from "node:fs"; +import { dirname, join } from "node:path"; +import { Writable } from "node:stream"; +import { mkdirp } from "mkdirp"; const tfs = new Writable({ objectMode: true, @@ -9,13 +9,13 @@ const tfs = new Writable({ mkdirp(dirname(chunk.path)) .then(() => fs.writeFile(chunk.path, chunk.content)) .then(() => cb()) - .catch(cb) + .catch(cb); }, -}) +}); -const outDir = join(import.meta.dirname, 'files') +const outDir = join(import.meta.dirname, "files"); -tfs.write({ path: join(outDir, 'file1.txt'), content: 'Hello' }) -tfs.write({ path: join(outDir, 'file2.txt'), content: 'Node.js' }) -tfs.write({ path: join(outDir, 'file3.txt'), content: 'streams' }) -tfs.end(() => console.log('All files created')) +tfs.write({ path: join(outDir, "file1.txt"), content: "Hello" }); +tfs.write({ path: join(outDir, "file2.txt"), content: "Node.js" }); +tfs.write({ path: join(outDir, "file3.txt"), content: "streams" }); +tfs.end(() => console.log("All files created")); diff --git a/06-coding-with-streams/11-custom-writable-to-file-stream/to-file-stream.js b/06-coding-with-streams/11-custom-writable-to-file-stream/to-file-stream.js index 0676477..55cf20f 100644 --- a/06-coding-with-streams/11-custom-writable-to-file-stream/to-file-stream.js +++ b/06-coding-with-streams/11-custom-writable-to-file-stream/to-file-stream.js @@ -1,17 +1,17 @@ -import { promises as fs } from 'node:fs' -import { dirname } from 'node:path' -import { Writable } from 'node:stream' -import { mkdirp } from 'mkdirp' // v3.0.1 +import { promises as fs } from "node:fs"; +import { dirname } from "node:path"; +import { Writable } from "node:stream"; +import { mkdirp } from "mkdirp"; // v3.0.1 export class ToFileStream extends Writable { constructor(options) { - super({ ...options, objectMode: true }) + super({ ...options, objectMode: true }); } _write(chunk, _encoding, cb) { mkdirp(dirname(chunk.path)) .then(() => fs.writeFile(chunk.path, chunk.content)) .then(() => cb()) - .catch(cb) + .catch(cb); } } diff --git a/06-coding-with-streams/12-transform-stream-replace/index.js b/06-coding-with-streams/12-transform-stream-replace/index.js index 252ea3e..04381e2 100644 --- a/06-coding-with-streams/12-transform-stream-replace/index.js +++ b/06-coding-with-streams/12-transform-stream-replace/index.js @@ -1,8 +1,8 @@ -import { ReplaceStream } from './replace-stream.js' +import { ReplaceStream } from "./replace-stream.js"; -const replaceStream = new ReplaceStream('World', 'Node.js') -replaceStream.on('data', chunk => process.stdout.write(chunk.toString())) +const replaceStream = new ReplaceStream("World", "Node.js"); +replaceStream.on("data", (chunk) => process.stdout.write(chunk.toString())); -replaceStream.write('Hello W') -replaceStream.write('orld!') -replaceStream.end('\n') +replaceStream.write("Hello W"); +replaceStream.write("orld!"); +replaceStream.end("\n"); diff --git a/06-coding-with-streams/12-transform-stream-replace/replace-stream.js b/06-coding-with-streams/12-transform-stream-replace/replace-stream.js index 88908f5..a9d7e04 100644 --- a/06-coding-with-streams/12-transform-stream-replace/replace-stream.js +++ b/06-coding-with-streams/12-transform-stream-replace/replace-stream.js @@ -1,26 +1,26 @@ -import { Transform } from 'node:stream' +import { Transform } from "node:stream"; export class ReplaceStream extends Transform { constructor(searchStr, replaceStr, options) { - super({ ...options }) - this.searchStr = searchStr - this.replaceStr = replaceStr - this.tail = '' + super({ ...options }); + this.searchStr = searchStr; + this.replaceStr = replaceStr; + this.tail = ""; } _transform(chunk, _encoding, cb) { - const pieces = (this.tail + chunk).split(this.searchStr) - const lastPiece = pieces[pieces.length - 1] - const tailLen = this.searchStr.length - 1 - this.tail = lastPiece.slice(-tailLen) - pieces[pieces.length - 1] = lastPiece.slice(0, -tailLen) + const pieces = (this.tail + chunk).split(this.searchStr); + const lastPiece = pieces[pieces.length - 1]; + const tailLen = this.searchStr.length - 1; + this.tail = lastPiece.slice(-tailLen); + pieces[pieces.length - 1] = lastPiece.slice(0, -tailLen); - this.push(pieces.join(this.replaceStr)) - cb() + this.push(pieces.join(this.replaceStr)); + cb(); } _flush(cb) { - this.push(this.tail) - cb() + this.push(this.tail); + cb(); } } diff --git a/06-coding-with-streams/12-transform-stream-replace/simplified-construction.js b/06-coding-with-streams/12-transform-stream-replace/simplified-construction.js index ff6e7d4..103b526 100644 --- a/06-coding-with-streams/12-transform-stream-replace/simplified-construction.js +++ b/06-coding-with-streams/12-transform-stream-replace/simplified-construction.js @@ -1,29 +1,29 @@ -import { Transform } from 'node:stream' +import { Transform } from "node:stream"; -const searchStr = 'World' -const replaceStr = 'Node.js' -let tail = '' +const searchStr = "World"; +const replaceStr = "Node.js"; +let tail = ""; const replaceStream = new Transform({ - defaultEncoding: 'utf8', + defaultEncoding: "utf8", transform(chunk, _encoding, cb) { - const pieces = (tail + chunk).split(searchStr) - const lastPiece = pieces[pieces.length - 1] - const tailLen = searchStr.length - 1 - tail = lastPiece.slice(-tailLen) - pieces[pieces.length - 1] = lastPiece.slice(0, -tailLen) - this.push(pieces.join(replaceStr)) - cb() + const pieces = (tail + chunk).split(searchStr); + const lastPiece = pieces[pieces.length - 1]; + const tailLen = searchStr.length - 1; + tail = lastPiece.slice(-tailLen); + pieces[pieces.length - 1] = lastPiece.slice(0, -tailLen); + this.push(pieces.join(replaceStr)); + cb(); }, flush(cb) { - this.push(tail) - cb() + this.push(tail); + cb(); }, -}) +}); -replaceStream.on('data', chunk => process.stdout.write(chunk.toString())) -replaceStream.write('Hello W') -replaceStream.write('orld!') -replaceStream.end('\n') +replaceStream.on("data", (chunk) => process.stdout.write(chunk.toString())); +replaceStream.write("Hello W"); +replaceStream.write("orld!"); +replaceStream.end("\n"); diff --git a/06-coding-with-streams/13-transform-filter-reduce/filter-by-country.js b/06-coding-with-streams/13-transform-filter-reduce/filter-by-country.js index bfdf648..270fcf2 100644 --- a/06-coding-with-streams/13-transform-filter-reduce/filter-by-country.js +++ b/06-coding-with-streams/13-transform-filter-reduce/filter-by-country.js @@ -1,16 +1,16 @@ -import { Transform } from 'node:stream' +import { Transform } from "node:stream"; export class FilterByCountry extends Transform { constructor(country, options = {}) { - options.objectMode = true - super(options) - this.country = country + options.objectMode = true; + super(options); + this.country = country; } _transform(record, _enc, cb) { if (record.country === this.country) { - this.push(record) + this.push(record); } - cb() + cb(); } } diff --git a/06-coding-with-streams/13-transform-filter-reduce/index.js b/06-coding-with-streams/13-transform-filter-reduce/index.js index fe32c9f..37e80f8 100644 --- a/06-coding-with-streams/13-transform-filter-reduce/index.js +++ b/06-coding-with-streams/13-transform-filter-reduce/index.js @@ -1,18 +1,18 @@ -import { createReadStream } from 'node:fs' -import { createGunzip } from 'node:zlib' -import { Parser } from 'csv-parse' -import { FilterByCountry } from './filter-by-country.js' -import { SumProfit } from './sum-profit.js' +import { createReadStream } from "node:fs"; +import { createGunzip } from "node:zlib"; +import { Parser } from "csv-parse"; +import { FilterByCountry } from "./filter-by-country.js"; +import { SumProfit } from "./sum-profit.js"; -const csvParser = new Parser({ columns: true }) +const csvParser = new Parser({ columns: true }); // A small difference from the code presented in the book is that // here we have gzipped the data to keep the download size of the repository // as small as possible. For this reason we added an extra step that decompresses // the data on the fly. The final result doesn't change -createReadStream('data.csv.gz') +createReadStream("data.csv.gz") .pipe(createGunzip()) .pipe(csvParser) - .pipe(new FilterByCountry('Italy')) + .pipe(new FilterByCountry("Italy")) .pipe(new SumProfit()) - .pipe(process.stdout) + .pipe(process.stdout); diff --git a/06-coding-with-streams/13-transform-filter-reduce/sum-profit.js b/06-coding-with-streams/13-transform-filter-reduce/sum-profit.js index dad1a6d..08338e9 100644 --- a/06-coding-with-streams/13-transform-filter-reduce/sum-profit.js +++ b/06-coding-with-streams/13-transform-filter-reduce/sum-profit.js @@ -1,19 +1,19 @@ -import { Transform } from 'node:stream' +import { Transform } from "node:stream"; export class SumProfit extends Transform { constructor(options = {}) { - options.objectMode = true - super(options) - this.total = 0 + options.objectMode = true; + super(options); + this.total = 0; } _transform(record, _enc, cb) { - this.total += Number.parseFloat(record.profit) - cb() + this.total += Number.parseFloat(record.profit); + cb(); } _flush(cb) { - this.push(this.total.toString()) - cb() + this.push(this.total.toString()); + cb(); } } diff --git a/06-coding-with-streams/14-passthrough-monitoring/index.js b/06-coding-with-streams/14-passthrough-monitoring/index.js index 9ce2912..310e204 100644 --- a/06-coding-with-streams/14-passthrough-monitoring/index.js +++ b/06-coding-with-streams/14-passthrough-monitoring/index.js @@ -1,13 +1,13 @@ -import { PassThrough } from 'node:stream' +import { PassThrough } from "node:stream"; -let bytesWritten = 0 -const monitor = new PassThrough() -monitor.on('data', chunk => { - bytesWritten += chunk.length -}) -monitor.on('finish', () => { - console.log(`${bytesWritten} bytes written`) -}) +let bytesWritten = 0; +const monitor = new PassThrough(); +monitor.on("data", (chunk) => { + bytesWritten += chunk.length; +}); +monitor.on("finish", () => { + console.log(`${bytesWritten} bytes written`); +}); -monitor.write('Hello!') -monitor.end() +monitor.write("Hello!"); +monitor.end(); diff --git a/06-coding-with-streams/15-passthrough-late-piping-alternative/server.js b/06-coding-with-streams/15-passthrough-late-piping-alternative/server.js index 3e81e80..af0a02e 100644 --- a/06-coding-with-streams/15-passthrough-late-piping-alternative/server.js +++ b/06-coding-with-streams/15-passthrough-late-piping-alternative/server.js @@ -1,18 +1,18 @@ -import { createWriteStream } from 'node:fs' -import { createServer } from 'node:http' -import { basename, join } from 'node:path' +import { createWriteStream } from "node:fs"; +import { createServer } from "node:http"; +import { basename, join } from "node:path"; -const destDir = join(import.meta.dirname, 'received_files') +const destDir = join(import.meta.dirname, "received_files"); const server = createServer((req, res) => { - const filename = basename(req.headers['x-filename']) - const destFilename = join(destDir, filename) - console.log(`File request received: ${filename}`) - req.pipe(createWriteStream(destFilename)).on('finish', () => { - res.writeHead(201, { 'Content-Type': 'text/plain' }) - res.end('OK\n') - console.log(`File saved: ${destFilename}`) - }) -}) + const filename = basename(req.headers["x-filename"]); + const destFilename = join(destDir, filename); + console.log(`File request received: ${filename}`); + req.pipe(createWriteStream(destFilename)).on("finish", () => { + res.writeHead(201, { "Content-Type": "text/plain" }); + res.end("OK\n"); + console.log(`File saved: ${destFilename}`); + }); +}); -server.listen(3000, () => console.log('Listening on http://localhost:3000')) +server.listen(3000, () => console.log("Listening on http://localhost:3000")); diff --git a/06-coding-with-streams/15-passthrough-late-piping-alternative/upload-cli.js b/06-coding-with-streams/15-passthrough-late-piping-alternative/upload-cli.js index d99e1b1..3e5dd24 100644 --- a/06-coding-with-streams/15-passthrough-late-piping-alternative/upload-cli.js +++ b/06-coding-with-streams/15-passthrough-late-piping-alternative/upload-cli.js @@ -1,22 +1,22 @@ -import { createReadStream } from 'node:fs' -import { basename } from 'node:path' -import { pipeline } from 'node:stream' -import { createBrotliCompress } from 'node:zlib' -import { createUploadStream } from './upload.js' +import { createReadStream } from "node:fs"; +import { basename } from "node:path"; +import { pipeline } from "node:stream"; +import { createBrotliCompress } from "node:zlib"; +import { createUploadStream } from "./upload.js"; -const filepath = process.argv[2] -const filename = basename(filepath) +const filepath = process.argv[2]; +const filename = basename(filepath); pipeline( createReadStream(filepath), createBrotliCompress(), createUploadStream(`${filename}.br`), - err => { + (err) => { if (err) { - console.error(err) - process.exit(1) + console.error(err); + process.exit(1); } - console.log('File uploaded') - } -) + console.log("File uploaded"); + }, +); diff --git a/06-coding-with-streams/15-passthrough-late-piping-alternative/upload.js b/06-coding-with-streams/15-passthrough-late-piping-alternative/upload.js index 116d9b6..aa0825b 100644 --- a/06-coding-with-streams/15-passthrough-late-piping-alternative/upload.js +++ b/06-coding-with-streams/15-passthrough-late-piping-alternative/upload.js @@ -1,19 +1,19 @@ -import { PassThrough } from 'node:stream' -import axios from 'axios' +import { PassThrough } from "node:stream"; +import axios from "axios"; export function createUploadStream(filename) { - const connector = new PassThrough() + const connector = new PassThrough(); axios - .post('http://localhost:3000', connector, { + .post("http://localhost:3000", connector, { headers: { - 'Content-Type': 'application/octet-stream', - 'X-Filename': filename, + "Content-Type": "application/octet-stream", + "X-Filename": filename, }, }) - .catch(err => { - connector.emit(err) - }) + .catch((err) => { + connector.emit(err); + }); - return connector + return connector; } diff --git a/06-coding-with-streams/15-passthrough-late-piping/server.js b/06-coding-with-streams/15-passthrough-late-piping/server.js index 3e81e80..af0a02e 100644 --- a/06-coding-with-streams/15-passthrough-late-piping/server.js +++ b/06-coding-with-streams/15-passthrough-late-piping/server.js @@ -1,18 +1,18 @@ -import { createWriteStream } from 'node:fs' -import { createServer } from 'node:http' -import { basename, join } from 'node:path' +import { createWriteStream } from "node:fs"; +import { createServer } from "node:http"; +import { basename, join } from "node:path"; -const destDir = join(import.meta.dirname, 'received_files') +const destDir = join(import.meta.dirname, "received_files"); const server = createServer((req, res) => { - const filename = basename(req.headers['x-filename']) - const destFilename = join(destDir, filename) - console.log(`File request received: ${filename}`) - req.pipe(createWriteStream(destFilename)).on('finish', () => { - res.writeHead(201, { 'Content-Type': 'text/plain' }) - res.end('OK\n') - console.log(`File saved: ${destFilename}`) - }) -}) + const filename = basename(req.headers["x-filename"]); + const destFilename = join(destDir, filename); + console.log(`File request received: ${filename}`); + req.pipe(createWriteStream(destFilename)).on("finish", () => { + res.writeHead(201, { "Content-Type": "text/plain" }); + res.end("OK\n"); + console.log(`File saved: ${destFilename}`); + }); +}); -server.listen(3000, () => console.log('Listening on http://localhost:3000')) +server.listen(3000, () => console.log("Listening on http://localhost:3000")); diff --git a/06-coding-with-streams/15-passthrough-late-piping/upload-cli.js b/06-coding-with-streams/15-passthrough-late-piping/upload-cli.js index a4d0f2f..4900a4f 100644 --- a/06-coding-with-streams/15-passthrough-late-piping/upload-cli.js +++ b/06-coding-with-streams/15-passthrough-late-piping/upload-cli.js @@ -1,20 +1,20 @@ -import { createReadStream } from 'node:fs' -import { basename } from 'node:path' -import { PassThrough } from 'node:stream' -import { createBrotliCompress } from 'node:zlib' -import { upload } from './upload.js' +import { createReadStream } from "node:fs"; +import { basename } from "node:path"; +import { PassThrough } from "node:stream"; +import { createBrotliCompress } from "node:zlib"; +import { upload } from "./upload.js"; -const filepath = process.argv[2] -const filename = basename(filepath) -const contentStream = new PassThrough() +const filepath = process.argv[2]; +const filename = basename(filepath); +const contentStream = new PassThrough(); upload(`${filename}.br`, contentStream) - .then(response => { - console.log(`Server response: ${response.data}`) - }) - .catch(err => { - console.error(err) - process.exit(1) + .then((response) => { + console.log(`Server response: ${response.data}`); }) + .catch((err) => { + console.error(err); + process.exit(1); + }); -createReadStream(filepath).pipe(createBrotliCompress()).pipe(contentStream) +createReadStream(filepath).pipe(createBrotliCompress()).pipe(contentStream); diff --git a/06-coding-with-streams/15-passthrough-late-piping/upload.js b/06-coding-with-streams/15-passthrough-late-piping/upload.js index b95880f..d87d101 100644 --- a/06-coding-with-streams/15-passthrough-late-piping/upload.js +++ b/06-coding-with-streams/15-passthrough-late-piping/upload.js @@ -1,10 +1,10 @@ -import axios from 'axios' +import axios from "axios"; export function upload(filename, contentStream) { - return axios.post('http://localhost:3000', contentStream, { + return axios.post("http://localhost:3000", contentStream, { headers: { - 'Content-Type': 'application/octet-stream', - 'X-Filename': filename, + "Content-Type": "application/octet-stream", + "X-Filename": filename, }, - }) + }); } diff --git a/06-coding-with-streams/16-connecting-streams-using-pipes/replace-stream.js b/06-coding-with-streams/16-connecting-streams-using-pipes/replace-stream.js index 88908f5..a9d7e04 100644 --- a/06-coding-with-streams/16-connecting-streams-using-pipes/replace-stream.js +++ b/06-coding-with-streams/16-connecting-streams-using-pipes/replace-stream.js @@ -1,26 +1,26 @@ -import { Transform } from 'node:stream' +import { Transform } from "node:stream"; export class ReplaceStream extends Transform { constructor(searchStr, replaceStr, options) { - super({ ...options }) - this.searchStr = searchStr - this.replaceStr = replaceStr - this.tail = '' + super({ ...options }); + this.searchStr = searchStr; + this.replaceStr = replaceStr; + this.tail = ""; } _transform(chunk, _encoding, cb) { - const pieces = (this.tail + chunk).split(this.searchStr) - const lastPiece = pieces[pieces.length - 1] - const tailLen = this.searchStr.length - 1 - this.tail = lastPiece.slice(-tailLen) - pieces[pieces.length - 1] = lastPiece.slice(0, -tailLen) + const pieces = (this.tail + chunk).split(this.searchStr); + const lastPiece = pieces[pieces.length - 1]; + const tailLen = this.searchStr.length - 1; + this.tail = lastPiece.slice(-tailLen); + pieces[pieces.length - 1] = lastPiece.slice(0, -tailLen); - this.push(pieces.join(this.replaceStr)) - cb() + this.push(pieces.join(this.replaceStr)); + cb(); } _flush(cb) { - this.push(this.tail) - cb() + this.push(this.tail); + cb(); } } diff --git a/06-coding-with-streams/16-connecting-streams-using-pipes/replace.js b/06-coding-with-streams/16-connecting-streams-using-pipes/replace.js index 373d228..51a3745 100644 --- a/06-coding-with-streams/16-connecting-streams-using-pipes/replace.js +++ b/06-coding-with-streams/16-connecting-streams-using-pipes/replace.js @@ -1,5 +1,5 @@ -import { ReplaceStream } from './replace-stream.js' +import { ReplaceStream } from "./replace-stream.js"; process.stdin .pipe(new ReplaceStream(process.argv[2], process.argv[3])) - .pipe(process.stdout) + .pipe(process.stdout); diff --git a/06-coding-with-streams/17-pipeline-helper/uppercasify-gzipped.js b/06-coding-with-streams/17-pipeline-helper/uppercasify-gzipped.js index b94f317..f810189 100644 --- a/06-coding-with-streams/17-pipeline-helper/uppercasify-gzipped.js +++ b/06-coding-with-streams/17-pipeline-helper/uppercasify-gzipped.js @@ -1,18 +1,18 @@ -import { Transform } from 'node:stream' -import { pipeline } from 'node:stream/promises' -import { createGunzip, createGzip } from 'node:zlib' +import { Transform } from "node:stream"; +import { pipeline } from "node:stream/promises"; +import { createGunzip, createGzip } from "node:zlib"; const uppercasify = new Transform({ transform(chunk, _enc, cb) { - this.push(chunk.toString().toUpperCase()) - cb() + this.push(chunk.toString().toUpperCase()); + cb(); }, -}) +}); await pipeline( process.stdin, createGunzip(), uppercasify, createGzip(), - process.stdout -) + process.stdout, +); diff --git a/06-coding-with-streams/18-sequential-execution/concat-files.js b/06-coding-with-streams/18-sequential-execution/concat-files.js index d9a2c57..22a5751 100644 --- a/06-coding-with-streams/18-sequential-execution/concat-files.js +++ b/06-coding-with-streams/18-sequential-execution/concat-files.js @@ -1,32 +1,32 @@ -import { createReadStream, createWriteStream } from 'node:fs' -import { Readable, Transform } from 'node:stream' +import { createReadStream, createWriteStream } from "node:fs"; +import { Readable, Transform } from "node:stream"; export function concatFiles(dest, files) { return new Promise((resolve, reject) => { - const destStream = createWriteStream(dest) + const destStream = createWriteStream(dest); Readable.from(files) .pipe( new Transform({ objectMode: true, transform(filename, _enc, done) { - const src = createReadStream(filename) - src.pipe(destStream, { end: false }) + const src = createReadStream(filename); + src.pipe(destStream, { end: false }); // same as ((err) => done(err)) // propagates the error - src.on('error', done) + src.on("error", done); // same as (() => done()) // propagates correct completion - src.on('end', done) + src.on("end", done); }, - }) + }), ) - .on('error', err => { - destStream.end() - reject(err) + .on("error", (err) => { + destStream.end(); + reject(err); }) - .on('finish', () => { - destStream.end() - resolve() - }) - }) + .on("finish", () => { + destStream.end(); + resolve(); + }); + }); } diff --git a/06-coding-with-streams/18-sequential-execution/concat.js b/06-coding-with-streams/18-sequential-execution/concat.js index 2a1587b..5cd1474 100644 --- a/06-coding-with-streams/18-sequential-execution/concat.js +++ b/06-coding-with-streams/18-sequential-execution/concat.js @@ -1,10 +1,10 @@ -import { concatFiles } from './concat-files.js' +import { concatFiles } from "./concat-files.js"; try { - await concatFiles(process.argv[2], process.argv.slice(3)) + await concatFiles(process.argv[2], process.argv.slice(3)); } catch (err) { - console.error(err) - process.exit(1) + console.error(err); + process.exit(1); } -console.log('All files concatenated successfully') +console.log("All files concatenated successfully"); diff --git a/06-coding-with-streams/19-unordered-concurrent-execution/check-urls.js b/06-coding-with-streams/19-unordered-concurrent-execution/check-urls.js index 7614298..f0c00a1 100644 --- a/06-coding-with-streams/19-unordered-concurrent-execution/check-urls.js +++ b/06-coding-with-streams/19-unordered-concurrent-execution/check-urls.js @@ -1,26 +1,26 @@ -import { createReadStream, createWriteStream } from 'node:fs' -import { createInterface } from 'node:readline' -import { pipeline } from 'node:stream/promises' -import { ConcurrentStream } from './concurrent-stream.js' +import { createReadStream, createWriteStream } from "node:fs"; +import { createInterface } from "node:readline"; +import { pipeline } from "node:stream/promises"; +import { ConcurrentStream } from "./concurrent-stream.js"; -const inputFile = createReadStream(process.argv[2]) +const inputFile = createReadStream(process.argv[2]); const fileLines = createInterface({ input: inputFile, -}) +}); const checkUrls = new ConcurrentStream(async (url, _enc, push, done) => { if (!url) { - return done() + return done(); } try { - await fetch(url, { method: 'HEAD', timeout: 5 * 1000 }) - push(`${url} is up\n`) + await fetch(url, { method: "HEAD", timeout: 5 * 1000 }); + push(`${url} is up\n`); } catch (err) { - push(`${url} is down: ${err}\n`) + push(`${url} is down: ${err}\n`); } - done() -}) -const outputFile = createWriteStream('results.txt') + done(); +}); +const outputFile = createWriteStream("results.txt"); -await pipeline(fileLines, checkUrls, outputFile) +await pipeline(fileLines, checkUrls, outputFile); -console.log('All urls have been checked') +console.log("All urls have been checked"); diff --git a/06-coding-with-streams/19-unordered-concurrent-execution/concurrent-stream.js b/06-coding-with-streams/19-unordered-concurrent-execution/concurrent-stream.js index 5732e36..0f88033 100644 --- a/06-coding-with-streams/19-unordered-concurrent-execution/concurrent-stream.js +++ b/06-coding-with-streams/19-unordered-concurrent-execution/concurrent-stream.js @@ -1,39 +1,39 @@ -import { Transform } from 'node:stream' +import { Transform } from "node:stream"; export class ConcurrentStream extends Transform { constructor(userTransform, opts) { - super({ objectMode: true, ...opts }) - this.userTransform = userTransform - this.running = 0 - this.terminateCb = null + super({ objectMode: true, ...opts }); + this.userTransform = userTransform; + this.running = 0; + this.terminateCb = null; } _transform(chunk, enc, done) { - this.running++ + this.running++; this.userTransform( chunk, enc, this.push.bind(this), - this._onComplete.bind(this) - ) - done() + this._onComplete.bind(this), + ); + done(); } _flush(done) { if (this.running > 0) { - this.terminateCb = done + this.terminateCb = done; } else { - done() + done(); } } _onComplete(err) { - this.running-- + this.running--; if (err) { - return this.emit('error', err) + return this.emit("error", err); } if (this.running === 0) { - this.terminateCb?.() + this.terminateCb?.(); } } } diff --git a/06-coding-with-streams/20-unordered-limited-concurrent-execution/check-urls.js b/06-coding-with-streams/20-unordered-limited-concurrent-execution/check-urls.js index 46d3e54..dcb56e4 100644 --- a/06-coding-with-streams/20-unordered-limited-concurrent-execution/check-urls.js +++ b/06-coding-with-streams/20-unordered-limited-concurrent-execution/check-urls.js @@ -1,33 +1,33 @@ -import { createReadStream, createWriteStream } from 'node:fs' -import { createInterface } from 'node:readline' -import { pipeline } from 'node:stream/promises' -import { LimitedConcurrentStream } from './limited-concurrent-stream.js' +import { createReadStream, createWriteStream } from "node:fs"; +import { createInterface } from "node:readline"; +import { pipeline } from "node:stream/promises"; +import { LimitedConcurrentStream } from "./limited-concurrent-stream.js"; -const inputFile = createReadStream(process.argv[2]) +const inputFile = createReadStream(process.argv[2]); const fileLines = createInterface({ input: inputFile, -}) +}); const checkUrls = new LimitedConcurrentStream( 8, async (url, _enc, push, done) => { if (!url) { - return done() + return done(); } try { await fetch(url, { - method: 'HEAD', + method: "HEAD", timeout: 5000, signal: AbortSignal.timeout(5000), - }) - push(`${url} is up\n`) + }); + push(`${url} is up\n`); } catch (err) { - push(`${url} is down: ${err}\n`) + push(`${url} is down: ${err}\n`); } - done() - } -) -const outputFile = createWriteStream('results.txt') + done(); + }, +); +const outputFile = createWriteStream("results.txt"); -await pipeline(fileLines, checkUrls, outputFile) +await pipeline(fileLines, checkUrls, outputFile); -console.log('All urls have been checked') +console.log("All urls have been checked"); diff --git a/06-coding-with-streams/20-unordered-limited-concurrent-execution/limited-concurrent-stream.js b/06-coding-with-streams/20-unordered-limited-concurrent-execution/limited-concurrent-stream.js index 4999239..cf1393b 100644 --- a/06-coding-with-streams/20-unordered-limited-concurrent-execution/limited-concurrent-stream.js +++ b/06-coding-with-streams/20-unordered-limited-concurrent-execution/limited-concurrent-stream.js @@ -1,53 +1,53 @@ -import { Transform } from 'node:stream' +import { Transform } from "node:stream"; export class LimitedConcurrentStream extends Transform { constructor(concurrency, userTransform, opts) { - super({ objectMode: true, ...opts }) - this.concurrency = concurrency - this.userTransform = userTransform - this.running = 0 - this.continueCb = null - this.terminateCb = null + super({ objectMode: true, ...opts }); + this.concurrency = concurrency; + this.userTransform = userTransform; + this.running = 0; + this.continueCb = null; + this.terminateCb = null; } _transform(chunk, enc, done) { - this.running++ + this.running++; const maybePromise = this.userTransform( chunk, enc, this.push.bind(this), - this._onComplete.bind(this) - ) + this._onComplete.bind(this), + ); - if (maybePromise && typeof maybePromise.then === 'function') { - maybePromise.catch(err => this.emit('error', err)) + if (maybePromise && typeof maybePromise.then === "function") { + maybePromise.catch((err) => this.emit("error", err)); } if (this.running < this.concurrency) { - done() + done(); } else { - this.continueCb = done + this.continueCb = done; } } _flush(done) { if (this.running > 0) { - this.terminateCb = done + this.terminateCb = done; } else { - done() + done(); } } _onComplete(err) { - this.running-- + this.running--; if (err) { - return this.emit('error', err) + return this.emit("error", err); } - const tmpCb = this.continueCb - this.continueCb = null - tmpCb?.() + const tmpCb = this.continueCb; + this.continueCb = null; + tmpCb?.(); if (this.running === 0) { - this.terminateCb?.() + this.terminateCb?.(); } } } diff --git a/06-coding-with-streams/21-ordered-limited-concurrent-execution/check-urls.js b/06-coding-with-streams/21-ordered-limited-concurrent-execution/check-urls.js index 9d54c80..7f728c7 100644 --- a/06-coding-with-streams/21-ordered-limited-concurrent-execution/check-urls.js +++ b/06-coding-with-streams/21-ordered-limited-concurrent-execution/check-urls.js @@ -1,26 +1,26 @@ -import { createReadStream, createWriteStream } from 'node:fs' -import { createInterface } from 'node:readline' -import { pipeline } from 'node:stream/promises' -import parallelTransform from 'parallel-transform' // v1.2.0 +import { createReadStream, createWriteStream } from "node:fs"; +import { createInterface } from "node:readline"; +import { pipeline } from "node:stream/promises"; +import parallelTransform from "parallel-transform"; // v1.2.0 -const inputFile = createReadStream(process.argv[2]) +const inputFile = createReadStream(process.argv[2]); const fileLines = createInterface({ input: inputFile, -}) +}); const checkUrls = parallelTransform(8, async function (url, done) { if (!url) { - return done() + return done(); } try { - await fetch(url, { method: 'HEAD', timeout: 5 * 1000 }) - this.push(`${url} is up\n`) + await fetch(url, { method: "HEAD", timeout: 5 * 1000 }); + this.push(`${url} is up\n`); } catch (err) { - this.push(`${url} is down: ${err}\n`) + this.push(`${url} is down: ${err}\n`); } - done() -}) -const outputFile = createWriteStream('results.txt') + done(); +}); +const outputFile = createWriteStream("results.txt"); -await pipeline(fileLines, checkUrls, outputFile) +await pipeline(fileLines, checkUrls, outputFile); -console.log('All urls have been checked') +console.log("All urls have been checked"); diff --git a/06-coding-with-streams/22-combined-stream/archive.js b/06-coding-with-streams/22-combined-stream/archive.js index 7b1e9f2..6fdfa17 100644 --- a/06-coding-with-streams/22-combined-stream/archive.js +++ b/06-coding-with-streams/22-combined-stream/archive.js @@ -1,21 +1,21 @@ -import { randomBytes } from 'node:crypto' -import { createReadStream, createWriteStream } from 'node:fs' -import { pipeline } from 'node:stream' -import { createCompressAndEncrypt } from './combined-streams.js' +import { randomBytes } from "node:crypto"; +import { createReadStream, createWriteStream } from "node:fs"; +import { pipeline } from "node:stream"; +import { createCompressAndEncrypt } from "./combined-streams.js"; -const [, , password, source] = process.argv -const iv = randomBytes(16) -const destination = `${source}.gz.enc` +const [, , password, source] = process.argv; +const iv = randomBytes(16); +const destination = `${source}.gz.enc`; pipeline( createReadStream(source), createCompressAndEncrypt(password, iv), createWriteStream(destination), - err => { + (err) => { if (err) { - console.error(err) - process.exit(1) + console.error(err); + process.exit(1); } - console.log(`${destination} created with iv: ${iv.toString('hex')}`) - } -) + console.log(`${destination} created with iv: ${iv.toString("hex")}`); + }, +); diff --git a/06-coding-with-streams/22-combined-stream/combined-streams.js b/06-coding-with-streams/22-combined-stream/combined-streams.js index 83b28fb..94fcb28 100644 --- a/06-coding-with-streams/22-combined-stream/combined-streams.js +++ b/06-coding-with-streams/22-combined-stream/combined-streams.js @@ -1,23 +1,23 @@ -import { createCipheriv, createDecipheriv, scryptSync } from 'node:crypto' -import { compose } from 'node:stream' -import { createGunzip, createGzip } from 'node:zlib' +import { createCipheriv, createDecipheriv, scryptSync } from "node:crypto"; +import { compose } from "node:stream"; +import { createGunzip, createGzip } from "node:zlib"; function createKey(password) { - return scryptSync(password, 'salt', 24) + return scryptSync(password, "salt", 24); } export function createCompressAndEncrypt(password, iv) { - const key = createKey(password) + const key = createKey(password); const combinedStream = compose( createGzip(), - createCipheriv('aes192', key, iv) - ) - combinedStream.iv = iv + createCipheriv("aes192", key, iv), + ); + combinedStream.iv = iv; - return combinedStream + return combinedStream; } export function createDecryptAndDecompress(password, iv) { - const key = createKey(password) - return compose(createDecipheriv('aes192', key, iv), createGunzip()) + const key = createKey(password); + return compose(createDecipheriv("aes192", key, iv), createGunzip()); } diff --git a/06-coding-with-streams/22-combined-stream/pipe-and-pipeline-test.js b/06-coding-with-streams/22-combined-stream/pipe-and-pipeline-test.js index d74682b..c14d532 100644 --- a/06-coding-with-streams/22-combined-stream/pipe-and-pipeline-test.js +++ b/06-coding-with-streams/22-combined-stream/pipe-and-pipeline-test.js @@ -1,23 +1,23 @@ -import assert from 'node:assert/strict' -import { createReadStream, createWriteStream } from 'node:fs' -import { Transform, pipeline } from 'node:stream' +import assert from "node:assert/strict"; +import { createReadStream, createWriteStream } from "node:fs"; +import { pipeline, Transform } from "node:stream"; -const streamA = createReadStream('package.json') +const streamA = createReadStream("package.json"); const streamB = new Transform({ transform(chunk, _enc, done) { - this.push(chunk.toString().toUpperCase()) - done() + this.push(chunk.toString().toUpperCase()); + done(); }, -}) -const streamC = createWriteStream('package-uppercase.json') +}); +const streamC = createWriteStream("package-uppercase.json"); const pipelineReturn = pipeline(streamA, streamB, streamC, () => { // handle errors here -}) +}); // biome-ignore lint/suspicious/noMisplacedAssertion: Not an actual unit test -assert.equal(streamC, pipelineReturn) // valid +assert.equal(streamC, pipelineReturn); // valid -const pipeReturn = streamA.pipe(streamB).pipe(streamC) +const pipeReturn = streamA.pipe(streamB).pipe(streamC); // biome-ignore lint/suspicious/noMisplacedAssertion: Not an actual unit test -assert.equal(streamC, pipeReturn) // valid +assert.equal(streamC, pipeReturn); // valid diff --git a/06-coding-with-streams/22-combined-stream/unarchive.js b/06-coding-with-streams/22-combined-stream/unarchive.js index de9d419..391b73b 100644 --- a/06-coding-with-streams/22-combined-stream/unarchive.js +++ b/06-coding-with-streams/22-combined-stream/unarchive.js @@ -1,22 +1,22 @@ -import { createReadStream, createWriteStream } from 'node:fs' -import { pipeline } from 'node:stream' -import { createDecryptAndDecompress } from './combined-streams.js' +import { createReadStream, createWriteStream } from "node:fs"; +import { pipeline } from "node:stream"; +import { createDecryptAndDecompress } from "./combined-streams.js"; // usage: node unarchive.js // example: // node unarchive.js alovelypassword 158bc6bb3648afc1415371ae0c240715 package.json.gz.enc decoded-package.json -const [, , password, ivHex, source, destination] = process.argv -const iv = Buffer.from(ivHex, 'hex') +const [, , password, ivHex, source, destination] = process.argv; +const iv = Buffer.from(ivHex, "hex"); pipeline( createReadStream(source), createDecryptAndDecompress(password, iv), createWriteStream(destination), - err => { + (err) => { if (err) { - console.error(err) - process.exit(1) + console.error(err); + process.exit(1); } - console.log(`${destination} created`) - } -) + console.log(`${destination} created`); + }, +); diff --git a/06-coding-with-streams/23-forking-streams/generate-hashes.js b/06-coding-with-streams/23-forking-streams/generate-hashes.js index 0874c42..377cd6e 100644 --- a/06-coding-with-streams/23-forking-streams/generate-hashes.js +++ b/06-coding-with-streams/23-forking-streams/generate-hashes.js @@ -1,12 +1,12 @@ -import { createHash } from 'node:crypto' -import { createReadStream, createWriteStream } from 'node:fs' +import { createHash } from "node:crypto"; +import { createReadStream, createWriteStream } from "node:fs"; -const filename = process.argv[2] -const sha1Stream = createHash('sha1').setEncoding('hex') -const md5Stream = createHash('md5').setEncoding('hex') +const filename = process.argv[2]; +const sha1Stream = createHash("sha1").setEncoding("hex"); +const md5Stream = createHash("md5").setEncoding("hex"); -const inputStream = createReadStream(filename) +const inputStream = createReadStream(filename); -inputStream.pipe(sha1Stream).pipe(createWriteStream(`${filename}.sha1`)) +inputStream.pipe(sha1Stream).pipe(createWriteStream(`${filename}.sha1`)); -inputStream.pipe(md5Stream).pipe(createWriteStream(`${filename}.md5`)) +inputStream.pipe(md5Stream).pipe(createWriteStream(`${filename}.md5`)); diff --git a/06-coding-with-streams/24-merging-streams/merge-lines.js b/06-coding-with-streams/24-merging-streams/merge-lines.js index 031c35e..d53b463 100644 --- a/06-coding-with-streams/24-merging-streams/merge-lines.js +++ b/06-coding-with-streams/24-merging-streams/merge-lines.js @@ -1,25 +1,25 @@ -import { createReadStream, createWriteStream } from 'node:fs' -import { createInterface } from 'node:readline' -import { Readable, Transform } from 'node:stream' +import { createReadStream, createWriteStream } from "node:fs"; +import { createInterface } from "node:readline"; +import { Readable, Transform } from "node:stream"; -const [, , dest, ...sources] = process.argv -const destStream = createWriteStream(dest) +const [, , dest, ...sources] = process.argv; +const destStream = createWriteStream(dest); -let endCount = 0 +let endCount = 0; for (const source of sources) { - const sourceStream = createReadStream(source, { highWaterMark: 16 }) - const linesStream = Readable.from(createInterface({ input: sourceStream })) + const sourceStream = createReadStream(source, { highWaterMark: 16 }); + const linesStream = Readable.from(createInterface({ input: sourceStream })); const addLineEnd = new Transform({ transform(chunk, _encoding, cb) { - cb(null, `${chunk}\n`) + cb(null, `${chunk}\n`); }, - }) + }); - sourceStream.on('end', () => { + sourceStream.on("end", () => { if (++endCount === sources.length) { - destStream.end() - console.log(`${dest} created`) + destStream.end(); + console.log(`${dest} created`); } - }) - linesStream.pipe(addLineEnd).pipe(destStream, { end: false }) + }); + linesStream.pipe(addLineEnd).pipe(destStream, { end: false }); } diff --git a/06-coding-with-streams/25-multiplexing/client.js b/06-coding-with-streams/25-multiplexing/client.js index 35db51e..bc5b947 100644 --- a/06-coding-with-streams/25-multiplexing/client.js +++ b/06-coding-with-streams/25-multiplexing/client.js @@ -1,31 +1,31 @@ -import { fork } from 'node:child_process' -import { connect } from 'node:net' +import { fork } from "node:child_process"; +import { connect } from "node:net"; function multiplexChannels(sources, destination) { - let openChannels = sources.length + let openChannels = sources.length; for (let i = 0; i < sources.length; i++) { sources[i] - .on('readable', function () { - let chunk + .on("readable", function () { + let chunk; // biome-ignore lint/suspicious/noAssignInExpressions: idiomatic while ((chunk = this.read()) !== null) { - const outBuff = Buffer.alloc(1 + 4 + chunk.length) - outBuff.writeUInt8(i, 0) - outBuff.writeUInt32BE(chunk.length, 1) - chunk.copy(outBuff, 5) - console.log(`Sending packet to channel: ${i}`) - destination.write(outBuff) + const outBuff = Buffer.alloc(1 + 4 + chunk.length); + outBuff.writeUInt8(i, 0); + outBuff.writeUInt32BE(chunk.length, 1); + chunk.copy(outBuff, 5); + console.log(`Sending packet to channel: ${i}`); + destination.write(outBuff); } }) - .on('end', () => { + .on("end", () => { if (--openChannels === 0) { - destination.end() + destination.end(); } - }) + }); } } const socket = connect(3000, () => { - const child = fork(process.argv[2], process.argv.slice(3), { silent: true }) - multiplexChannels([child.stdout, child.stderr], socket) -}) + const child = fork(process.argv[2], process.argv.slice(3), { silent: true }); + multiplexChannels([child.stdout, child.stderr], socket); +}); diff --git a/06-coding-with-streams/25-multiplexing/generate-data.js b/06-coding-with-streams/25-multiplexing/generate-data.js index 312b20f..d702a29 100644 --- a/06-coding-with-streams/25-multiplexing/generate-data.js +++ b/06-coding-with-streams/25-multiplexing/generate-data.js @@ -1,5 +1,5 @@ -console.log('out1') -console.log('out2') -console.error('err1') -console.log('out3') -console.error('err2') +console.log("out1"); +console.log("out2"); +console.error("err1"); +console.log("out3"); +console.error("err2"); diff --git a/06-coding-with-streams/25-multiplexing/server.js b/06-coding-with-streams/25-multiplexing/server.js index 22018dc..1891a4d 100644 --- a/06-coding-with-streams/25-multiplexing/server.js +++ b/06-coding-with-streams/25-multiplexing/server.js @@ -1,47 +1,47 @@ -import { createWriteStream } from 'node:fs' -import { createServer } from 'node:net' +import { createWriteStream } from "node:fs"; +import { createServer } from "node:net"; function demultiplexChannel(source, destinations) { - let currentChannel = null - let currentLength = null + let currentChannel = null; + let currentLength = null; source - .on('readable', () => { - let chunk + .on("readable", () => { + let chunk; if (currentChannel === null) { - chunk = source.read(1) - currentChannel = chunk?.readUInt8(0) + chunk = source.read(1); + currentChannel = chunk?.readUInt8(0); } if (currentLength === null) { - chunk = source.read(4) - currentLength = chunk?.readUInt32BE(0) + chunk = source.read(4); + currentLength = chunk?.readUInt32BE(0); if (currentLength === null) { - return null + return null; } } - chunk = source.read(currentLength) + chunk = source.read(currentLength); if (chunk === null) { - return null + return null; } - console.log(`Received packet from: ${currentChannel}`) - destinations[currentChannel].write(chunk) - currentChannel = null - currentLength = null + console.log(`Received packet from: ${currentChannel}`); + destinations[currentChannel].write(chunk); + currentChannel = null; + currentLength = null; }) - .on('end', () => { + .on("end", () => { for (const destination of destinations) { - destination.end() + destination.end(); } - console.log('Source channel closed') - }) + console.log("Source channel closed"); + }); } -const server = createServer(socket => { - const stdoutStream = createWriteStream('stdout.log') - const stderrStream = createWriteStream('stderr.log') - demultiplexChannel(socket, [stdoutStream, stderrStream]) -}) -server.listen(3000, () => console.log('Server started')) +const server = createServer((socket) => { + const stdoutStream = createWriteStream("stdout.log"); + const stderrStream = createWriteStream("stderr.log"); + demultiplexChannel(socket, [stdoutStream, stderrStream]); +}); +server.listen(3000, () => console.log("Server started")); diff --git a/06-coding-with-streams/26-readable-stream-utilities/index.js b/06-coding-with-streams/26-readable-stream-utilities/index.js index f5180f9..2d14240 100644 --- a/06-coding-with-streams/26-readable-stream-utilities/index.js +++ b/06-coding-with-streams/26-readable-stream-utilities/index.js @@ -1,21 +1,21 @@ -import { createReadStream } from 'node:fs' -import { createInterface } from 'node:readline' -import { Readable, compose } from 'node:stream' -import { createGunzip } from 'node:zlib' +import { createReadStream } from "node:fs"; +import { createInterface } from "node:readline"; +import { compose, Readable } from "node:stream"; +import { createGunzip } from "node:zlib"; const uncompressedData = compose( - createReadStream('data.csv.gz'), - createGunzip() -) -const byLine = Readable.from(createInterface({ input: uncompressedData })) + createReadStream("data.csv.gz"), + createGunzip(), +); +const byLine = Readable.from(createInterface({ input: uncompressedData })); const totalProfit = await byLine .drop(1) - .map(chunk => { - const [type, country, profit] = chunk.toString().split(',') - return { type, country, profit: Number.parseFloat(profit) } + .map((chunk) => { + const [type, country, profit] = chunk.toString().split(","); + return { type, country, profit: Number.parseFloat(profit) }; }) - .filter(record => record.country === 'Italy') - .reduce((acc, record) => acc + record.profit, 0) + .filter((record) => record.country === "Italy") + .reduce((acc, record) => acc + record.profit, 0); -console.log(totalProfit) +console.log(totalProfit); diff --git a/06-coding-with-streams/27-web-streams/node-to-web.js b/06-coding-with-streams/27-web-streams/node-to-web.js index 0da7bad..dee7aba 100644 --- a/06-coding-with-streams/27-web-streams/node-to-web.js +++ b/06-coding-with-streams/27-web-streams/node-to-web.js @@ -1,34 +1,34 @@ -import { Readable, Transform, Writable } from 'node:stream' +import { Readable, Transform, Writable } from "node:stream"; const nodeReadable = new Readable({ read() { - this.push('Hello, ') - this.push('world!') - this.push(null) + this.push("Hello, "); + this.push("world!"); + this.push(null); }, -}) +}); -const webReadable = Readable.toWeb(nodeReadable) -console.log(webReadable) +const webReadable = Readable.toWeb(nodeReadable); +console.log(webReadable); const nodeWritable = new Writable({ write(chunk, _enc, cb) { - console.log(chunk.toString()) - cb() + console.log(chunk.toString()); + cb(); }, -}) +}); -const webWritable = Writable.toWeb(nodeWritable) -console.log(webWritable) +const webWritable = Writable.toWeb(nodeWritable); +console.log(webWritable); const nodeTransform = new Transform({ transform(chunk, _enc, cb) { - cb(null, chunk.toString().toUpperCase()) + cb(null, chunk.toString().toUpperCase()); }, -}) +}); -const webTransform = Transform.toWeb(nodeTransform) -console.log(webTransform) +const webTransform = Transform.toWeb(nodeTransform); +console.log(webTransform); -nodeReadable.pipe(process.stdout) -webReadable.pipeTo(Writable.toWeb(process.stdout)) +nodeReadable.pipe(process.stdout); +webReadable.pipeTo(Writable.toWeb(process.stdout)); diff --git a/06-coding-with-streams/27-web-streams/web-to-node.js b/06-coding-with-streams/27-web-streams/web-to-node.js index c4a0f8a..31cad26 100644 --- a/06-coding-with-streams/27-web-streams/web-to-node.js +++ b/06-coding-with-streams/27-web-streams/web-to-node.js @@ -1,35 +1,35 @@ -import { Readable, Transform, Writable } from 'node:stream' +import { Readable, Transform, Writable } from "node:stream"; import { ReadableStream, TransformStream, WritableStream, -} from 'node:stream/web' +} from "node:stream/web"; const webReadable = new ReadableStream({ start(controller) { - controller.enqueue('Hello, ') - controller.enqueue('world!') - controller.close() + controller.enqueue("Hello, "); + controller.enqueue("world!"); + controller.close(); }, -}) +}); -const nodeReadable = Readable.fromWeb(webReadable) -console.log(nodeReadable) +const nodeReadable = Readable.fromWeb(webReadable); +console.log(nodeReadable); const webWritable = new WritableStream({ write(chunk) { - console.log(chunk) + console.log(chunk); }, -}) +}); -const nodeWritable = Writable.fromWeb(webWritable) -console.log(nodeWritable) +const nodeWritable = Writable.fromWeb(webWritable); +console.log(nodeWritable); const webTransform = new TransformStream({ transform(chunk, controller) { - controller.enqueue(chunk.toString().toUpperCase()) + controller.enqueue(chunk.toString().toUpperCase()); }, -}) +}); -const nodeTransform = Transform.fromWeb(webTransform) -console.log(nodeTransform) +const nodeTransform = Transform.fromWeb(webTransform); +console.log(nodeTransform); diff --git a/06-coding-with-streams/28-stream-consumers/accumulate-http-consumers.js b/06-coding-with-streams/28-stream-consumers/accumulate-http-consumers.js index 237e2bf..27aeb22 100644 --- a/06-coding-with-streams/28-stream-consumers/accumulate-http-consumers.js +++ b/06-coding-with-streams/28-stream-consumers/accumulate-http-consumers.js @@ -1,11 +1,11 @@ -import { request } from 'node:https' -import consumers from 'node:stream/consumers' +import { request } from "node:https"; +import consumers from "node:stream/consumers"; const req = request( - 'https://jsonplaceholder.typicode.com/todos/1', - async res => { - const buffer = await consumers.json(res) - console.log(buffer) - } -) -req.end() + "https://jsonplaceholder.typicode.com/todos/1", + async (res) => { + const buffer = await consumers.json(res); + console.log(buffer); + }, +); +req.end(); diff --git a/06-coding-with-streams/28-stream-consumers/accumulate-http-fetch.js b/06-coding-with-streams/28-stream-consumers/accumulate-http-fetch.js index 349ff5b..8f4e6b8 100644 --- a/06-coding-with-streams/28-stream-consumers/accumulate-http-fetch.js +++ b/06-coding-with-streams/28-stream-consumers/accumulate-http-fetch.js @@ -1,3 +1,3 @@ -const res = await fetch('https://jsonplaceholder.typicode.com/todos/1') -const buffer = await res.json() -console.log(buffer) +const res = await fetch("https://jsonplaceholder.typicode.com/todos/1"); +const buffer = await res.json(); +console.log(buffer); diff --git a/06-coding-with-streams/28-stream-consumers/accumulate-http.js b/06-coding-with-streams/28-stream-consumers/accumulate-http.js index daeb617..241f8cb 100644 --- a/06-coding-with-streams/28-stream-consumers/accumulate-http.js +++ b/06-coding-with-streams/28-stream-consumers/accumulate-http.js @@ -1,12 +1,12 @@ -import { request } from 'node:https' +import { request } from "node:https"; -const req = request('https://jsonplaceholder.typicode.com/todos/1', res => { - let buffer = '' - res.on('data', chunk => { - buffer += chunk - }) - res.on('end', () => { - console.log(JSON.parse(buffer)) - }) -}) -req.end() +const req = request("https://jsonplaceholder.typicode.com/todos/1", (res) => { + let buffer = ""; + res.on("data", (chunk) => { + buffer += chunk; + }); + res.on("end", () => { + console.log(JSON.parse(buffer)); + }); +}); +req.end(); diff --git a/07-creational-design-patterns/01-factory-simple/image.js b/07-creational-design-patterns/01-factory-simple/image.js index 8c48190..eff7444 100644 --- a/07-creational-design-patterns/01-factory-simple/image.js +++ b/07-creational-design-patterns/01-factory-simple/image.js @@ -1,5 +1,5 @@ export class Image { constructor(path) { - this.path = path + this.path = path; } } diff --git a/07-creational-design-patterns/01-factory-simple/index.js b/07-creational-design-patterns/01-factory-simple/index.js index be5ddbe..01708bf 100644 --- a/07-creational-design-patterns/01-factory-simple/index.js +++ b/07-creational-design-patterns/01-factory-simple/index.js @@ -1,11 +1,11 @@ -import { Image } from './image.js' +import { Image } from "./image.js"; // Factory function function createImage(name) { - return new Image(name) + return new Image(name); } // Factory invocation -const image = createImage('photo.jpeg') +const image = createImage("photo.jpeg"); -console.log(image) +console.log(image); diff --git a/07-creational-design-patterns/02-factory-dynamic-class/image.js b/07-creational-design-patterns/02-factory-dynamic-class/image.js index 8c48190..eff7444 100644 --- a/07-creational-design-patterns/02-factory-dynamic-class/image.js +++ b/07-creational-design-patterns/02-factory-dynamic-class/image.js @@ -1,5 +1,5 @@ export class Image { constructor(path) { - this.path = path + this.path = path; } } diff --git a/07-creational-design-patterns/02-factory-dynamic-class/imageGif.js b/07-creational-design-patterns/02-factory-dynamic-class/imageGif.js index 5cf8458..c04adeb 100644 --- a/07-creational-design-patterns/02-factory-dynamic-class/imageGif.js +++ b/07-creational-design-patterns/02-factory-dynamic-class/imageGif.js @@ -1,11 +1,11 @@ -import { Image } from './image.js' -import { gifRgx } from './index.js' +import { Image } from "./image.js"; +import { gifRgx } from "./index.js"; export class ImageGif extends Image { constructor(path) { if (!path.match(gifRgx)) { - throw new Error(`${path} is not a GIF image`) + throw new Error(`${path} is not a GIF image`); } - super(path) + super(path); } } diff --git a/07-creational-design-patterns/02-factory-dynamic-class/imageJpeg.js b/07-creational-design-patterns/02-factory-dynamic-class/imageJpeg.js index 9f0cf84..ec18200 100644 --- a/07-creational-design-patterns/02-factory-dynamic-class/imageJpeg.js +++ b/07-creational-design-patterns/02-factory-dynamic-class/imageJpeg.js @@ -1,11 +1,11 @@ -import { Image } from './image.js' -import { jpgRgx } from './index.js' +import { Image } from "./image.js"; +import { jpgRgx } from "./index.js"; export class ImageJpeg extends Image { constructor(path) { if (!path.match(jpgRgx)) { - throw new Error(`${path} is not a JPEG image`) + throw new Error(`${path} is not a JPEG image`); } - super(path) + super(path); } } diff --git a/07-creational-design-patterns/02-factory-dynamic-class/imagePng.js b/07-creational-design-patterns/02-factory-dynamic-class/imagePng.js index c89dc4a..5c9f961 100644 --- a/07-creational-design-patterns/02-factory-dynamic-class/imagePng.js +++ b/07-creational-design-patterns/02-factory-dynamic-class/imagePng.js @@ -1,11 +1,11 @@ -import { Image } from './image.js' -import { pngRgx } from './index.js' +import { Image } from "./image.js"; +import { pngRgx } from "./index.js"; export class ImagePng extends Image { constructor(path) { if (!path.match(pngRgx)) { - throw new Error(`${path} is not a PNG image`) + throw new Error(`${path} is not a PNG image`); } - super(path) + super(path); } } diff --git a/07-creational-design-patterns/02-factory-dynamic-class/index.js b/07-creational-design-patterns/02-factory-dynamic-class/index.js index a5c521c..4799524 100644 --- a/07-creational-design-patterns/02-factory-dynamic-class/index.js +++ b/07-creational-design-patterns/02-factory-dynamic-class/index.js @@ -1,26 +1,26 @@ -import { ImageGif } from './imageGif.js' -import { ImageJpeg } from './imageJpeg.js' -import { ImagePng } from './imagePng.js' +import { ImageGif } from "./imageGif.js"; +import { ImageJpeg } from "./imageJpeg.js"; +import { ImagePng } from "./imagePng.js"; -export const jpgRgx = /\.jpe?g$/ -export const gifRgx = /\.gif$/ -export const pngRgx = /\.png$/ +export const jpgRgx = /\.jpe?g$/; +export const gifRgx = /\.gif$/; +export const pngRgx = /\.png$/; function createImage(name) { if (name.match(jpgRgx)) { - return new ImageJpeg(name) + return new ImageJpeg(name); } if (name.match(gifRgx)) { - return new ImageGif(name) + return new ImageGif(name); } if (name.match(pngRgx)) { - return new ImagePng(name) + return new ImagePng(name); } - throw new Error('Unsupported format') + throw new Error("Unsupported format"); } -const image1 = createImage('photo.jpg') -const image2 = createImage('photo.gif') -const image3 = createImage('photo.png') +const image1 = createImage("photo.jpg"); +const image2 = createImage("photo.gif"); +const image3 = createImage("photo.png"); -console.log(image1, image2, image3) +console.log(image1, image2, image3); diff --git a/07-creational-design-patterns/03-factory-encapsulation/index.js b/07-creational-design-patterns/03-factory-encapsulation/index.js index 56ffe76..fab80bc 100644 --- a/07-creational-design-patterns/03-factory-encapsulation/index.js +++ b/07-creational-design-patterns/03-factory-encapsulation/index.js @@ -1,22 +1,22 @@ function createPerson(name) { - const privateProperties = {} + const privateProperties = {}; const person = { setName(name) { if (!name) { - throw new Error('A person must have a name') + throw new Error("A person must have a name"); } - privateProperties.name = name + privateProperties.name = name; }, getName() { - return privateProperties.name + return privateProperties.name; }, - } + }; - person.setName(name) - return person + person.setName(name); + return person; } -const person = createPerson('James Joyce') +const person = createPerson("James Joyce"); -console.log(person.getName(), person) +console.log(person.getName(), person); diff --git a/07-creational-design-patterns/04-factory-example-profiler/index.js b/07-creational-design-patterns/04-factory-example-profiler/index.js index f2c0fde..03b2594 100644 --- a/07-creational-design-patterns/04-factory-example-profiler/index.js +++ b/07-creational-design-patterns/04-factory-example-profiler/index.js @@ -1,22 +1,22 @@ -import { createProfiler } from './profiler.js' +import { createProfiler } from "./profiler.js"; function getAllFactors(n) { - let intNumber = n - const profiler = createProfiler(`Finding all factors of ${intNumber}`) + let intNumber = n; + const profiler = createProfiler(`Finding all factors of ${intNumber}`); - profiler.start() - const factors = [] + profiler.start(); + const factors = []; for (let factor = 2; factor <= intNumber; factor++) { while (intNumber % factor === 0) { - factors.push(factor) - intNumber /= factor + factors.push(factor); + intNumber /= factor; } } - profiler.end() + profiler.end(); - return factors + return factors; } -const myNumber = process.argv[2] -const myFactors = getAllFactors(myNumber) -console.log(`Factors of ${myNumber} are: `, myFactors) +const myNumber = process.argv[2]; +const myFactors = getAllFactors(myNumber); +console.log(`Factors of ${myNumber} are: `, myFactors); diff --git a/07-creational-design-patterns/04-factory-example-profiler/profiler.js b/07-creational-design-patterns/04-factory-example-profiler/profiler.js index c42ee89..0775f23 100644 --- a/07-creational-design-patterns/04-factory-example-profiler/profiler.js +++ b/07-creational-design-patterns/04-factory-example-profiler/profiler.js @@ -1,31 +1,31 @@ class Profiler { constructor(label) { - this.label = label - this.lastTime = null + this.label = label; + this.lastTime = null; } start() { - this.lastTime = process.hrtime() + this.lastTime = process.hrtime(); } end() { - const diff = process.hrtime(this.lastTime) + const diff = process.hrtime(this.lastTime); console.log( `Timer "${this.label}" took ${diff[0]} seconds ` + - `and ${diff[1]} nanoseconds.` - ) + `and ${diff[1]} nanoseconds.`, + ); } } const noopProfiler = { start() {}, end() {}, -} +}; export function createProfiler(label) { - if (process.env.NODE_ENV === 'production') { - return noopProfiler + if (process.env.NODE_ENV === "production") { + return noopProfiler; } - return new Profiler(label) + return new Profiler(label); } diff --git a/07-creational-design-patterns/05-builder-boat-builder/boat.js b/07-creational-design-patterns/05-builder-boat-builder/boat.js index 502010d..c80ea3e 100644 --- a/07-creational-design-patterns/05-builder-boat-builder/boat.js +++ b/07-creational-design-patterns/05-builder-boat-builder/boat.js @@ -1,43 +1,43 @@ class Boat { constructor(config) { - this.hasMotor = Boolean(config.hasMotor) - this.motorCount = config.motorCount || 0 - this.motorBrand = config.motorBrand || '' - this.motorModel = config.motorModel || '' - this.hasSails = Boolean(config.hasSails) - this.sailsCount = config.sailsCount || 0 - this.sailsMaterial = config.sailsMaterial || '' - this.sailsColor = config.sailsColor || '' - this.hullColor = config.hullColor || '' - this.hasCabin = Boolean(config.hasCabin) + this.hasMotor = Boolean(config.hasMotor); + this.motorCount = config.motorCount || 0; + this.motorBrand = config.motorBrand || ""; + this.motorModel = config.motorModel || ""; + this.hasSails = Boolean(config.hasSails); + this.sailsCount = config.sailsCount || 0; + this.sailsMaterial = config.sailsMaterial || ""; + this.sailsColor = config.sailsColor || ""; + this.hullColor = config.hullColor || ""; + this.hasCabin = Boolean(config.hasCabin); } } export class BoatBuilder { withMotors(count, brand, model) { - this.hasMotor = true - this.motorCount = count - this.motorBrand = brand - this.motorModel = model - return this + this.hasMotor = true; + this.motorCount = count; + this.motorBrand = brand; + this.motorModel = model; + return this; } withSails(count, material, color) { - this.hasSails = true - this.sailsCount = count - this.sailsMaterial = material - this.sailsColor = color - return this + this.hasSails = true; + this.sailsCount = count; + this.sailsMaterial = material; + this.sailsColor = color; + return this; } hullColor(color) { - this.hullColor = color - return this + this.hullColor = color; + return this; } withCabin() { - this.hasCabin = true - return this + this.hasCabin = true; + return this; } build() { @@ -52,6 +52,6 @@ export class BoatBuilder { sailsColor: this.sailsColor, hullColor: this.hullColor, hasCabin: this.hasCabin, - }) + }); } } diff --git a/07-creational-design-patterns/05-builder-boat-builder/index.js b/07-creational-design-patterns/05-builder-boat-builder/index.js index 555d5f5..d01bdd1 100644 --- a/07-creational-design-patterns/05-builder-boat-builder/index.js +++ b/07-creational-design-patterns/05-builder-boat-builder/index.js @@ -1,10 +1,10 @@ -import { BoatBuilder } from './boat.js' +import { BoatBuilder } from "./boat.js"; const myBoat = new BoatBuilder() - .withMotors(2, 'Best Motor Co. ', 'OM123') - .withSails(1, 'fabric', 'white') + .withMotors(2, "Best Motor Co. ", "OM123") + .withSails(1, "fabric", "white") .withCabin() - .hullColor('blue') - .build() + .hullColor("blue") + .build(); -console.log(myBoat) +console.log(myBoat); diff --git a/07-creational-design-patterns/05-builder-boat-builder/nested-properties-config.ts b/07-creational-design-patterns/05-builder-boat-builder/nested-properties-config.ts index 975544c..bf87744 100644 --- a/07-creational-design-patterns/05-builder-boat-builder/nested-properties-config.ts +++ b/07-creational-design-patterns/05-builder-boat-builder/nested-properties-config.ts @@ -1,58 +1,58 @@ export type BoatConfig = { motor?: { - count: number - brand: string - model: string - } + count: number; + brand: string; + model: string; + }; sails?: { - count: number - material: string - color: string - } - hullColor: string - hasCabin: boolean -} + count: number; + material: string; + color: string; + }; + hullColor: string; + hasCabin: boolean; +}; class Boat { - hasMotor: boolean - motorCount: number - motorBrand: string - motorModel: string - hasSails: boolean - sailsCount: number - sailsMaterial: string - sailsColor: string - hullColor: string - hasCabin: boolean + hasMotor: boolean; + motorCount: number; + motorBrand: string; + motorModel: string; + hasSails: boolean; + sailsCount: number; + sailsMaterial: string; + sailsColor: string; + hullColor: string; + hasCabin: boolean; constructor(config: BoatConfig) { // in real life you'd use the config - this.hasMotor = Boolean(config.motor) - this.motorCount = config.motor?.count || 0 - this.motorBrand = config.motor?.brand || '' - this.motorModel = config.motor?.model || '' - this.hasSails = Boolean(config.sails) - this.sailsCount = config.sails?.count || 0 - this.sailsMaterial = config.sails?.material || '' - this.sailsColor = config.sails?.color || '' - this.hullColor = config.hullColor || '' - this.hasCabin = Boolean(config.hasCabin) + this.hasMotor = Boolean(config.motor); + this.motorCount = config.motor?.count || 0; + this.motorBrand = config.motor?.brand || ""; + this.motorModel = config.motor?.model || ""; + this.hasSails = Boolean(config.sails); + this.sailsCount = config.sails?.count || 0; + this.sailsMaterial = config.sails?.material || ""; + this.sailsColor = config.sails?.color || ""; + this.hullColor = config.hullColor || ""; + this.hasCabin = Boolean(config.hasCabin); } } const myBoat = new Boat({ motor: { count: 2, - brand: 'Best Motor Co. ', - model: 'OM123', + brand: "Best Motor Co. ", + model: "OM123", }, sails: { count: 1, - material: 'fabric', - color: 'white', + material: "fabric", + color: "white", }, - hullColor: 'blue', + hullColor: "blue", hasCabin: false, -}) +}); -console.log(myBoat) +console.log(myBoat); diff --git a/07-creational-design-patterns/06-builder-url-builder/index.js b/07-creational-design-patterns/06-builder-url-builder/index.js index ab49a40..1eff3d7 100644 --- a/07-creational-design-patterns/06-builder-url-builder/index.js +++ b/07-creational-design-patterns/06-builder-url-builder/index.js @@ -1,9 +1,9 @@ -import { UrlBuilder } from './urlBuilder.js' +import { UrlBuilder } from "./urlBuilder.js"; const url = new UrlBuilder() - .setProtocol('https') - .setAuthentication('user', 'pass') - .setHostname('example.com') - .build() + .setProtocol("https") + .setAuthentication("user", "pass") + .setHostname("example.com") + .build(); -console.log(url.toString()) +console.log(url.toString()); diff --git a/07-creational-design-patterns/06-builder-url-builder/url.js b/07-creational-design-patterns/06-builder-url-builder/url.js index c83817f..7003453 100644 --- a/07-creational-design-patterns/06-builder-url-builder/url.js +++ b/07-creational-design-patterns/06-builder-url-builder/url.js @@ -7,45 +7,45 @@ export class Url { port, pathname, search, - hash + hash, ) { - this.protocol = protocol - this.username = username - this.password = password - this.hostname = hostname - this.port = port - this.pathname = pathname - this.search = search - this.hash = hash + this.protocol = protocol; + this.username = username; + this.password = password; + this.hostname = hostname; + this.port = port; + this.pathname = pathname; + this.search = search; + this.hash = hash; - this.validate() + this.validate(); } validate() { if (!(this.protocol && this.hostname)) { - throw new Error('Must specify at least a protocol and a hostname') + throw new Error("Must specify at least a protocol and a hostname"); } } toString() { - let url = '' - url += `${this.protocol}://` + let url = ""; + url += `${this.protocol}://`; if (this.username && this.password) { - url += `${this.username}:${this.password}@` + url += `${this.username}:${this.password}@`; } - url += this.hostname + url += this.hostname; if (this.port) { - url += this.port + url += this.port; } if (this.pathname) { - url += this.pathname + url += this.pathname; } if (this.search) { - url += `?${this.search}` + url += `?${this.search}`; } if (this.hash) { - url += `#${this.hash}` + url += `#${this.hash}`; } - return url + return url; } } diff --git a/07-creational-design-patterns/06-builder-url-builder/urlBuilder.js b/07-creational-design-patterns/06-builder-url-builder/urlBuilder.js index e5005d9..2830739 100644 --- a/07-creational-design-patterns/06-builder-url-builder/urlBuilder.js +++ b/07-creational-design-patterns/06-builder-url-builder/urlBuilder.js @@ -1,40 +1,40 @@ -import { Url } from './url.js' +import { Url } from "./url.js"; export class UrlBuilder { setProtocol(protocol) { - this.protocol = protocol - return this + this.protocol = protocol; + return this; } setAuthentication(username, password) { - this.username = username - this.password = password - return this + this.username = username; + this.password = password; + return this; } setHostname(hostname) { - this.hostname = hostname - return this + this.hostname = hostname; + return this; } setPort(port) { - this.port = port - return this + this.port = port; + return this; } setPathname(pathname) { - this.pathname = pathname - return this + this.pathname = pathname; + return this; } setSearch(search) { - this.search = search - return this + this.search = search; + return this; } setHash(hash) { - this.hash = hash - return this + this.hash = hash; + return this; } build() { @@ -46,7 +46,7 @@ export class UrlBuilder { this.port, this.pathname, this.search, - this.hash - ) + this.hash, + ); } } diff --git a/07-creational-design-patterns/06b-builder-in-the-wild/cli.js b/07-creational-design-patterns/06b-builder-in-the-wild/cli.js index 8334fcc..a268bbd 100644 --- a/07-creational-design-patterns/06b-builder-in-the-wild/cli.js +++ b/07-creational-design-patterns/06b-builder-in-the-wild/cli.js @@ -1,17 +1,17 @@ -import { Command } from 'commander' // v14.0.0 +import { Command } from "commander"; // v14.0.0 -const program = new Command() +const program = new Command(); program - .name('string-util') - .description('CLI to some JavaScript string utilities') - .version('0.8.0') - .command('split') - .description('Split a string into substrings and display as an array') - .argument('', 'string to split') - .option('--first', 'display just the first substring') - .option('-s, --separator ', 'separator character', ',') + .name("string-util") + .description("CLI to some JavaScript string utilities") + .version("0.8.0") + .command("split") + .description("Split a string into substrings and display as an array") + .argument("", "string to split") + .option("--first", "display just the first substring") + .option("-s, --separator ", "separator character", ",") .action((str, options) => { - const limit = options.first ? 1 : undefined - console.log(str.split(options.separator, limit)) + const limit = options.first ? 1 : undefined; + console.log(str.split(options.separator, limit)); }) - .parse() + .parse(); diff --git a/07-creational-design-patterns/06b-builder-in-the-wild/superagent.js b/07-creational-design-patterns/06b-builder-in-the-wild/superagent.js index 78fea38..2347d6f 100644 --- a/07-creational-design-patterns/06b-builder-in-the-wild/superagent.js +++ b/07-creational-design-patterns/06b-builder-in-the-wild/superagent.js @@ -1,10 +1,10 @@ -import superagent from 'superagent' // v10.1.1 +import superagent from "superagent"; // v10.1.1 superagent - .post('https://jsonplaceholder.typicode.com/posts') - .send({ name: 'John Doe', role: 'user' }) - .set('accept', 'json') - .then(response => { + .post("https://jsonplaceholder.typicode.com/posts") + .send({ name: "John Doe", role: "user" }) + .set("accept", "json") + .then((response) => { // deal with the response - console.log(response.body) - }) + console.log(response.body); + }); diff --git a/07-creational-design-patterns/07-revealing-constructor-immutable-buffer/immutableBuffer.js b/07-creational-design-patterns/07-revealing-constructor-immutable-buffer/immutableBuffer.js index d08a698..f78c068 100644 --- a/07-creational-design-patterns/07-revealing-constructor-immutable-buffer/immutableBuffer.js +++ b/07-creational-design-patterns/07-revealing-constructor-immutable-buffer/immutableBuffer.js @@ -1,21 +1,21 @@ -const MODIFIER_NAMES = ['swap', 'write', 'fill'] +const MODIFIER_NAMES = ["swap", "write", "fill"]; export class ImmutableBuffer { constructor(size, executor) { - const buffer = Buffer.alloc(size) - const modifiers = {} + const buffer = Buffer.alloc(size); + const modifiers = {}; for (const prop in buffer) { - if (typeof buffer[prop] !== 'function') { - continue + if (typeof buffer[prop] !== "function") { + continue; } - if (MODIFIER_NAMES.some(m => prop.startsWith(m))) { - modifiers[prop] = buffer[prop].bind(buffer) + if (MODIFIER_NAMES.some((m) => prop.startsWith(m))) { + modifiers[prop] = buffer[prop].bind(buffer); } else { - this[prop] = buffer[prop].bind(buffer) + this[prop] = buffer[prop].bind(buffer); } } - executor(modifiers) + executor(modifiers); } } diff --git a/07-creational-design-patterns/07-revealing-constructor-immutable-buffer/index.js b/07-creational-design-patterns/07-revealing-constructor-immutable-buffer/index.js index f9e1df5..f88d48b 100644 --- a/07-creational-design-patterns/07-revealing-constructor-immutable-buffer/index.js +++ b/07-creational-design-patterns/07-revealing-constructor-immutable-buffer/index.js @@ -1,11 +1,11 @@ -import { ImmutableBuffer } from './immutableBuffer.js' +import { ImmutableBuffer } from "./immutableBuffer.js"; -const hello = 'Hello!' +const hello = "Hello!"; const immutable = new ImmutableBuffer(hello.length, ({ write }) => { - write(hello) -}) + write(hello); +}); -console.log(String.fromCharCode(immutable.readInt8(0))) +console.log(String.fromCharCode(immutable.readInt8(0))); // the following line will throw // "TypeError: immutable.write is not a function" diff --git a/07-creational-design-patterns/08-singleton-false-uniqueness/index.js b/07-creational-design-patterns/08-singleton-false-uniqueness/index.js index 4c2fae6..a47774d 100644 --- a/07-creational-design-patterns/08-singleton-false-uniqueness/index.js +++ b/07-creational-design-patterns/08-singleton-false-uniqueness/index.js @@ -1,7 +1,7 @@ -import { getDbInstance as getDbFromA } from 'package-a' -import { getDbInstance as getDbFromB } from 'package-b' +import { getDbInstance as getDbFromA } from "package-a"; +import { getDbInstance as getDbFromB } from "package-b"; -const isSame = getDbFromA() === getDbFromB() +const isSame = getDbFromA() === getDbFromB(); console.log( - `Is the db instance in package-a the same as package-b? ${isSame ? 'YES' : 'NO'}` -) + `Is the db instance in package-a the same as package-b? ${isSame ? "YES" : "NO"}`, +); diff --git a/07-creational-design-patterns/09-singleton-dependencies/blog.js b/07-creational-design-patterns/09-singleton-dependencies/blog.js index cad7caf..342f4ca 100644 --- a/07-creational-design-patterns/09-singleton-dependencies/blog.js +++ b/07-creational-design-patterns/09-singleton-dependencies/blog.js @@ -1,4 +1,4 @@ -import { db } from './db.js' +import { db } from "./db.js"; export class Blog { initialize() { @@ -7,22 +7,22 @@ export class Blog { title TEXT NOT NULL, content TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP - );` + );`; - return db.run(initQuery) + return db.run(initQuery); } createPost(id, title, content, createdAt) { return db.run( - 'INSERT INTO posts VALUES (?, ?, ?, ?)', + "INSERT INTO posts VALUES (?, ?, ?, ?)", id, title, content, - createdAt - ) + createdAt, + ); } getAllPosts() { - return db.all('SELECT * FROM posts ORDER BY created_at DESC') + return db.all("SELECT * FROM posts ORDER BY created_at DESC"); } } diff --git a/07-creational-design-patterns/09-singleton-dependencies/db.js b/07-creational-design-patterns/09-singleton-dependencies/db.js index 721aef2..f01df3a 100644 --- a/07-creational-design-patterns/09-singleton-dependencies/db.js +++ b/07-creational-design-patterns/09-singleton-dependencies/db.js @@ -1,8 +1,8 @@ -import { join } from 'node:path' -import { open } from 'sqlite' -import sqlite3 from 'sqlite3' +import { join } from "node:path"; +import { open } from "sqlite"; +import sqlite3 from "sqlite3"; export const db = await open({ - filename: join(import.meta.dirname, 'data.sqlite'), + filename: join(import.meta.dirname, "data.sqlite"), driver: sqlite3.Database, -}) +}); diff --git a/07-creational-design-patterns/09-singleton-dependencies/import-posts.js b/07-creational-design-patterns/09-singleton-dependencies/import-posts.js index e12e0e9..7f0223f 100644 --- a/07-creational-design-patterns/09-singleton-dependencies/import-posts.js +++ b/07-creational-design-patterns/09-singleton-dependencies/import-posts.js @@ -1,37 +1,37 @@ -import { Blog } from './blog.js' +import { Blog } from "./blog.js"; const posts = [ { - id: 'my-first-post', - title: 'My first post', - content: 'Hello World!\nThis is my first post', + id: "my-first-post", + title: "My first post", + content: "Hello World!\nThis is my first post", // biome-ignore lint/style/useNamingConvention: db mapping - created_at: new Date('2020-02-03'), + created_at: new Date("2020-02-03"), }, { - id: 'iterator-patterns', - title: 'Node.js iterator patterns', + id: "iterator-patterns", + title: "Node.js iterator patterns", content: "Let's talk about some iterator patterns in Node.js\n\n...", // biome-ignore lint/style/useNamingConvention: db mapping - created_at: new Date('2020-02-06'), + created_at: new Date("2020-02-06"), }, { - id: 'dependency-injection', - title: 'Dependency injection in Node.js', + id: "dependency-injection", + title: "Dependency injection in Node.js", content: - 'Today we will discuss about dependency injection in Node.js\n\n...', + "Today we will discuss about dependency injection in Node.js\n\n...", // biome-ignore lint/style/useNamingConvention: db mapping - created_at: new Date('2020-02-29'), + created_at: new Date("2020-02-29"), }, // ... -] +]; -const blog = new Blog() -await blog.initialize() +const blog = new Blog(); +await blog.initialize(); await Promise.all( - posts.map(post => - blog.createPost(post.id, post.title, post.content, post.created_at) - ) -) -console.log('All posts imported') + posts.map((post) => + blog.createPost(post.id, post.title, post.content, post.created_at), + ), +); +console.log("All posts imported"); diff --git a/07-creational-design-patterns/09-singleton-dependencies/index.js b/07-creational-design-patterns/09-singleton-dependencies/index.js index dc3a0f9..d000133 100644 --- a/07-creational-design-patterns/09-singleton-dependencies/index.js +++ b/07-creational-design-patterns/09-singleton-dependencies/index.js @@ -1,18 +1,18 @@ -import { Blog } from './blog.js' +import { Blog } from "./blog.js"; -const blog = new Blog() -await blog.initialize() -const posts = await blog.getAllPosts() +const blog = new Blog(); +await blog.initialize(); +const posts = await blog.getAllPosts(); if (posts.length === 0) { console.log( - 'No post available. Run `node import-posts.js`' + - ' to load some sample posts' - ) + "No post available. Run `node import-posts.js`" + + " to load some sample posts", + ); } for (const post of posts) { - console.log(post.title) - console.log('-'.repeat(post.title.length)) - console.log(`Published on ${new Date(post.created_at).toISOString()}`) - console.log(post.content) + console.log(post.title); + console.log("-".repeat(post.title.length)); + console.log(`Published on ${new Date(post.created_at).toISOString()}`); + console.log(post.content); } diff --git a/07-creational-design-patterns/10-dependency-injection/blog.js b/07-creational-design-patterns/10-dependency-injection/blog.js index e1947d9..b34e0c5 100644 --- a/07-creational-design-patterns/10-dependency-injection/blog.js +++ b/07-creational-design-patterns/10-dependency-injection/blog.js @@ -1,6 +1,6 @@ export class Blog { constructor(db) { - this.db = db + this.db = db; } initialize() { @@ -9,22 +9,22 @@ export class Blog { title TEXT NOT NULL, content TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP - );` + );`; - return this.db.run(initQuery) + return this.db.run(initQuery); } createPost(id, title, content, createdAt) { return this.db.run( - 'INSERT INTO posts VALUES (?, ?, ?, ?)', + "INSERT INTO posts VALUES (?, ?, ?, ?)", id, title, content, - createdAt - ) + createdAt, + ); } getAllPosts() { - return this.db.all('SELECT * FROM posts ORDER BY created_at DESC') + return this.db.all("SELECT * FROM posts ORDER BY created_at DESC"); } } diff --git a/07-creational-design-patterns/10-dependency-injection/db.js b/07-creational-design-patterns/10-dependency-injection/db.js index 859cba4..6a1880f 100644 --- a/07-creational-design-patterns/10-dependency-injection/db.js +++ b/07-creational-design-patterns/10-dependency-injection/db.js @@ -1,9 +1,9 @@ -import { open } from 'sqlite' -import sqlite3 from 'sqlite3' +import { open } from "sqlite"; +import sqlite3 from "sqlite3"; export function createDb(filename) { return open({ filename, driver: sqlite3.Database, - }) + }); } diff --git a/07-creational-design-patterns/10-dependency-injection/import-posts.js b/07-creational-design-patterns/10-dependency-injection/import-posts.js index 969a8ab..82c78a7 100644 --- a/07-creational-design-patterns/10-dependency-injection/import-posts.js +++ b/07-creational-design-patterns/10-dependency-injection/import-posts.js @@ -1,40 +1,40 @@ -import { join } from 'node:path' -import { Blog } from './blog.js' -import { createDb } from './db.js' +import { join } from "node:path"; +import { Blog } from "./blog.js"; +import { createDb } from "./db.js"; const posts = [ { - id: 'my-first-post', - title: 'My first post', - content: 'Hello World!\nThis is my first post', + id: "my-first-post", + title: "My first post", + content: "Hello World!\nThis is my first post", // biome-ignore lint/style/useNamingConvention: db mapping - created_at: new Date('2020-02-03'), + created_at: new Date("2020-02-03"), }, { - id: 'iterator-patterns', - title: 'Node.js iterator patterns', + id: "iterator-patterns", + title: "Node.js iterator patterns", content: "Let's talk about some iterator patterns in Node.js\n\n...", // biome-ignore lint/style/useNamingConvention: db mapping - created_at: new Date('2020-02-06'), + created_at: new Date("2020-02-06"), }, { - id: 'dependency-injection', - title: 'Dependency injection in Node.js', + id: "dependency-injection", + title: "Dependency injection in Node.js", content: - 'Today we will discuss about dependency injection in Node.js\n\n...', + "Today we will discuss about dependency injection in Node.js\n\n...", // biome-ignore lint/style/useNamingConvention: db mapping - created_at: new Date('2020-02-29'), + created_at: new Date("2020-02-29"), }, // ... -] +]; -const db = await createDb(join(import.meta.dirname, 'data.sqlite')) -const blog = new Blog(db) -await blog.initialize() +const db = await createDb(join(import.meta.dirname, "data.sqlite")); +const blog = new Blog(db); +await blog.initialize(); await Promise.all( - posts.map(post => - blog.createPost(post.id, post.title, post.content, post.created_at) - ) -) -console.log('All posts imported') + posts.map((post) => + blog.createPost(post.id, post.title, post.content, post.created_at), + ), +); +console.log("All posts imported"); diff --git a/07-creational-design-patterns/10-dependency-injection/index.js b/07-creational-design-patterns/10-dependency-injection/index.js index 9eb9074..15cd498 100644 --- a/07-creational-design-patterns/10-dependency-injection/index.js +++ b/07-creational-design-patterns/10-dependency-injection/index.js @@ -1,21 +1,21 @@ -import { join } from 'node:path' -import { Blog } from './blog.js' -import { createDb } from './db.js' +import { join } from "node:path"; +import { Blog } from "./blog.js"; +import { createDb } from "./db.js"; -const db = await createDb(join(import.meta.dirname, 'data.sqlite')) -const blog = new Blog(db) -await blog.initialize() -const posts = await blog.getAllPosts() +const db = await createDb(join(import.meta.dirname, "data.sqlite")); +const blog = new Blog(db); +await blog.initialize(); +const posts = await blog.getAllPosts(); if (posts.length === 0) { console.log( - 'No post available. Run `node import-posts.js`' + - ' to load some sample posts' - ) + "No post available. Run `node import-posts.js`" + + " to load some sample posts", + ); } for (const post of posts) { - console.log(post.title) - console.log('-'.repeat(post.title.length)) - console.log(`Published on ${new Date(post.created_at).toISOString()}`) - console.log(post.content) + console.log(post.title); + console.log("-".repeat(post.title.length)); + console.log(`Published on ${new Date(post.created_at).toISOString()}`); + console.log(post.content); } diff --git a/08-structural-design-patterns/01-proxy-composition/index.js b/08-structural-design-patterns/01-proxy-composition/index.js index 94e72ff..e209073 100644 --- a/08-structural-design-patterns/01-proxy-composition/index.js +++ b/08-structural-design-patterns/01-proxy-composition/index.js @@ -1,93 +1,93 @@ class StackCalculator { constructor() { - this.stack = [] + this.stack = []; } putValue(value) { - this.stack.push(value) + this.stack.push(value); } getValue() { - return this.stack.pop() + return this.stack.pop(); } peekValue() { - return this.stack[this.stack.length - 1] + return this.stack[this.stack.length - 1]; } clear() { - this.stack = [] + this.stack = []; } divide() { - const divisor = this.getValue() - const dividend = this.getValue() - const result = dividend / divisor - this.putValue(result) - return result + const divisor = this.getValue(); + const dividend = this.getValue(); + const result = dividend / divisor; + this.putValue(result); + return result; } multiply() { - const multiplicand = this.getValue() - const multiplier = this.getValue() - const result = multiplier * multiplicand - this.putValue(result) - return result + const multiplicand = this.getValue(); + const multiplier = this.getValue(); + const result = multiplier * multiplicand; + this.putValue(result); + return result; } } class SafeCalculator { constructor(calculator) { - this.calculator = calculator + this.calculator = calculator; } // proxied method divide() { // additional validation logic - const divisor = this.calculator.peekValue() + const divisor = this.calculator.peekValue(); if (divisor === 0) { - throw new Error('Division by 0') + throw new Error("Division by 0"); } // if valid delegates to the subject - return this.calculator.divide() + return this.calculator.divide(); } // delegated methods putValue(value) { - return this.calculator.putValue(value) + return this.calculator.putValue(value); } getValue() { - return this.calculator.getValue() + return this.calculator.getValue(); } peekValue() { - return this.calculator.peekValue() + return this.calculator.peekValue(); } clear() { - return this.calculator.clear() + return this.calculator.clear(); } multiply() { - return this.calculator.multiply() + return this.calculator.multiply(); } } -const calculator = new StackCalculator() -const safeCalculator = new SafeCalculator(calculator) +const calculator = new StackCalculator(); +const safeCalculator = new SafeCalculator(calculator); -calculator.putValue(3) -calculator.putValue(2) -console.log(calculator.multiply()) // 3*2 = 6 +calculator.putValue(3); +calculator.putValue(2); +console.log(calculator.multiply()); // 3*2 = 6 -safeCalculator.putValue(2) -console.log(safeCalculator.multiply()) // 6*2 = 12 +safeCalculator.putValue(2); +console.log(safeCalculator.multiply()); // 6*2 = 12 -calculator.putValue(0) -console.log(calculator.divide()) // 12/0 = Infinity +calculator.putValue(0); +console.log(calculator.divide()); // 12/0 = Infinity -safeCalculator.clear() -safeCalculator.putValue(4) -safeCalculator.putValue(0) -console.log(safeCalculator.divide()) // 4/0 -> Error('Division by 0') +safeCalculator.clear(); +safeCalculator.putValue(4); +safeCalculator.putValue(0); +console.log(safeCalculator.divide()); // 4/0 -> Error('Division by 0') diff --git a/08-structural-design-patterns/02-proxy-object-literal/index.js b/08-structural-design-patterns/02-proxy-object-literal/index.js index f58c596..35716b6 100644 --- a/08-structural-design-patterns/02-proxy-object-literal/index.js +++ b/08-structural-design-patterns/02-proxy-object-literal/index.js @@ -1,38 +1,38 @@ class StackCalculator { constructor() { - this.stack = [] + this.stack = []; } putValue(value) { - this.stack.push(value) + this.stack.push(value); } getValue() { - return this.stack.pop() + return this.stack.pop(); } peekValue() { - return this.stack[this.stack.length - 1] + return this.stack[this.stack.length - 1]; } clear() { - this.stack = [] + this.stack = []; } divide() { - const divisor = this.getValue() - const dividend = this.getValue() - const result = dividend / divisor - this.putValue(result) - return result + const divisor = this.getValue(); + const dividend = this.getValue(); + const result = dividend / divisor; + this.putValue(result); + return result; } multiply() { - const multiplicand = this.getValue() - const multiplier = this.getValue() - const result = multiplier * multiplicand - this.putValue(result) - return result + const multiplicand = this.getValue(); + const multiplier = this.getValue(); + const result = multiplier * multiplicand; + this.putValue(result); + return result; } } @@ -41,46 +41,46 @@ function createSafeCalculator(calculator) { // proxied method divide() { // additional validation logic - const divisor = calculator.peekValue() + const divisor = calculator.peekValue(); if (divisor === 0) { - throw new Error('Division by 0') + throw new Error("Division by 0"); } // if valid delegates to the subject - return calculator.divide() + return calculator.divide(); }, // delegated methods putValue(value) { - return calculator.putValue(value) + return calculator.putValue(value); }, getValue() { - return calculator.getValue() + return calculator.getValue(); }, peekValue() { - return calculator.peekValue() + return calculator.peekValue(); }, clear() { - return calculator.clear() + return calculator.clear(); }, multiply() { - return calculator.multiply() + return calculator.multiply(); }, - } + }; } -const calculator = new StackCalculator() -const safeCalculator = createSafeCalculator(calculator) +const calculator = new StackCalculator(); +const safeCalculator = createSafeCalculator(calculator); -calculator.putValue(3) -calculator.putValue(2) -console.log(calculator.multiply()) // 3*2 = 6 +calculator.putValue(3); +calculator.putValue(2); +console.log(calculator.multiply()); // 3*2 = 6 -safeCalculator.putValue(2) -console.log(safeCalculator.multiply()) // 6*2 = 12 +safeCalculator.putValue(2); +console.log(safeCalculator.multiply()); // 6*2 = 12 -calculator.putValue(0) -console.log(calculator.divide()) // 12/0 = Infinity +calculator.putValue(0); +console.log(calculator.divide()); // 12/0 = Infinity -safeCalculator.clear() -safeCalculator.putValue(4) -safeCalculator.putValue(0) -console.log(safeCalculator.divide()) // 4/0 -> Error('Division by 0') +safeCalculator.clear(); +safeCalculator.putValue(4); +safeCalculator.putValue(0); +console.log(safeCalculator.divide()); // 4/0 -> Error('Division by 0') diff --git a/08-structural-design-patterns/02-proxy-object-literal/removedDuplication.js b/08-structural-design-patterns/02-proxy-object-literal/removedDuplication.js index 38a7653..f038203 100644 --- a/08-structural-design-patterns/02-proxy-object-literal/removedDuplication.js +++ b/08-structural-design-patterns/02-proxy-object-literal/removedDuplication.js @@ -1,38 +1,38 @@ class StackCalculator { constructor() { - this.stack = [] + this.stack = []; } putValue(value) { - this.stack.push(value) + this.stack.push(value); } getValue() { - return this.stack.pop() + return this.stack.pop(); } peekValue() { - return this.stack[this.stack.length - 1] + return this.stack[this.stack.length - 1]; } clear() { - this.stack = [] + this.stack = []; } divide() { - const divisor = this.getValue() - const dividend = this.getValue() - const result = dividend / divisor - this.putValue(result) - return result + const divisor = this.getValue(); + const dividend = this.getValue(); + const result = dividend / divisor; + this.putValue(result); + return result; } multiply() { - const multiplicand = this.getValue() - const multiplier = this.getValue() - const result = multiplier * multiplicand - this.putValue(result) - return result + const multiplicand = this.getValue(); + const multiplier = this.getValue(); + const result = multiplier * multiplicand; + this.putValue(result); + return result; } } @@ -41,37 +41,37 @@ function createSafeCalculator(calculator) { // proxied method divide() { // additional validation logic - const divisor = calculator.peekValue() + const divisor = calculator.peekValue(); if (divisor === 0) { - throw new Error('Division by 0') + throw new Error("Division by 0"); } // if valid delegates to the subject - return calculator.divide() + return calculator.divide(); }, - } + }; // delegated methods - for (const fn of ['putValue', 'getValue', 'peekValue', 'clear', 'multiply']) { - safeCalculator[fn] = calculator[fn].bind(calculator) + for (const fn of ["putValue", "getValue", "peekValue", "clear", "multiply"]) { + safeCalculator[fn] = calculator[fn].bind(calculator); } - return safeCalculator + return safeCalculator; } -const calculator = new StackCalculator() -const safeCalculator = createSafeCalculator(calculator) +const calculator = new StackCalculator(); +const safeCalculator = createSafeCalculator(calculator); -calculator.putValue(3) -calculator.putValue(2) -console.log(calculator.multiply()) // 3*2 = 6 +calculator.putValue(3); +calculator.putValue(2); +console.log(calculator.multiply()); // 3*2 = 6 -safeCalculator.putValue(2) -console.log(safeCalculator.multiply()) // 6*2 = 12 +safeCalculator.putValue(2); +console.log(safeCalculator.multiply()); // 6*2 = 12 -calculator.putValue(0) -console.log(calculator.divide()) // 12/0 = Infinity +calculator.putValue(0); +console.log(calculator.divide()); // 12/0 = Infinity -safeCalculator.clear() -safeCalculator.putValue(4) -safeCalculator.putValue(0) -console.log(safeCalculator.divide()) // 4/0 -> Error('Division by 0') +safeCalculator.clear(); +safeCalculator.putValue(4); +safeCalculator.putValue(0); +console.log(safeCalculator.divide()); // 4/0 -> Error('Division by 0') diff --git a/08-structural-design-patterns/03-proxy-object-augmentation/index.js b/08-structural-design-patterns/03-proxy-object-augmentation/index.js index c1e1bab..b1f04bb 100644 --- a/08-structural-design-patterns/03-proxy-object-augmentation/index.js +++ b/08-structural-design-patterns/03-proxy-object-augmentation/index.js @@ -1,70 +1,70 @@ class StackCalculator { constructor() { - this.stack = [] + this.stack = []; } putValue(value) { - this.stack.push(value) + this.stack.push(value); } getValue() { - return this.stack.pop() + return this.stack.pop(); } peekValue() { - return this.stack[this.stack.length - 1] + return this.stack[this.stack.length - 1]; } clear() { - this.stack = [] + this.stack = []; } divide() { - const divisor = this.getValue() - const dividend = this.getValue() - const result = dividend / divisor - this.putValue(result) - return result + const divisor = this.getValue(); + const dividend = this.getValue(); + const result = dividend / divisor; + this.putValue(result); + return result; } multiply() { - const multiplicand = this.getValue() - const multiplier = this.getValue() - const result = multiplier * multiplicand - this.putValue(result) - return result + const multiplicand = this.getValue(); + const multiplier = this.getValue(); + const result = multiplier * multiplicand; + this.putValue(result); + return result; } } function patchToSafeCalculator(calculator) { - const divideOrig = calculator.divide + const divideOrig = calculator.divide; calculator.divide = () => { // additional validation logic - const divisor = calculator.peekValue() + const divisor = calculator.peekValue(); if (divisor === 0) { - throw new Error('Division by 0') + throw new Error("Division by 0"); } // if valid delegates to the subject - return divideOrig.apply(calculator) - } + return divideOrig.apply(calculator); + }; - return calculator + return calculator; } -const calculator = new StackCalculator() -const safeCalculator = patchToSafeCalculator(calculator) +const calculator = new StackCalculator(); +const safeCalculator = patchToSafeCalculator(calculator); -calculator.putValue(3) -calculator.putValue(2) -console.log(calculator.multiply()) // 3*2 = 6 +calculator.putValue(3); +calculator.putValue(2); +console.log(calculator.multiply()); // 3*2 = 6 -safeCalculator.putValue(2) -console.log(safeCalculator.multiply()) // 6*2 = 12 +safeCalculator.putValue(2); +console.log(safeCalculator.multiply()); // 6*2 = 12 // calculator.putValue(0) // console.log(calculator.divide()) // 12/0 -> Error('Division by 0') -safeCalculator.clear() -safeCalculator.putValue(4) -safeCalculator.putValue(0) -console.log(safeCalculator.divide()) // 4/0 -> Error('Division by 0') +safeCalculator.clear(); +safeCalculator.putValue(4); +safeCalculator.putValue(0); +console.log(safeCalculator.divide()); // 4/0 -> Error('Division by 0') diff --git a/08-structural-design-patterns/04-proxy-es2015/even-numbers.js b/08-structural-design-patterns/04-proxy-es2015/even-numbers.js index 1b11d2d..e6e5a6e 100644 --- a/08-structural-design-patterns/04-proxy-es2015/even-numbers.js +++ b/08-structural-design-patterns/04-proxy-es2015/even-numbers.js @@ -1,8 +1,8 @@ const evenNumbers = new Proxy([], { get: (_target, index) => index * 2, has: (_target, number) => number % 2 === 0, -}) +}); -console.log(2 in evenNumbers) // true -console.log(5 in evenNumbers) // false -console.log(evenNumbers[7]) // 14 +console.log(2 in evenNumbers); // true +console.log(5 in evenNumbers); // false +console.log(evenNumbers[7]); // 14 diff --git a/08-structural-design-patterns/04-proxy-es2015/safe-calculator.js b/08-structural-design-patterns/04-proxy-es2015/safe-calculator.js index 6ee2729..318666b 100644 --- a/08-structural-design-patterns/04-proxy-es2015/safe-calculator.js +++ b/08-structural-design-patterns/04-proxy-es2015/safe-calculator.js @@ -1,76 +1,76 @@ class StackCalculator { constructor() { - this.stack = [] + this.stack = []; } putValue(value) { - this.stack.push(value) + this.stack.push(value); } getValue() { - return this.stack.pop() + return this.stack.pop(); } peekValue() { - return this.stack[this.stack.length - 1] + return this.stack[this.stack.length - 1]; } clear() { - this.stack = [] + this.stack = []; } divide() { - const divisor = this.getValue() - const dividend = this.getValue() - const result = dividend / divisor - this.putValue(result) - return result + const divisor = this.getValue(); + const dividend = this.getValue(); + const result = dividend / divisor; + this.putValue(result); + return result; } multiply() { - const multiplicand = this.getValue() - const multiplier = this.getValue() - const result = multiplier * multiplicand - this.putValue(result) - return result + const multiplicand = this.getValue(); + const multiplier = this.getValue(); + const result = multiplier * multiplicand; + this.putValue(result); + return result; } } const safeCalculatorHandler = { get: (target, property) => { - if (property === 'divide') { + if (property === "divide") { // proxied method return () => { // additional validation logic - const divisor = target.peekValue() + const divisor = target.peekValue(); if (divisor === 0) { - throw new Error('Division by 0') + throw new Error("Division by 0"); } // if valid delegates to the subject - return target.divide() - } + return target.divide(); + }; } // delegated methods and properties - return target[property] + return target[property]; }, -} +}; -const calculator = new StackCalculator() -const safeCalculator = new Proxy(calculator, safeCalculatorHandler) +const calculator = new StackCalculator(); +const safeCalculator = new Proxy(calculator, safeCalculatorHandler); -console.log(safeCalculator instanceof StackCalculator) // true! +console.log(safeCalculator instanceof StackCalculator); // true! -calculator.putValue(3) -calculator.putValue(2) -console.log(calculator.multiply()) // 3*2 = 6 +calculator.putValue(3); +calculator.putValue(2); +console.log(calculator.multiply()); // 3*2 = 6 -safeCalculator.putValue(2) -console.log(safeCalculator.multiply()) // 6*2 = 12 +safeCalculator.putValue(2); +console.log(safeCalculator.multiply()); // 6*2 = 12 -calculator.putValue(0) -console.log(calculator.divide()) // 12/0 = Infinity +calculator.putValue(0); +console.log(calculator.divide()); // 12/0 = Infinity -safeCalculator.clear() -safeCalculator.putValue(4) -safeCalculator.putValue(0) -console.log(safeCalculator.divide()) // 4/0 -> Error('Division by 0') +safeCalculator.clear(); +safeCalculator.putValue(4); +safeCalculator.putValue(0); +console.log(safeCalculator.divide()); // 4/0 -> Error('Division by 0') diff --git a/08-structural-design-patterns/05-proxy-logging-writable-stream/index.js b/08-structural-design-patterns/05-proxy-logging-writable-stream/index.js index 8a03e98..fc69995 100644 --- a/08-structural-design-patterns/05-proxy-logging-writable-stream/index.js +++ b/08-structural-design-patterns/05-proxy-logging-writable-stream/index.js @@ -1,10 +1,10 @@ -import { createWriteStream } from 'node:fs' -import { createLoggingWritable } from './logging-writable.js' +import { createWriteStream } from "node:fs"; +import { createLoggingWritable } from "./logging-writable.js"; -const writable = createWriteStream('test.txt') -const writableProxy = createLoggingWritable(writable) +const writable = createWriteStream("test.txt"); +const writableProxy = createLoggingWritable(writable); -writableProxy.write('First chunk') -writableProxy.write('Second chunk') -writable.write('This is not logged') -writableProxy.end() +writableProxy.write("First chunk"); +writableProxy.write("Second chunk"); +writable.write("This is not logged"); +writableProxy.end(); diff --git a/08-structural-design-patterns/05-proxy-logging-writable-stream/logging-writable.js b/08-structural-design-patterns/05-proxy-logging-writable-stream/logging-writable.js index 03c58a2..3e5cd71 100644 --- a/08-structural-design-patterns/05-proxy-logging-writable-stream/logging-writable.js +++ b/08-structural-design-patterns/05-proxy-logging-writable-stream/logging-writable.js @@ -1,14 +1,14 @@ export function createLoggingWritable(writable) { return new Proxy(writable, { get(target, propKey, _receiver) { - if (propKey === 'write') { + if (propKey === "write") { return (...args) => { - const [chunk] = args - console.log('Writing', chunk) - return writable.write(...args) - } + const [chunk] = args; + console.log("Writing", chunk); + return writable.write(...args); + }; } - return target[propKey] + return target[propKey]; }, - }) + }); } diff --git a/08-structural-design-patterns/06-proxy-reactive/create-observable.js b/08-structural-design-patterns/06-proxy-reactive/create-observable.js index 830b694..c4292ed 100644 --- a/08-structural-design-patterns/06-proxy-reactive/create-observable.js +++ b/08-structural-design-patterns/06-proxy-reactive/create-observable.js @@ -2,13 +2,13 @@ export function createObservable(target, observer) { const observable = new Proxy(target, { set(obj, prop, value) { if (value !== obj[prop]) { - const prev = obj[prop] - obj[prop] = value - observer({ prop, prev, curr: value }) + const prev = obj[prop]; + obj[prop] = value; + observer({ prop, prev, curr: value }); } - return true + return true; }, - }) + }); - return observable + return observable; } diff --git a/08-structural-design-patterns/06-proxy-reactive/index.js b/08-structural-design-patterns/06-proxy-reactive/index.js index e33af9a..2ef7a08 100644 --- a/08-structural-design-patterns/06-proxy-reactive/index.js +++ b/08-structural-design-patterns/06-proxy-reactive/index.js @@ -1,31 +1,31 @@ -import { createObservable } from './create-observable.js' +import { createObservable } from "./create-observable.js"; function calculateTotal(invoice) { // (1) - return invoice.subtotal - invoice.discount + invoice.tax + return invoice.subtotal - invoice.discount + invoice.tax; } const invoice = { subtotal: 100, discount: 10, tax: 20, -} -let total = calculateTotal(invoice) -console.log(`Starting total: ${total}`) +}; +let total = calculateTotal(invoice); +console.log(`Starting total: ${total}`); const obsInvoice = createObservable( // (2) invoice, ({ prop, prev, curr }) => { - total = calculateTotal(invoice) - console.log(`TOTAL: ${total} (${prop} changed: ${prev} -> ${curr})`) - } -) + total = calculateTotal(invoice); + console.log(`TOTAL: ${total} (${prop} changed: ${prev} -> ${curr})`); + }, +); // (3) -obsInvoice.subtotal = 200 // TOTAL: 210 -obsInvoice.discount = 20 // TOTAL: 200 -obsInvoice.discount = 20 // no change: doesn't notify -obsInvoice.tax = 30 // TOTAL: 210 +obsInvoice.subtotal = 200; // TOTAL: 210 +obsInvoice.discount = 20; // TOTAL: 200 +obsInvoice.discount = 20; // no change: doesn't notify +obsInvoice.tax = 30; // TOTAL: 210 -console.log(`Final total: ${total}`) +console.log(`Final total: ${total}`); diff --git a/08-structural-design-patterns/07-decorator-composition/index.js b/08-structural-design-patterns/07-decorator-composition/index.js index e6e1bbc..0ad1d28 100644 --- a/08-structural-design-patterns/07-decorator-composition/index.js +++ b/08-structural-design-patterns/07-decorator-composition/index.js @@ -1,95 +1,95 @@ class StackCalculator { constructor() { - this.stack = [] + this.stack = []; } putValue(value) { - this.stack.push(value) + this.stack.push(value); } getValue() { - return this.stack.pop() + return this.stack.pop(); } peekValue() { - return this.stack[this.stack.length - 1] + return this.stack[this.stack.length - 1]; } clear() { - this.stack = [] + this.stack = []; } divide() { - const divisor = this.getValue() - const dividend = this.getValue() - const result = dividend / divisor - this.putValue(result) - return result + const divisor = this.getValue(); + const dividend = this.getValue(); + const result = dividend / divisor; + this.putValue(result); + return result; } multiply() { - const multiplicand = this.getValue() - const multiplier = this.getValue() - const result = multiplier * multiplicand - this.putValue(result) - return result + const multiplicand = this.getValue(); + const multiplier = this.getValue(); + const result = multiplier * multiplicand; + this.putValue(result); + return result; } } class EnhancedCalculator { constructor(calculator) { - this.calculator = calculator + this.calculator = calculator; } // new method add() { - const addend2 = this.getValue() - const addend1 = this.getValue() - const result = addend1 + addend2 - this.putValue(result) - return result + const addend2 = this.getValue(); + const addend1 = this.getValue(); + const result = addend1 + addend2; + this.putValue(result); + return result; } // modified method divide() { // additional validation logic - const divisor = this.calculator.peekValue() + const divisor = this.calculator.peekValue(); if (divisor === 0) { - throw new Error('Division by 0') + throw new Error("Division by 0"); } // if valid delegates to the subject - return this.calculator.divide() + return this.calculator.divide(); } // delegated methods putValue(value) { - return this.calculator.putValue(value) + return this.calculator.putValue(value); } getValue() { - return this.calculator.getValue() + return this.calculator.getValue(); } peekValue() { - return this.calculator.peekValue() + return this.calculator.peekValue(); } clear() { - return this.calculator.clear() + return this.calculator.clear(); } multiply() { - return this.calculator.multiply() + return this.calculator.multiply(); } } -const calculator = new StackCalculator() -const enhancedCalculator = new EnhancedCalculator(calculator) +const calculator = new StackCalculator(); +const enhancedCalculator = new EnhancedCalculator(calculator); -enhancedCalculator.putValue(4) -enhancedCalculator.putValue(3) -console.log(enhancedCalculator.add()) // 4+3 = 7 -enhancedCalculator.putValue(2) -console.log(enhancedCalculator.multiply()) // 7*2 = 14 +enhancedCalculator.putValue(4); +enhancedCalculator.putValue(3); +console.log(enhancedCalculator.add()); // 4+3 = 7 +enhancedCalculator.putValue(2); +console.log(enhancedCalculator.multiply()); // 7*2 = 14 // enhancedCalculator.putValue(0) // console.log(enhancedCalculator.divide()) // 14/0 -> Error('Division by 0') diff --git a/08-structural-design-patterns/08-decorator-object-augmentation/index.js b/08-structural-design-patterns/08-decorator-object-augmentation/index.js index ddcc8f5..ba57e3c 100644 --- a/08-structural-design-patterns/08-decorator-object-augmentation/index.js +++ b/08-structural-design-patterns/08-decorator-object-augmentation/index.js @@ -1,73 +1,73 @@ class StackCalculator { constructor() { - this.stack = [] + this.stack = []; } putValue(value) { - this.stack.push(value) + this.stack.push(value); } getValue() { - return this.stack.pop() + return this.stack.pop(); } peekValue() { - return this.stack[this.stack.length - 1] + return this.stack[this.stack.length - 1]; } clear() { - this.stack = [] + this.stack = []; } divide() { - const divisor = this.getValue() - const dividend = this.getValue() - const result = dividend / divisor - this.putValue(result) - return result + const divisor = this.getValue(); + const dividend = this.getValue(); + const result = dividend / divisor; + this.putValue(result); + return result; } multiply() { - const multiplicand = this.getValue() - const multiplier = this.getValue() - const result = multiplier * multiplicand - this.putValue(result) - return result + const multiplicand = this.getValue(); + const multiplier = this.getValue(); + const result = multiplier * multiplicand; + this.putValue(result); + return result; } } function patchCalculator(calculator) { // new method calculator.add = () => { - const addend2 = calculator.getValue() - const addend1 = calculator.getValue() - const result = addend1 + addend2 - calculator.putValue(result) - return result - } + const addend2 = calculator.getValue(); + const addend1 = calculator.getValue(); + const result = addend1 + addend2; + calculator.putValue(result); + return result; + }; // modified method - const divideOrig = calculator.divide + const divideOrig = calculator.divide; calculator.divide = () => { // additional validation logic - const divisor = calculator.peekValue() + const divisor = calculator.peekValue(); if (divisor === 0) { - throw new Error('Division by 0') + throw new Error("Division by 0"); } // if valid delegates to the subject - return divideOrig.apply(calculator) - } + return divideOrig.apply(calculator); + }; - return calculator + return calculator; } -const calculator = new StackCalculator() -const enhancedCalculator = patchCalculator(calculator) +const calculator = new StackCalculator(); +const enhancedCalculator = patchCalculator(calculator); -enhancedCalculator.putValue(4) -enhancedCalculator.putValue(3) -console.log(enhancedCalculator.add()) // 4+3 = 7 -enhancedCalculator.putValue(2) -console.log(enhancedCalculator.multiply()) // 7*2 = 14 +enhancedCalculator.putValue(4); +enhancedCalculator.putValue(3); +console.log(enhancedCalculator.add()); // 4+3 = 7 +enhancedCalculator.putValue(2); +console.log(enhancedCalculator.multiply()); // 7*2 = 14 // enhancedCalculator.putValue(0) // console.log(enhancedCalculator.divide()) // 14/0 -> Error('Division by 0') diff --git a/08-structural-design-patterns/09-decorator-es2015/index.js b/08-structural-design-patterns/09-decorator-es2015/index.js index eff6bd4..a500542 100644 --- a/08-structural-design-patterns/09-decorator-es2015/index.js +++ b/08-structural-design-patterns/09-decorator-es2015/index.js @@ -1,80 +1,80 @@ class StackCalculator { constructor() { - this.stack = [] + this.stack = []; } putValue(value) { - this.stack.push(value) + this.stack.push(value); } getValue() { - return this.stack.pop() + return this.stack.pop(); } peekValue() { - return this.stack[this.stack.length - 1] + return this.stack[this.stack.length - 1]; } clear() { - this.stack = [] + this.stack = []; } divide() { - const divisor = this.getValue() - const dividend = this.getValue() - const result = dividend / divisor - this.putValue(result) - return result + const divisor = this.getValue(); + const dividend = this.getValue(); + const result = dividend / divisor; + this.putValue(result); + return result; } multiply() { - const multiplicand = this.getValue() - const multiplier = this.getValue() - const result = multiplier * multiplicand - this.putValue(result) - return result + const multiplicand = this.getValue(); + const multiplier = this.getValue(); + const result = multiplier * multiplicand; + this.putValue(result); + return result; } } const enhancedCalculatorHandler = { get(target, property) { - if (property === 'add') { + if (property === "add") { // new method return function add() { - const addend2 = target.getValue() - const addend1 = target.getValue() - const result = addend1 + addend2 - target.putValue(result) - return result - } + const addend2 = target.getValue(); + const addend1 = target.getValue(); + const result = addend1 + addend2; + target.putValue(result); + return result; + }; } - if (property === 'divide') { + if (property === "divide") { // modified method return () => { // additional validation logic - const divisor = target.peekValue() + const divisor = target.peekValue(); if (divisor === 0) { - throw new Error('Division by 0') + throw new Error("Division by 0"); } // if valid delegates to the subject - return target.divide() - } + return target.divide(); + }; } // delegated methods and properties - return target[property] + return target[property]; }, -} +}; -const calculator = new StackCalculator() -const enhancedCalculator = new Proxy(calculator, enhancedCalculatorHandler) +const calculator = new StackCalculator(); +const enhancedCalculator = new Proxy(calculator, enhancedCalculatorHandler); -console.log(enhancedCalculator instanceof StackCalculator) // true! -enhancedCalculator.putValue(4) -enhancedCalculator.putValue(3) -console.log(enhancedCalculator.add()) // 4+3 = 7 -enhancedCalculator.putValue(2) -console.log(enhancedCalculator.multiply()) // 7*2 = 14 +console.log(enhancedCalculator instanceof StackCalculator); // true! +enhancedCalculator.putValue(4); +enhancedCalculator.putValue(3); +console.log(enhancedCalculator.add()); // 4+3 = 7 +enhancedCalculator.putValue(2); +console.log(enhancedCalculator.multiply()); // 7*2 = 14 // enhancedCalculator.putValue(0) // console.log(enhancedCalculator.divide()) // 14/0 -> Error('Division by 0') diff --git a/08-structural-design-patterns/10-decorator-levelup-plugin/index.js b/08-structural-design-patterns/10-decorator-levelup-plugin/index.js index 39762c0..ade1c33 100644 --- a/08-structural-design-patterns/10-decorator-levelup-plugin/index.js +++ b/08-structural-design-patterns/10-decorator-levelup-plugin/index.js @@ -1,20 +1,20 @@ -import { join } from 'node:path' -import { Level } from 'level' -import { levelSubscribe } from './level-subscribe.js' +import { join } from "node:path"; +import { Level } from "level"; +import { levelSubscribe } from "./level-subscribe.js"; -const dbPath = join(import.meta.dirname, 'db') -const db = new Level(dbPath, { valueEncoding: 'json' }) -levelSubscribe(db) +const dbPath = join(import.meta.dirname, "db"); +const db = new Level(dbPath, { valueEncoding: "json" }); +levelSubscribe(db); -db.subscribe({ doctype: 'message', language: 'en' }, (_k, val) => - console.log(val) -) -await db.put('1', { - doctype: 'message', - text: 'Hi', - language: 'en', -}) -await db.put('2', { - doctype: 'company', - name: 'ACME Co.', -}) +db.subscribe({ doctype: "message", language: "en" }, (_k, val) => + console.log(val), +); +await db.put("1", { + doctype: "message", + text: "Hi", + language: "en", +}); +await db.put("2", { + doctype: "company", + name: "ACME Co.", +}); diff --git a/08-structural-design-patterns/10-decorator-levelup-plugin/level-subscribe.js b/08-structural-design-patterns/10-decorator-levelup-plugin/level-subscribe.js index 3113c91..52bfa2c 100644 --- a/08-structural-design-patterns/10-decorator-levelup-plugin/level-subscribe.js +++ b/08-structural-design-patterns/10-decorator-levelup-plugin/level-subscribe.js @@ -1,16 +1,16 @@ export function levelSubscribe(db) { db.subscribe = (pattern, listener) => { - db.on('write', docs => { + db.on("write", (docs) => { for (const doc of docs) { const match = Object.keys(pattern).every( - k => pattern[k] === doc.value[k] - ) + (k) => pattern[k] === doc.value[k], + ); if (match) { - listener(doc.key, doc.value) + listener(doc.key, doc.value); } } - }) - } + }); + }; - return db + return db; } diff --git a/08-structural-design-patterns/11-adapter/fs-adapter.js b/08-structural-design-patterns/11-adapter/fs-adapter.js index 78cf8e7..112f035 100644 --- a/08-structural-design-patterns/11-adapter/fs-adapter.js +++ b/08-structural-design-patterns/11-adapter/fs-adapter.js @@ -1,30 +1,30 @@ -import { resolve } from 'node:path' +import { resolve } from "node:path"; export function createFsAdapter(db) { return { async readFile(filename, options = undefined) { const valueEncoding = - typeof options === 'string' ? options : options?.encoding - const opt = valueEncoding ? { valueEncoding } : undefined - const value = await db.get(resolve(filename), opt) + typeof options === "string" ? options : options?.encoding; + const opt = valueEncoding ? { valueEncoding } : undefined; + const value = await db.get(resolve(filename), opt); - if (typeof value === 'undefined') { + if (typeof value === "undefined") { const e = new Error( - `ENOENT: no such file or directory, open '${filename}'` - ) - e.code = 'ENOENT' - e.errno = 34 - e.path = filename - throw e + `ENOENT: no such file or directory, open '${filename}'`, + ); + e.code = "ENOENT"; + e.errno = 34; + e.path = filename; + throw e; } - return value + return value; }, async writeFile(filename, contents, options = undefined) { const valueEncoding = - typeof options === 'string' ? options : options?.encoding - const opt = valueEncoding ? { valueEncoding } : undefined - await db.put(resolve(filename), contents, opt) + typeof options === "string" ? options : options?.encoding; + const opt = valueEncoding ? { valueEncoding } : undefined; + await db.put(resolve(filename), contents, opt); }, - } + }; } diff --git a/08-structural-design-patterns/11-adapter/index-no-adapter.js b/08-structural-design-patterns/11-adapter/index-no-adapter.js index 98e90f6..9d356b5 100644 --- a/08-structural-design-patterns/11-adapter/index-no-adapter.js +++ b/08-structural-design-patterns/11-adapter/index-no-adapter.js @@ -1,8 +1,8 @@ -import fs from 'node:fs/promises' +import fs from "node:fs/promises"; -await fs.writeFile('file.txt', 'Hello!', 'utf8') -const res = await fs.readFile('file.txt', 'utf8') -console.log(res) +await fs.writeFile("file.txt", "Hello!", "utf8"); +const res = await fs.readFile("file.txt", "utf8"); +console.log(res); // try to read a missing file (throws an error) -await fs.readFile('missing.txt') +await fs.readFile("missing.txt"); diff --git a/08-structural-design-patterns/11-adapter/index.js b/08-structural-design-patterns/11-adapter/index.js index 6513b5f..eb61762 100644 --- a/08-structural-design-patterns/11-adapter/index.js +++ b/08-structural-design-patterns/11-adapter/index.js @@ -1,15 +1,15 @@ -import { join } from 'node:path' -import { Level } from 'level' -import { createFsAdapter } from './fs-adapter.js' +import { join } from "node:path"; +import { Level } from "level"; +import { createFsAdapter } from "./fs-adapter.js"; -const db = new Level(join(import.meta.dirname, 'db'), { - valueEncoding: 'binary', -}) -const fs = createFsAdapter(db) +const db = new Level(join(import.meta.dirname, "db"), { + valueEncoding: "binary", +}); +const fs = createFsAdapter(db); -await fs.writeFile('file.txt', 'Hello!', 'utf8') -const res = await fs.readFile('file.txt', 'utf8') -console.log(res) +await fs.writeFile("file.txt", "Hello!", "utf8"); +const res = await fs.readFile("file.txt", "utf8"); +console.log(res); // try to read a missing file (throws an error) -await fs.readFile('missing.txt') +await fs.readFile("missing.txt"); diff --git a/09-behavioral-design-patterns/01-strategy-multiformat-config/config.ts b/09-behavioral-design-patterns/01-strategy-multiformat-config/config.ts index cfcbe98..1d8903b 100644 --- a/09-behavioral-design-patterns/01-strategy-multiformat-config/config.ts +++ b/09-behavioral-design-patterns/01-strategy-multiformat-config/config.ts @@ -1,29 +1,29 @@ -import { readFile, writeFile } from 'node:fs/promises' -import type { ConfigData } from './configData.ts' -import type { FormatStrategy } from './strategies.ts' +import { readFile, writeFile } from "node:fs/promises"; +import type { ConfigData } from "./configData.ts"; +import type { FormatStrategy } from "./strategies.ts"; export class Config { - data?: ConfigData - formatStrategy: FormatStrategy + data?: ConfigData; + formatStrategy: FormatStrategy; constructor(formatStrategy: FormatStrategy) { - this.data = undefined - this.formatStrategy = formatStrategy + this.data = undefined; + this.formatStrategy = formatStrategy; } async load(filePath: string): Promise { - console.log(`Deserializing from ${filePath}`) + console.log(`Deserializing from ${filePath}`); this.data = this.formatStrategy.deserialize( - await readFile(filePath, 'utf-8') - ) + await readFile(filePath, "utf-8"), + ); } async save(filePath: string): Promise { if (!this.data) { - throw new Error('No data to save') + throw new Error("No data to save"); } - console.log(`Serializing to ${filePath}`) - await writeFile(filePath, this.formatStrategy.serialize(this.data)) + console.log(`Serializing to ${filePath}`); + await writeFile(filePath, this.formatStrategy.serialize(this.data)); } } diff --git a/09-behavioral-design-patterns/01-strategy-multiformat-config/configData.ts b/09-behavioral-design-patterns/01-strategy-multiformat-config/configData.ts index d11e8f1..0364c55 100644 --- a/09-behavioral-design-patterns/01-strategy-multiformat-config/configData.ts +++ b/09-behavioral-design-patterns/01-strategy-multiformat-config/configData.ts @@ -1,12 +1,12 @@ export type ConfigData = { listen: { - port: number - host: string - } + port: number; + host: string; + }; timeouts: { - headersTimeoutMs: number - keepAliveTimeoutMs: number - requestTimeoutMs: number - } - env: Record -} + headersTimeoutMs: number; + keepAliveTimeoutMs: number; + requestTimeoutMs: number; + }; + env: Record; +}; diff --git a/09-behavioral-design-patterns/01-strategy-multiformat-config/index.ts b/09-behavioral-design-patterns/01-strategy-multiformat-config/index.ts index 6d24ad7..c007f6c 100644 --- a/09-behavioral-design-patterns/01-strategy-multiformat-config/index.ts +++ b/09-behavioral-design-patterns/01-strategy-multiformat-config/index.ts @@ -1,29 +1,29 @@ -import { join } from 'node:path' -import { Config } from './config.ts' -import { jsonStrategy, tomlStrategy, yamlStrategy } from './strategies.ts' +import { join } from "node:path"; +import { Config } from "./config.ts"; +import { jsonStrategy, tomlStrategy, yamlStrategy } from "./strategies.ts"; -const SAMPLES = join(import.meta.dirname, 'samples') +const SAMPLES = join(import.meta.dirname, "samples"); -const jsonConfig = new Config(jsonStrategy) -await jsonConfig.load(join(SAMPLES, 'config.json')) +const jsonConfig = new Config(jsonStrategy); +await jsonConfig.load(join(SAMPLES, "config.json")); if (jsonConfig.data?.env) { - jsonConfig.data.env.NODE_ENV = 'production' - jsonConfig.data.env.NODE_OPTIONS = '--enable-source-maps' + jsonConfig.data.env.NODE_ENV = "production"; + jsonConfig.data.env.NODE_OPTIONS = "--enable-source-maps"; } -await jsonConfig.save(join(SAMPLES, 'config_mod.json')) +await jsonConfig.save(join(SAMPLES, "config_mod.json")); -const yamlConfig = new Config(yamlStrategy) -await yamlConfig.load(join(SAMPLES, 'config.yaml')) +const yamlConfig = new Config(yamlStrategy); +await yamlConfig.load(join(SAMPLES, "config.yaml")); if (yamlConfig.data?.env) { - yamlConfig.data.env.NODE_ENV = 'production' - yamlConfig.data.env.NODE_OPTIONS = '--enable-source-maps' + yamlConfig.data.env.NODE_ENV = "production"; + yamlConfig.data.env.NODE_OPTIONS = "--enable-source-maps"; } -await yamlConfig.save(join(SAMPLES, 'config_mod.yaml')) +await yamlConfig.save(join(SAMPLES, "config_mod.yaml")); -const tomlConfig = new Config(tomlStrategy) -await tomlConfig.load(join(SAMPLES, 'config.toml')) +const tomlConfig = new Config(tomlStrategy); +await tomlConfig.load(join(SAMPLES, "config.toml")); if (tomlConfig.data?.env) { - tomlConfig.data.env.NODE_ENV = 'production' - tomlConfig.data.env.NODE_OPTIONS = '--enable-source-maps' + tomlConfig.data.env.NODE_ENV = "production"; + tomlConfig.data.env.NODE_OPTIONS = "--enable-source-maps"; } -await tomlConfig.save(join(SAMPLES, 'config_mod.toml')) +await tomlConfig.save(join(SAMPLES, "config_mod.toml")); diff --git a/09-behavioral-design-patterns/01-strategy-multiformat-config/strategies.ts b/09-behavioral-design-patterns/01-strategy-multiformat-config/strategies.ts index 471e5e4..17a9a8e 100644 --- a/09-behavioral-design-patterns/01-strategy-multiformat-config/strategies.ts +++ b/09-behavioral-design-patterns/01-strategy-multiformat-config/strategies.ts @@ -1,35 +1,35 @@ -import TOML from 'smol-toml' // v1.3.1 -import YAML from 'yaml' // v2.7.0 -import type { ConfigData } from './configData.ts' +import TOML from "smol-toml"; // v1.3.1 +import YAML from "yaml"; // v2.7.0 +import type { ConfigData } from "./configData.ts"; export type FormatStrategy = { - deserialize: (data: string) => ConfigData - serialize: (data: ConfigData) => string -} + deserialize: (data: string) => ConfigData; + serialize: (data: ConfigData) => string; +}; export const jsonStrategy: FormatStrategy = { deserialize(data): ConfigData { - return JSON.parse(data) + return JSON.parse(data); }, serialize(data: ConfigData): string { - return JSON.stringify(data, null, 2) + return JSON.stringify(data, null, 2); }, -} +}; export const yamlStrategy: FormatStrategy = { deserialize(data): ConfigData { - return YAML.parse(data) + return YAML.parse(data); }, serialize(data: ConfigData): string { - return YAML.stringify(data, { indent: 2 }) + return YAML.stringify(data, { indent: 2 }); }, -} +}; export const tomlStrategy: FormatStrategy = { deserialize(data): ConfigData { - return TOML.parse(data) as ConfigData + return TOML.parse(data) as ConfigData; }, serialize(data: ConfigData): string { - return TOML.stringify(data) + return TOML.stringify(data); }, -} +}; diff --git a/09-behavioral-design-patterns/02-state-failsafe-socket/client.js b/09-behavioral-design-patterns/02-state-failsafe-socket/client.js index ba46817..b83b8fd 100644 --- a/09-behavioral-design-patterns/02-state-failsafe-socket/client.js +++ b/09-behavioral-design-patterns/02-state-failsafe-socket/client.js @@ -1,10 +1,10 @@ -import { hostname } from 'node:os' -import { FailsafeSocket } from './failsafeSocket.js' +import { hostname } from "node:os"; +import { FailsafeSocket } from "./failsafeSocket.js"; -const clientId = `${hostname()}@${process.pid}` -console.log(`Starting client ${clientId}`) +const clientId = `${hostname()}@${process.pid}`; +console.log(`Starting client ${clientId}`); -const failsafeSocket = new FailsafeSocket({ port: 4545 }) +const failsafeSocket = new FailsafeSocket({ port: 4545 }); setInterval(() => { // constructs the message @@ -14,13 +14,13 @@ setInterval(() => { client: clientId, mem: process.memoryUsage(), }), - 'utf-8' - ) + "utf-8", + ); // creates a 4-byte buffer to store the message length - const messageLength = Buffer.alloc(4) - messageLength.writeUInt32BE(messageData.length, 0) + const messageLength = Buffer.alloc(4); + messageLength.writeUInt32BE(messageData.length, 0); // concatenates the message length and message data - const message = Buffer.concat([messageLength, messageData]) + const message = Buffer.concat([messageLength, messageData]); // sends the message - failsafeSocket.send(message) -}, 5000) + failsafeSocket.send(message); +}, 5000); diff --git a/09-behavioral-design-patterns/02-state-failsafe-socket/failsafeSocket.js b/09-behavioral-design-patterns/02-state-failsafe-socket/failsafeSocket.js index ae0e315..2403e9b 100644 --- a/09-behavioral-design-patterns/02-state-failsafe-socket/failsafeSocket.js +++ b/09-behavioral-design-patterns/02-state-failsafe-socket/failsafeSocket.js @@ -1,26 +1,26 @@ -import { OfflineState } from './offlineState.js' -import { OnlineState } from './onlineState.js' +import { OfflineState } from "./offlineState.js"; +import { OnlineState } from "./onlineState.js"; export class FailsafeSocket { constructor(options) { - this.options = options - this.queue = [] - this.currentState = null - this.socket = null + this.options = options; + this.queue = []; + this.currentState = null; + this.socket = null; this.states = { offline: new OfflineState(this), online: new OnlineState(this), - } - this.changeState('offline') + }; + this.changeState("offline"); } changeState(state) { - console.log(`Activating state: ${state}`) - this.currentState = this.states[state] - this.currentState.activate() + console.log(`Activating state: ${state}`); + this.currentState = this.states[state]; + this.currentState.activate(); } send(data) { - this.currentState.send(data) + this.currentState.send(data); } } diff --git a/09-behavioral-design-patterns/02-state-failsafe-socket/offlineState.js b/09-behavioral-design-patterns/02-state-failsafe-socket/offlineState.js index ac7a8b3..1d8487b 100644 --- a/09-behavioral-design-patterns/02-state-failsafe-socket/offlineState.js +++ b/09-behavioral-design-patterns/02-state-failsafe-socket/offlineState.js @@ -1,30 +1,30 @@ -import { createConnection } from 'node:net' +import { createConnection } from "node:net"; export class OfflineState { constructor(failsafeSocket) { - this.failsafeSocket = failsafeSocket + this.failsafeSocket = failsafeSocket; } send(data) { - this.failsafeSocket.queue.push(data) + this.failsafeSocket.queue.push(data); } activate() { const retry = () => { - setTimeout(() => this.activate(), 1000) - } + setTimeout(() => this.activate(), 1000); + }; console.log( - `Trying to connect (${this.failsafeSocket.queue.length} queued messages)` - ) + `Trying to connect (${this.failsafeSocket.queue.length} queued messages)`, + ); this.failsafeSocket.socket = createConnection( this.failsafeSocket.options, () => { - console.log('Connection established') - this.failsafeSocket.socket.removeListener('error', retry) - this.failsafeSocket.changeState('online') - } - ) - this.failsafeSocket.socket.once('error', retry) + console.log("Connection established"); + this.failsafeSocket.socket.removeListener("error", retry); + this.failsafeSocket.changeState("online"); + }, + ); + this.failsafeSocket.socket.once("error", retry); } } diff --git a/09-behavioral-design-patterns/02-state-failsafe-socket/onlineState.js b/09-behavioral-design-patterns/02-state-failsafe-socket/onlineState.js index adc41f9..8459ba3 100644 --- a/09-behavioral-design-patterns/02-state-failsafe-socket/onlineState.js +++ b/09-behavioral-design-patterns/02-state-failsafe-socket/onlineState.js @@ -1,49 +1,49 @@ export class OnlineState { constructor(failsafeSocket) { - this.failsafeSocket = failsafeSocket + this.failsafeSocket = failsafeSocket; } send(data) { - this.failsafeSocket.queue.push(data) - this._tryFlush() + this.failsafeSocket.queue.push(data); + this._tryFlush(); } async _tryFlush() { try { - let success = true + let success = true; while (this.failsafeSocket.queue.length > 0) { - const data = this.failsafeSocket.queue[0] - const flushed = await this._tryWrite(data) + const data = this.failsafeSocket.queue[0]; + const flushed = await this._tryWrite(data); if (flushed) { - this.failsafeSocket.queue.shift() + this.failsafeSocket.queue.shift(); } else { - success = false - break + success = false; + break; } } if (!success) { - this.failsafeSocket.changeState('offline') + this.failsafeSocket.changeState("offline"); } } catch (err) { - console.error('Error during flush', err.message) - this.failsafeSocket.changeState('offline') + console.error("Error during flush", err.message); + this.failsafeSocket.changeState("offline"); } } _tryWrite(data) { - return new Promise(resolve => { - this.failsafeSocket.socket.write(data, err => { + return new Promise((resolve) => { + this.failsafeSocket.socket.write(data, (err) => { if (err) { - console.error('Error writing data', err.message) - resolve(false) + console.error("Error writing data", err.message); + resolve(false); } else { - resolve(true) + resolve(true); } - }) - }) + }); + }); } activate() { - this._tryFlush() + this._tryFlush(); } } diff --git a/09-behavioral-design-patterns/02-state-failsafe-socket/server.js b/09-behavioral-design-patterns/02-state-failsafe-socket/server.js index f2abf7e..617ebda 100644 --- a/09-behavioral-design-patterns/02-state-failsafe-socket/server.js +++ b/09-behavioral-design-patterns/02-state-failsafe-socket/server.js @@ -1,39 +1,39 @@ -import { createServer } from 'node:net' +import { createServer } from "node:net"; -const server = createServer(socket => { - socket.on('error', err => { - console.error('Server error', err.message) - }) +const server = createServer((socket) => { + socket.on("error", (err) => { + console.error("Server error", err.message); + }); // Accumulate incoming data - let buffer = Buffer.alloc(0) + let buffer = Buffer.alloc(0); // When a chunk of data is received - socket.on('data', data => { + socket.on("data", (data) => { // Append new data to the buffer - buffer = Buffer.concat([buffer, data]) + buffer = Buffer.concat([buffer, data]); // Ensure we have enough bytes for the length prefix while (buffer.length >= 4) { // Read the message length (Big Endian) - const messageLength = buffer.readUInt32BE(0) + const messageLength = buffer.readUInt32BE(0); if (buffer.length < 4 + messageLength) { // Not enough data yet; wait for more - break + break; } // Check if we have the complete message - const message = buffer.subarray(4, 4 + messageLength).toString('utf8') + const message = buffer.subarray(4, 4 + messageLength).toString("utf8"); // Process the message (just log it) - console.log('Received message:', JSON.parse(message)) + console.log("Received message:", JSON.parse(message)); // Remove the processed message from the buffer - buffer = buffer.subarray(4 + messageLength) + buffer = buffer.subarray(4 + messageLength); } - }) -}) + }); +}); // Start the server and listen on port 4545 -server.listen(4545, () => console.log('Server started')) +server.listen(4545, () => console.log("Server started")); diff --git a/09-behavioral-design-patterns/03-template-multiformat-config/configTemplate.js b/09-behavioral-design-patterns/03-template-multiformat-config/configTemplate.js index 7a49f55..3f0739a 100644 --- a/09-behavioral-design-patterns/03-template-multiformat-config/configTemplate.js +++ b/09-behavioral-design-patterns/03-template-multiformat-config/configTemplate.js @@ -1,21 +1,21 @@ -import { readFile, writeFile } from 'node:fs/promises' +import { readFile, writeFile } from "node:fs/promises"; export class ConfigTemplate { async load(file) { - console.log(`Deserializing from ${file}`) - this.data = this._deserialize(await readFile(file, 'utf-8')) + console.log(`Deserializing from ${file}`); + this.data = this._deserialize(await readFile(file, "utf-8")); } async save(file) { - console.log(`Serializing to ${file}`) - await writeFile(file, this._serialize(this.data)) + console.log(`Serializing to ${file}`); + await writeFile(file, this._serialize(this.data)); } _serialize() { - throw new Error('_serialize() must be implemented') + throw new Error("_serialize() must be implemented"); } _deserialize() { - throw new Error('_deserialize() must be implemented') + throw new Error("_deserialize() must be implemented"); } } diff --git a/09-behavioral-design-patterns/03-template-multiformat-config/index.js b/09-behavioral-design-patterns/03-template-multiformat-config/index.js index 34c6489..154b59c 100644 --- a/09-behavioral-design-patterns/03-template-multiformat-config/index.js +++ b/09-behavioral-design-patterns/03-template-multiformat-config/index.js @@ -1,24 +1,24 @@ -import { join } from 'node:path' -import { JsonConfig } from './jsonConfig.js' -import { TomlConfig } from './tomlConfig.js' -import { YamlConfig } from './yamlConfig.js' +import { join } from "node:path"; +import { JsonConfig } from "./jsonConfig.js"; +import { TomlConfig } from "./tomlConfig.js"; +import { YamlConfig } from "./yamlConfig.js"; -const SAMPLES = join(import.meta.dirname, 'samples') +const SAMPLES = join(import.meta.dirname, "samples"); -const jsonConfig = new JsonConfig() -await jsonConfig.load(join(SAMPLES, 'config.json')) -jsonConfig.data.env.NODE_ENV = 'production' -jsonConfig.data.env.NODE_OPTIONS = '--enable-source-maps' -await jsonConfig.save(join(SAMPLES, 'config_mod.json')) +const jsonConfig = new JsonConfig(); +await jsonConfig.load(join(SAMPLES, "config.json")); +jsonConfig.data.env.NODE_ENV = "production"; +jsonConfig.data.env.NODE_OPTIONS = "--enable-source-maps"; +await jsonConfig.save(join(SAMPLES, "config_mod.json")); -const yamlConfig = new YamlConfig() -await yamlConfig.load(join(SAMPLES, 'config.yaml')) -yamlConfig.data.env.NODE_ENV = 'production' -yamlConfig.data.env.NODE_OPTIONS = '--enable-source-maps' -await yamlConfig.save(join(SAMPLES, 'config_mod.yaml')) +const yamlConfig = new YamlConfig(); +await yamlConfig.load(join(SAMPLES, "config.yaml")); +yamlConfig.data.env.NODE_ENV = "production"; +yamlConfig.data.env.NODE_OPTIONS = "--enable-source-maps"; +await yamlConfig.save(join(SAMPLES, "config_mod.yaml")); -const tomlConfig = new TomlConfig() -await tomlConfig.load(join(SAMPLES, 'config.toml')) -tomlConfig.data.env.NODE_ENV = 'production' -tomlConfig.data.env.NODE_OPTIONS = '--enable-source-maps' -await tomlConfig.save(join(SAMPLES, 'config_mod.toml')) +const tomlConfig = new TomlConfig(); +await tomlConfig.load(join(SAMPLES, "config.toml")); +tomlConfig.data.env.NODE_ENV = "production"; +tomlConfig.data.env.NODE_OPTIONS = "--enable-source-maps"; +await tomlConfig.save(join(SAMPLES, "config_mod.toml")); diff --git a/09-behavioral-design-patterns/03-template-multiformat-config/jsonConfig.js b/09-behavioral-design-patterns/03-template-multiformat-config/jsonConfig.js index 671880d..b4453f5 100644 --- a/09-behavioral-design-patterns/03-template-multiformat-config/jsonConfig.js +++ b/09-behavioral-design-patterns/03-template-multiformat-config/jsonConfig.js @@ -1,11 +1,11 @@ -import { ConfigTemplate } from './configTemplate.js' +import { ConfigTemplate } from "./configTemplate.js"; export class JsonConfig extends ConfigTemplate { _deserialize(data) { - return JSON.parse(data) + return JSON.parse(data); } _serialize(data) { - return JSON.stringify(data, null, ' ') + return JSON.stringify(data, null, " "); } } diff --git a/09-behavioral-design-patterns/03-template-multiformat-config/tomlConfig.js b/09-behavioral-design-patterns/03-template-multiformat-config/tomlConfig.js index e8fc3fa..10cd379 100644 --- a/09-behavioral-design-patterns/03-template-multiformat-config/tomlConfig.js +++ b/09-behavioral-design-patterns/03-template-multiformat-config/tomlConfig.js @@ -1,12 +1,12 @@ -import TOML from 'smol-toml' // v1.3.1 -import { ConfigTemplate } from './configTemplate.js' +import TOML from "smol-toml"; // v1.3.1 +import { ConfigTemplate } from "./configTemplate.js"; export class TomlConfig extends ConfigTemplate { _deserialize(data) { - return TOML.parse(data) + return TOML.parse(data); } _serialize(data) { - return TOML.stringify(data) + return TOML.stringify(data); } } diff --git a/09-behavioral-design-patterns/03-template-multiformat-config/yamlConfig.js b/09-behavioral-design-patterns/03-template-multiformat-config/yamlConfig.js index 88c4575..7bbd6db 100644 --- a/09-behavioral-design-patterns/03-template-multiformat-config/yamlConfig.js +++ b/09-behavioral-design-patterns/03-template-multiformat-config/yamlConfig.js @@ -1,12 +1,12 @@ -import YAML from 'yaml' // v2.7.0 -import { ConfigTemplate } from './configTemplate.js' +import YAML from "yaml"; // v2.7.0 +import { ConfigTemplate } from "./configTemplate.js"; export class YamlConfig extends ConfigTemplate { _deserialize(data) { - return YAML.parse(data) + return YAML.parse(data); } _serialize(data) { - return YAML.stringify(data) + return YAML.stringify(data); } } diff --git a/09-behavioral-design-patterns/04-iterator-alphabet-iterator/index.js b/09-behavioral-design-patterns/04-iterator-alphabet-iterator/index.js index 08f3d0b..f45413b 100644 --- a/09-behavioral-design-patterns/04-iterator-alphabet-iterator/index.js +++ b/09-behavioral-design-patterns/04-iterator-alphabet-iterator/index.js @@ -1,26 +1,26 @@ -const A_CHAR_CODE = 'A'.charCodeAt(0) // 65 -const Z_CHAR_CODE = 'Z'.charCodeAt(0) // 90 +const A_CHAR_CODE = "A".charCodeAt(0); // 65 +const Z_CHAR_CODE = "Z".charCodeAt(0); // 90 function createAlphabetIterator() { - let currCode = A_CHAR_CODE + let currCode = A_CHAR_CODE; return { next() { - const currChar = String.fromCodePoint(currCode) + const currChar = String.fromCodePoint(currCode); if (currCode > Z_CHAR_CODE) { - return { done: true } + return { done: true }; } - currCode++ - return { value: currChar, done: false } + currCode++; + return { value: currChar, done: false }; }, - } + }; } -const iterator = createAlphabetIterator() -let iterationResult = iterator.next() +const iterator = createAlphabetIterator(); +let iterationResult = iterator.next(); while (!iterationResult.done) { - console.log(iterationResult.value) - iterationResult = iterator.next() + console.log(iterationResult.value); + iterationResult = iterator.next(); } diff --git a/09-behavioral-design-patterns/05-iterable-matrix/index.js b/09-behavioral-design-patterns/05-iterable-matrix/index.js index 0654f34..657b9bb 100644 --- a/09-behavioral-design-patterns/05-iterable-matrix/index.js +++ b/09-behavioral-design-patterns/05-iterable-matrix/index.js @@ -1,26 +1,26 @@ -import { Matrix } from './matrix.js' +import { Matrix } from "./matrix.js"; const matrix2x2 = new Matrix([ - ['11', '12'], - ['21', '22'], -]) + ["11", "12"], + ["21", "22"], +]); -const iterator = matrix2x2[Symbol.iterator]() -let iterationResult = iterator.next() +const iterator = matrix2x2[Symbol.iterator](); +let iterationResult = iterator.next(); while (!iterationResult.done) { - console.log(iterationResult.value) - iterationResult = iterator.next() + console.log(iterationResult.value); + iterationResult = iterator.next(); } -console.log('for...of:') +console.log("for...of:"); for (const element of matrix2x2) { - console.log(element) + console.log(element); } -console.log('spread operator:') -const flattenedMatrix = [...matrix2x2] -console.log(flattenedMatrix) +console.log("spread operator:"); +const flattenedMatrix = [...matrix2x2]; +console.log(flattenedMatrix); -console.log('destructuring assignment:') -const [oneOne, oneTwo, twoOne, twoTwo] = matrix2x2 -console.log(oneOne, oneTwo, twoOne, twoTwo) +console.log("destructuring assignment:"); +const [oneOne, oneTwo, twoOne, twoTwo] = matrix2x2; +console.log(oneOne, oneTwo, twoOne, twoTwo); diff --git a/09-behavioral-design-patterns/05-iterable-matrix/matrix.js b/09-behavioral-design-patterns/05-iterable-matrix/matrix.js index d3bb652..7d1fa64 100644 --- a/09-behavioral-design-patterns/05-iterable-matrix/matrix.js +++ b/09-behavioral-design-patterns/05-iterable-matrix/matrix.js @@ -1,43 +1,43 @@ export class Matrix { constructor(inMatrix) { - this.data = inMatrix + this.data = inMatrix; } get(row, column) { if (row >= this.data.length || column >= this.data[row].length) { - throw new RangeError('Out of bounds') + throw new RangeError("Out of bounds"); } - return this.data[row][column] + return this.data[row][column]; } set(row, column, value) { if (row >= this.data.length || column >= this.data[row].length) { - throw new RangeError('Out of bounds') + throw new RangeError("Out of bounds"); } - this.data[row][column] = value + this.data[row][column] = value; } [Symbol.iterator]() { - let nextRow = 0 - let nextCol = 0 + let nextRow = 0; + let nextCol = 0; return { next: () => { if (nextRow === this.data.length) { - return { done: true } + return { done: true }; } - const currVal = this.data[nextRow][nextCol] + const currVal = this.data[nextRow][nextCol]; if (nextCol === this.data[nextRow].length - 1) { - nextRow++ - nextCol = 0 + nextRow++; + nextCol = 0; } else { - nextCol++ + nextCol++; } - return { value: currVal } + return { value: currVal }; }, - } + }; } } diff --git a/09-behavioral-design-patterns/06-iterator-iterable-alphabet/index.js b/09-behavioral-design-patterns/06-iterator-iterable-alphabet/index.js index 0b50e63..8bb56be 100644 --- a/09-behavioral-design-patterns/06-iterator-iterable-alphabet/index.js +++ b/09-behavioral-design-patterns/06-iterator-iterable-alphabet/index.js @@ -1,23 +1,23 @@ -const A_CHAR_CODE = 'A'.charCodeAt(0) // 65 -const Z_CHAR_CODE = 'Z'.charCodeAt(0) // 90 +const A_CHAR_CODE = "A".charCodeAt(0); // 65 +const Z_CHAR_CODE = "Z".charCodeAt(0); // 90 function createAlphabetIterableIterator() { - let currCode = A_CHAR_CODE + let currCode = A_CHAR_CODE; return { next() { - const currChar = String.fromCodePoint(currCode) + const currChar = String.fromCodePoint(currCode); if (currCode > Z_CHAR_CODE) { - return { done: true } + return { done: true }; } - currCode++ - return { value: currChar, done: false } + currCode++; + return { value: currChar, done: false }; }, [Symbol.iterator]() { - return this + return this; }, - } + }; } // // If you want to use the iterator API @@ -30,9 +30,9 @@ function createAlphabetIterableIterator() { // Using the iterable API for (const letter of createAlphabetIterableIterator()) { - console.log(letter) + console.log(letter); } // or -const letters = [...createAlphabetIterableIterator()] -console.log(letters) +const letters = [...createAlphabetIterableIterator()]; +console.log(letters); diff --git a/09-behavioral-design-patterns/07-iterator-prototype/index.js b/09-behavioral-design-patterns/07-iterator-prototype/index.js index 8e16493..9a4e6d7 100644 --- a/09-behavioral-design-patterns/07-iterator-prototype/index.js +++ b/09-behavioral-design-patterns/07-iterator-prototype/index.js @@ -1,63 +1,65 @@ class RangeIterator extends Iterator { - #start - #end - #step - #current + #start; + #end; + #step; + #current; constructor(start, end, step = 1) { - super() - this.#start = start - this.#end = end - this.#step = step - this.#current = undefined + super(); + this.#start = start; + this.#end = end; + this.#step = step; + this.#current = undefined; } next() { this.#current = - this.#current === undefined ? this.#start : this.#current + this.#step + this.#current === undefined ? this.#start : this.#current + this.#step; if ( this.#step > 0 ? this.#current < this.#end : this.#current > this.#end ) { - return { done: false, value: this.#current } + return { done: false, value: this.#current }; } - return { done: true } + return { done: true }; } } -const range = new RangeIterator(1, 6) +const range = new RangeIterator(1, 6); -let iterationResult = range.next() +let iterationResult = range.next(); while (!iterationResult.done) { - console.log(iterationResult.value) - iterationResult = range.next() + console.log(iterationResult.value); + iterationResult = range.next(); } -console.log(range instanceof Iterator) // true +console.log(range instanceof Iterator); // true // instances are also iterable -const numbers = [...new RangeIterator(1, 6)] -console.log(numbers) +const numbers = [...new RangeIterator(1, 6)]; +console.log(numbers); // Builds an iterator from 0 to 10, filters out the odd numbers, doubles the remaining ones: -const zeroToTen = new RangeIterator(0, 10) +const zeroToTen = new RangeIterator(0, 10); const doubledEven = zeroToTen - .filter(n => n % 2 === 0) - .map(n => n * 2) - .toArray() -console.log(doubledEven) + .filter((n) => n % 2 === 0) + .map((n) => n * 2) + .toArray(); +console.log(doubledEven); -const zeroToTenIt = new RangeIterator(0, 10) -const doubledEvenIt = zeroToTenIt.filter(n => n % 2 === 0).map(n => n * 2) -console.log(doubledEvenIt.next()) // { done: false, value: 0 } -console.log(doubledEvenIt.next()) // { done: false, value: 4 } +const zeroToTenIt = new RangeIterator(0, 10); +const doubledEvenIt = zeroToTenIt.filter((n) => n % 2 === 0).map((n) => n * 2); +console.log(doubledEvenIt.next()); // { done: false, value: 0 } +console.log(doubledEvenIt.next()); // { done: false, value: 4 } // Compares with the eager version of the Array prototype: -const numbersArray = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] -const doubledEvenArray = numbersArray.filter(n => n % 2 === 0).map(n => n * 2) -console.log(doubledEvenArray) +const numbersArray = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; +const doubledEvenArray = numbersArray + .filter((n) => n % 2 === 0) + .map((n) => n * 2); +console.log(doubledEvenArray); // Iterator.from([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) // .filter(n => n % 2 === 0) diff --git a/09-behavioral-design-patterns/08-iterator-fruit-generator/index.js b/09-behavioral-design-patterns/08-iterator-fruit-generator/index.js index b1cb51e..606b01d 100644 --- a/09-behavioral-design-patterns/08-iterator-fruit-generator/index.js +++ b/09-behavioral-design-patterns/08-iterator-fruit-generator/index.js @@ -1,16 +1,16 @@ function* fruitGenerator() { - yield 'peach' - yield 'watermelon' - return 'summer' + yield "peach"; + yield "watermelon"; + return "summer"; } -const fruitGeneratorObj = fruitGenerator() -console.log(fruitGeneratorObj.next()) -console.log(fruitGeneratorObj.next()) -console.log(fruitGeneratorObj.next()) +const fruitGeneratorObj = fruitGenerator(); +console.log(fruitGeneratorObj.next()); +console.log(fruitGeneratorObj.next()); +console.log(fruitGeneratorObj.next()); -console.log('Using for...of:') +console.log("Using for...of:"); for (const fruit of fruitGenerator()) { - console.log(fruit) + console.log(fruit); } diff --git a/09-behavioral-design-patterns/09-iterator-two-way/index.js b/09-behavioral-design-patterns/09-iterator-two-way/index.js index 21aa79d..1d3aa27 100644 --- a/09-behavioral-design-patterns/09-iterator-two-way/index.js +++ b/09-behavioral-design-patterns/09-iterator-two-way/index.js @@ -1,22 +1,22 @@ function* twoWayGenerator() { try { - const who = yield null - yield `Hello ${who}` + const who = yield null; + yield `Hello ${who}`; } catch (err) { - yield `Hello error: ${err.message}` + yield `Hello error: ${err.message}`; } } -console.log('Passing a value back to the generator:') -const twoWay = twoWayGenerator() -twoWay.next() -console.log(twoWay.next('world')) +console.log("Passing a value back to the generator:"); +const twoWay = twoWayGenerator(); +twoWay.next(); +console.log(twoWay.next("world")); -console.log('Using throw():') -const twoWayException = twoWayGenerator() -twoWayException.next() -console.log(twoWayException.throw(new Error('Boom!'))) +console.log("Using throw():"); +const twoWayException = twoWayGenerator(); +twoWayException.next(); +console.log(twoWayException.throw(new Error("Boom!"))); -console.log('Using return():') -const twoWayReturn = twoWayGenerator() -console.log(twoWayReturn.return('myReturnValue')) +console.log("Using return():"); +const twoWayReturn = twoWayGenerator(); +console.log(twoWayReturn.return("myReturnValue")); diff --git a/09-behavioral-design-patterns/10-iterator-matrix-generator/index.js b/09-behavioral-design-patterns/10-iterator-matrix-generator/index.js index 22a67fa..9b8af6a 100644 --- a/09-behavioral-design-patterns/10-iterator-matrix-generator/index.js +++ b/09-behavioral-design-patterns/10-iterator-matrix-generator/index.js @@ -1,13 +1,13 @@ -import { Matrix } from './matrix.js' +import { Matrix } from "./matrix.js"; const matrix2x2 = new Matrix([ - ['11', '12'], - ['21', '22'], -]) + ["11", "12"], + ["21", "22"], +]); -const iterator = matrix2x2[Symbol.iterator]() -let iterationResult = iterator.next() +const iterator = matrix2x2[Symbol.iterator](); +let iterationResult = iterator.next(); while (!iterationResult.done) { - console.log(iterationResult.value) - iterationResult = iterator.next() + console.log(iterationResult.value); + iterationResult = iterator.next(); } diff --git a/09-behavioral-design-patterns/10-iterator-matrix-generator/matrix.js b/09-behavioral-design-patterns/10-iterator-matrix-generator/matrix.js index bdceea9..c1039fd 100644 --- a/09-behavioral-design-patterns/10-iterator-matrix-generator/matrix.js +++ b/09-behavioral-design-patterns/10-iterator-matrix-generator/matrix.js @@ -1,26 +1,26 @@ export class Matrix { constructor(inMatrix) { - this.data = inMatrix + this.data = inMatrix; } get(row, column) { if (row >= this.data.length || column >= this.data[row].length) { - throw new RangeError('Out of bounds') + throw new RangeError("Out of bounds"); } - return this.data[row][column] + return this.data[row][column]; } set(row, column, value) { if (row >= this.data.length || column >= this.data[row].length) { - throw new RangeError('Out of bounds') + throw new RangeError("Out of bounds"); } - this.data[row][column] = value + this.data[row][column] = value; } *[Symbol.iterator]() { for (const row of this.data) { for (const cell of row) { - yield cell + yield cell; } } } diff --git a/09-behavioral-design-patterns/11-async-iterator-check-urls/checkUrls.js b/09-behavioral-design-patterns/11-async-iterator-check-urls/checkUrls.js index 4f30aca..32cadf4 100644 --- a/09-behavioral-design-patterns/11-async-iterator-check-urls/checkUrls.js +++ b/09-behavioral-design-patterns/11-async-iterator-check-urls/checkUrls.js @@ -1,44 +1,44 @@ export class CheckUrls { - #urls + #urls; constructor(urls) { - this.#urls = urls + this.#urls = urls; } [Symbol.asyncIterator]() { - const urlsIterator = Iterator.from(this.#urls) + const urlsIterator = Iterator.from(this.#urls); return { async next() { - const iteratorResult = urlsIterator.next() + const iteratorResult = urlsIterator.next(); if (iteratorResult.done) { - return { done: true } + return { done: true }; } - const url = iteratorResult.value + const url = iteratorResult.value; try { const checkResult = await fetch(url, { - method: 'HEAD', - redirect: 'follow', + method: "HEAD", + redirect: "follow", signal: AbortSignal.timeout(5000), // 5 secs timeout - }) + }); if (!checkResult.ok) { return { done: false, value: `${url} is down, error: ${checkResult.status} ${checkResult.statusText}`, - } + }; } return { done: false, value: `${url} is up, status: ${checkResult.status}`, - } + }; } catch (err) { return { done: false, value: `${url} is down, error: ${err.message}`, - } + }; } }, - } + }; } } diff --git a/09-behavioral-design-patterns/11-async-iterator-check-urls/index.js b/09-behavioral-design-patterns/11-async-iterator-check-urls/index.js index 2d15006..f4a36ef 100644 --- a/09-behavioral-design-patterns/11-async-iterator-check-urls/index.js +++ b/09-behavioral-design-patterns/11-async-iterator-check-urls/index.js @@ -1,18 +1,18 @@ -import { CheckUrls } from './checkUrls.js' +import { CheckUrls } from "./checkUrls.js"; const checkUrls = new CheckUrls([ - 'https://nodejsdesignpatterns.com', - 'https://example.com', - 'https://mustbedownforsurehopefully.com', - 'https://loige.co', - 'https://mario.fyi', - 'https://httpstat.us/200', - 'https://httpstat.us/301', - 'https://httpstat.us/404', - 'https://httpstat.us/500', - 'https://httpstat.us/200?sleep=6000', -]) + "https://nodejsdesignpatterns.com", + "https://example.com", + "https://mustbedownforsurehopefully.com", + "https://loige.co", + "https://mario.fyi", + "https://httpstat.us/200", + "https://httpstat.us/301", + "https://httpstat.us/404", + "https://httpstat.us/500", + "https://httpstat.us/200?sleep=6000", +]); for await (const status of checkUrls) { - console.log(status) + console.log(status); } diff --git a/09-behavioral-design-patterns/12-async-generator-check-urls/checkUrls.js b/09-behavioral-design-patterns/12-async-generator-check-urls/checkUrls.js index 0de2978..8c04383 100644 --- a/09-behavioral-design-patterns/12-async-generator-check-urls/checkUrls.js +++ b/09-behavioral-design-patterns/12-async-generator-check-urls/checkUrls.js @@ -1,21 +1,21 @@ export class CheckUrls { constructor(urls) { - this.urls = urls + this.urls = urls; } async *[Symbol.asyncIterator]() { for (const url of this.urls) { try { const checkResult = await fetch(url, { - method: 'HEAD', - redirect: 'follow', + method: "HEAD", + redirect: "follow", signal: AbortSignal.timeout(5000), // 5 secs timeout - }) + }); checkResult.ok ? yield `${url} is up, status: ${checkResult.status}` - : yield `${url} is down, error: ${checkResult.status} ${checkResult.statusText}` + : yield `${url} is down, error: ${checkResult.status} ${checkResult.statusText}`; } catch (err) { - yield `${url} is down, error: ${err.message}` + yield `${url} is down, error: ${err.message}`; } } } diff --git a/09-behavioral-design-patterns/12-async-generator-check-urls/index.js b/09-behavioral-design-patterns/12-async-generator-check-urls/index.js index 2d15006..f4a36ef 100644 --- a/09-behavioral-design-patterns/12-async-generator-check-urls/index.js +++ b/09-behavioral-design-patterns/12-async-generator-check-urls/index.js @@ -1,18 +1,18 @@ -import { CheckUrls } from './checkUrls.js' +import { CheckUrls } from "./checkUrls.js"; const checkUrls = new CheckUrls([ - 'https://nodejsdesignpatterns.com', - 'https://example.com', - 'https://mustbedownforsurehopefully.com', - 'https://loige.co', - 'https://mario.fyi', - 'https://httpstat.us/200', - 'https://httpstat.us/301', - 'https://httpstat.us/404', - 'https://httpstat.us/500', - 'https://httpstat.us/200?sleep=6000', -]) + "https://nodejsdesignpatterns.com", + "https://example.com", + "https://mustbedownforsurehopefully.com", + "https://loige.co", + "https://mario.fyi", + "https://httpstat.us/200", + "https://httpstat.us/301", + "https://httpstat.us/404", + "https://httpstat.us/500", + "https://httpstat.us/200?sleep=6000", +]); for await (const status of checkUrls) { - console.log(status) + console.log(status); } diff --git a/09-behavioral-design-patterns/13-async-iterator-streams/index.js b/09-behavioral-design-patterns/13-async-iterator-streams/index.js index cff51a1..dee1807 100644 --- a/09-behavioral-design-patterns/13-async-iterator-streams/index.js +++ b/09-behavioral-design-patterns/13-async-iterator-streams/index.js @@ -1,6 +1,6 @@ -import split from 'split2' // v4.2.0 +import split from "split2"; // v4.2.0 -const stream = process.stdin.pipe(split()) +const stream = process.stdin.pipe(split()); for await (const line of stream) { - console.log(`You wrote: ${line}`) + console.log(`You wrote: ${line}`); } diff --git a/09-behavioral-design-patterns/14-async-iterator-utilities/checkUrls.js b/09-behavioral-design-patterns/14-async-iterator-utilities/checkUrls.js index 0de2978..8c04383 100644 --- a/09-behavioral-design-patterns/14-async-iterator-utilities/checkUrls.js +++ b/09-behavioral-design-patterns/14-async-iterator-utilities/checkUrls.js @@ -1,21 +1,21 @@ export class CheckUrls { constructor(urls) { - this.urls = urls + this.urls = urls; } async *[Symbol.asyncIterator]() { for (const url of this.urls) { try { const checkResult = await fetch(url, { - method: 'HEAD', - redirect: 'follow', + method: "HEAD", + redirect: "follow", signal: AbortSignal.timeout(5000), // 5 secs timeout - }) + }); checkResult.ok ? yield `${url} is up, status: ${checkResult.status}` - : yield `${url} is down, error: ${checkResult.status} ${checkResult.statusText}` + : yield `${url} is down, error: ${checkResult.status} ${checkResult.statusText}`; } catch (err) { - yield `${url} is down, error: ${err.message}` + yield `${url} is down, error: ${err.message}`; } } } diff --git a/09-behavioral-design-patterns/14-async-iterator-utilities/index.js b/09-behavioral-design-patterns/14-async-iterator-utilities/index.js index b8d2f2e..21d8ac9 100644 --- a/09-behavioral-design-patterns/14-async-iterator-utilities/index.js +++ b/09-behavioral-design-patterns/14-async-iterator-utilities/index.js @@ -1,29 +1,29 @@ -import { Readable } from 'node:stream' -import { CheckUrls } from './checkUrls.js' +import { Readable } from "node:stream"; +import { CheckUrls } from "./checkUrls.js"; const checkUrls = new CheckUrls([ - 'https://nodejsdesignpatterns.com', - 'https://loige.co', - 'https://mario.fyi', - 'https://httpstat.us/200', - 'https://httpstat.us/200?sleep=6000', -]) + "https://nodejsdesignpatterns.com", + "https://loige.co", + "https://mario.fyi", + "https://httpstat.us/200", + "https://httpstat.us/200?sleep=6000", +]); const stats = await Readable.from(checkUrls) - .map(status => { - console.log(status) - return status + .map((status) => { + console.log(status); + return status; }) .reduce( (acc, status) => { - if (status.includes(' is up,')) { - acc.up++ + if (status.includes(" is up,")) { + acc.up++; } else { - acc.down++ + acc.down++; } - return acc + return acc; }, - { up: 0, down: 0 } - ) + { up: 0, down: 0 }, + ); -console.log(stats) +console.log(stats); diff --git a/09-behavioral-design-patterns/15-middleware-zmq/client.js b/09-behavioral-design-patterns/15-middleware-zmq/client.js index c6caac9..e5df390 100644 --- a/09-behavioral-design-patterns/15-middleware-zmq/client.js +++ b/09-behavioral-design-patterns/15-middleware-zmq/client.js @@ -1,25 +1,25 @@ -import zeromq from 'zeromq' // v6.3.0 -import { jsonMiddleware } from './jsonMiddleware.js' -import { zlibMiddleware } from './zlibMiddleware.js' -import { ZmqMiddlewareManager } from './zmqMiddlewareManager.js' +import zeromq from "zeromq"; // v6.3.0 +import { jsonMiddleware } from "./jsonMiddleware.js"; +import { zlibMiddleware } from "./zlibMiddleware.js"; +import { ZmqMiddlewareManager } from "./zmqMiddlewareManager.js"; -const socket = new zeromq.Request() -await socket.connect('tcp://127.0.0.1:5000') +const socket = new zeromq.Request(); +await socket.connect("tcp://127.0.0.1:5000"); -const zmqm = new ZmqMiddlewareManager(socket) -zmqm.use(zlibMiddleware()) -zmqm.use(jsonMiddleware()) +const zmqm = new ZmqMiddlewareManager(socket); +zmqm.use(zlibMiddleware()); +zmqm.use(jsonMiddleware()); zmqm.use({ inbound(message) { - console.log('Echoed back', message) - return message + console.log("Echoed back", message); + return message; }, -}) +}); setInterval(() => { zmqm - .send({ action: 'ping', echo: Date.now() }) - .catch(err => console.error(err)) -}, 1000) + .send({ action: "ping", echo: Date.now() }) + .catch((err) => console.error(err)); +}, 1000); -console.log('Client connected') +console.log("Client connected"); diff --git a/09-behavioral-design-patterns/15-middleware-zmq/jsonMiddleware.js b/09-behavioral-design-patterns/15-middleware-zmq/jsonMiddleware.js index 8a8d8c5..6c9e2b5 100644 --- a/09-behavioral-design-patterns/15-middleware-zmq/jsonMiddleware.js +++ b/09-behavioral-design-patterns/15-middleware-zmq/jsonMiddleware.js @@ -1,10 +1,10 @@ export function jsonMiddleware() { return { inbound(message) { - return JSON.parse(message.toString()) + return JSON.parse(message.toString()); }, outbound(message) { - return Buffer.from(JSON.stringify(message)) + return Buffer.from(JSON.stringify(message)); }, - } + }; } diff --git a/09-behavioral-design-patterns/15-middleware-zmq/server.js b/09-behavioral-design-patterns/15-middleware-zmq/server.js index 960200b..5ec3af5 100644 --- a/09-behavioral-design-patterns/15-middleware-zmq/server.js +++ b/09-behavioral-design-patterns/15-middleware-zmq/server.js @@ -1,22 +1,22 @@ -import zeromq from 'zeromq' // v6.3.0 -import { jsonMiddleware } from './jsonMiddleware.js' -import { zlibMiddleware } from './zlibMiddleware.js' -import { ZmqMiddlewareManager } from './zmqMiddlewareManager.js' +import zeromq from "zeromq"; // v6.3.0 +import { jsonMiddleware } from "./jsonMiddleware.js"; +import { zlibMiddleware } from "./zlibMiddleware.js"; +import { ZmqMiddlewareManager } from "./zmqMiddlewareManager.js"; -const socket = new zeromq.Reply() -await socket.bind('tcp://127.0.0.1:5000') +const socket = new zeromq.Reply(); +await socket.bind("tcp://127.0.0.1:5000"); -const zmqm = new ZmqMiddlewareManager(socket) -zmqm.use(zlibMiddleware()) -zmqm.use(jsonMiddleware()) +const zmqm = new ZmqMiddlewareManager(socket); +zmqm.use(zlibMiddleware()); +zmqm.use(jsonMiddleware()); zmqm.use({ async inbound(message) { - console.log('Received', message) - if (message.action === 'ping') { - await this.send({ action: 'pong', echo: message.echo }) + console.log("Received", message); + if (message.action === "ping") { + await this.send({ action: "pong", echo: message.echo }); } - return message + return message; }, -}) +}); -console.log('Server started') +console.log("Server started"); diff --git a/09-behavioral-design-patterns/15-middleware-zmq/zlibMiddleware.js b/09-behavioral-design-patterns/15-middleware-zmq/zlibMiddleware.js index 2ed0ae2..7a997fb 100644 --- a/09-behavioral-design-patterns/15-middleware-zmq/zlibMiddleware.js +++ b/09-behavioral-design-patterns/15-middleware-zmq/zlibMiddleware.js @@ -1,16 +1,16 @@ -import { promisify } from 'node:util' -import { deflateRaw, inflateRaw } from 'node:zlib' +import { promisify } from "node:util"; +import { deflateRaw, inflateRaw } from "node:zlib"; -const inflateRawAsync = promisify(inflateRaw) -const deflateRawAsync = promisify(deflateRaw) +const inflateRawAsync = promisify(inflateRaw); +const deflateRawAsync = promisify(deflateRaw); export function zlibMiddleware() { return { inbound(message) { - return inflateRawAsync(Buffer.from(message)) + return inflateRawAsync(Buffer.from(message)); }, outbound(message) { - return deflateRawAsync(message) + return deflateRawAsync(message); }, - } + }; } diff --git a/09-behavioral-design-patterns/15-middleware-zmq/zmqMiddlewareManager.js b/09-behavioral-design-patterns/15-middleware-zmq/zmqMiddlewareManager.js index d52a6ac..8442554 100644 --- a/09-behavioral-design-patterns/15-middleware-zmq/zmqMiddlewareManager.js +++ b/09-behavioral-design-patterns/15-middleware-zmq/zmqMiddlewareManager.js @@ -1,45 +1,45 @@ export class ZmqMiddlewareManager { - #socket - #inboundMiddleware = [] - #outboundMiddleware = [] + #socket; + #inboundMiddleware = []; + #outboundMiddleware = []; constructor(socket) { - this.#socket = socket - this.#handleIncomingMessages() + this.#socket = socket; + this.#handleIncomingMessages(); } async #handleIncomingMessages() { for await (const [message] of this.#socket) { await this.#executeMiddleware(this.#inboundMiddleware, message).catch( - err => { - console.error('Error while processing the message', err) - } - ) + (err) => { + console.error("Error while processing the message", err); + }, + ); } } async send(message) { const finalMessage = await this.#executeMiddleware( this.#outboundMiddleware, - message - ) - return this.#socket.send(finalMessage) + message, + ); + return this.#socket.send(finalMessage); } use(middleware) { if (middleware.inbound) { - this.#inboundMiddleware.push(middleware.inbound) + this.#inboundMiddleware.push(middleware.inbound); } if (middleware.outbound) { - this.#outboundMiddleware.unshift(middleware.outbound) + this.#outboundMiddleware.unshift(middleware.outbound); } } async #executeMiddleware(middlewares, initialMessage) { - let message = initialMessage + let message = initialMessage; for await (const middlewareFunc of middlewares) { - message = await middlewareFunc.call(this, message) + message = await middlewareFunc.call(this, message); } - return message + return message; } } diff --git a/09-behavioral-design-patterns/16-command/client.js b/09-behavioral-design-patterns/16-command/client.js index 90ceb6e..d08e153 100644 --- a/09-behavioral-design-patterns/16-command/client.js +++ b/09-behavioral-design-patterns/16-command/client.js @@ -1,11 +1,11 @@ -import { createPostStatusCmd } from './createPostStatusCmd.js' -import { Invoker } from './invoker.js' -import { statusUpdateService } from './statusUpdateService.js' +import { createPostStatusCmd } from "./createPostStatusCmd.js"; +import { Invoker } from "./invoker.js"; +import { statusUpdateService } from "./statusUpdateService.js"; // The Client code -const invoker = new Invoker() -const command = createPostStatusCmd(statusUpdateService, 'HI!') -invoker.run(command) -invoker.undo() -invoker.delay(command, 1000 * 3) -invoker.runRemotely(command) +const invoker = new Invoker(); +const command = createPostStatusCmd(statusUpdateService, "HI!"); +invoker.run(command); +invoker.undo(); +invoker.delay(command, 1000 * 3); +invoker.runRemotely(command); diff --git a/09-behavioral-design-patterns/16-command/createPostStatusCmd.js b/09-behavioral-design-patterns/16-command/createPostStatusCmd.js index 6e14857..cca3c21 100644 --- a/09-behavioral-design-patterns/16-command/createPostStatusCmd.js +++ b/09-behavioral-design-patterns/16-command/createPostStatusCmd.js @@ -1,19 +1,19 @@ export function createPostStatusCmd(service, status) { - let postId = null + let postId = null; // The Command return { run() { - postId = service.postUpdate(status) + postId = service.postUpdate(status); }, undo() { if (postId) { - service.destroyUpdate(postId) - postId = null + service.destroyUpdate(postId); + postId = null; } }, serialize() { - return { type: 'status', action: 'post', status } + return { type: "status", action: "post", status }; }, - } + }; } diff --git a/09-behavioral-design-patterns/16-command/invoker.js b/09-behavioral-design-patterns/16-command/invoker.js index b95afef..4869d2e 100644 --- a/09-behavioral-design-patterns/16-command/invoker.js +++ b/09-behavioral-design-patterns/16-command/invoker.js @@ -1,35 +1,35 @@ // The Invoker export class Invoker { - #history = [] + #history = []; run(cmd) { - this.#history.push(cmd) - cmd.run() - console.log('Command executed', cmd.serialize()) + this.#history.push(cmd); + cmd.run(); + console.log("Command executed", cmd.serialize()); } delay(cmd, delay) { setTimeout(() => { - console.log('Executing delayed command', cmd.serialize()) - this.run(cmd) - }, delay) + console.log("Executing delayed command", cmd.serialize()); + this.run(cmd); + }, delay); } undo() { - const cmd = this.#history.pop() + const cmd = this.#history.pop(); if (cmd) { - cmd.undo() - console.log('Command undone', cmd.serialize()) + cmd.undo(); + console.log("Command undone", cmd.serialize()); } } async runRemotely(cmd) { - await fetch('http://localhost:3000/cmd', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, + await fetch("http://localhost:3000/cmd", { + method: "POST", + headers: { "Content-Type": "application/json" }, body: JSON.stringify(cmd.serialize()), - }) + }); - console.log('Command executed remotely', cmd.serialize()) + console.log("Command executed remotely", cmd.serialize()); } } diff --git a/09-behavioral-design-patterns/16-command/server.js b/09-behavioral-design-patterns/16-command/server.js index 7be5c69..5eba2d1 100644 --- a/09-behavioral-design-patterns/16-command/server.js +++ b/09-behavioral-design-patterns/16-command/server.js @@ -1,22 +1,22 @@ -import { createServer } from 'node:http' +import { createServer } from "node:http"; const server = createServer(async (request, response) => { - if (request.url !== '/cmd') { - response.writeHead(400) - response.end() - return + if (request.url !== "/cmd") { + response.writeHead(400); + response.end(); + return; } - let data = '' + let data = ""; for await (const chunk of request) { - data += chunk + data += chunk; } - console.log('Received the command:', data) - response.writeHead(200, { 'Content-Type': 'application/json' }) - response.end(JSON.stringify({ ok: true })) -}) + console.log("Received the command:", data); + response.writeHead(200, { "Content-Type": "application/json" }); + response.end(JSON.stringify({ ok: true })); +}); server.listen(3000, () => { - console.log('Server started') -}) + console.log("Server started"); +}); diff --git a/09-behavioral-design-patterns/16-command/statusUpdateService.js b/09-behavioral-design-patterns/16-command/statusUpdateService.js index e2c08c9..7553731 100644 --- a/09-behavioral-design-patterns/16-command/statusUpdateService.js +++ b/09-behavioral-design-patterns/16-command/statusUpdateService.js @@ -1,16 +1,16 @@ -const statusUpdates = new Map() +const statusUpdates = new Map(); // The Target export const statusUpdateService = { postUpdate(status) { - const id = Math.floor(Math.random() * 1000000) - statusUpdates.set(id, status) - console.log(`Status posted: ${status} (${id})`) - return id + const id = Math.floor(Math.random() * 1000000); + statusUpdates.set(id, status); + console.log(`Status posted: ${status} (${id})`); + return id; }, destroyUpdate(id) { - statusUpdates.delete(id) - console.log(`Status removed: ${id}`) + statusUpdates.delete(id); + console.log(`Status removed: ${id}`); }, -} +}; diff --git a/09-behavioral-design-patterns/17-command-s3-put-object/index.js b/09-behavioral-design-patterns/17-command-s3-put-object/index.js index 9000e23..f9bf7d1 100644 --- a/09-behavioral-design-patterns/17-command-s3-put-object/index.js +++ b/09-behavioral-design-patterns/17-command-s3-put-object/index.js @@ -1,17 +1,17 @@ -import { createReadStream } from 'node:fs' -import { basename } from 'node:path' -import { PutObjectCommand, S3Client } from '@aws-sdk/client-s3' // v3.750.0 +import { createReadStream } from "node:fs"; +import { basename } from "node:path"; +import { PutObjectCommand, S3Client } from "@aws-sdk/client-s3"; // v3.750.0 -const [bucketName, filePath] = process.argv.slice(2) +const [bucketName, filePath] = process.argv.slice(2); if (!(bucketName && filePath)) { - console.error('Usage: node index.js ') - process.exit(1) + console.error("Usage: node index.js "); + process.exit(1); } -const s3Client = new S3Client() -const fileStream = createReadStream(filePath) -const key = basename(filePath) +const s3Client = new S3Client(); +const fileStream = createReadStream(filePath); +const key = basename(filePath); // Set the parameters for the PutObject command. const params = { @@ -21,14 +21,14 @@ const params = { Key: key, // biome-ignore lint/style/useNamingConvention: Body: fileStream, -} +}; try { // Create a PutObject command object. - const putObjectCommand = new PutObjectCommand(params) + const putObjectCommand = new PutObjectCommand(params); // Send the command to S3. - const data = await s3Client.send(putObjectCommand) - console.log('Successfully uploaded file to S3', data) + const data = await s3Client.send(putObjectCommand); + console.log("Successfully uploaded file to S3", data); } catch (err) { - console.log('Error', err) + console.log("Error", err); } diff --git a/10-testing/01-first-test/calculateBasketTotal.js b/10-testing/01-first-test/calculateBasketTotal.js index 6b47bf3..0f4788d 100644 --- a/10-testing/01-first-test/calculateBasketTotal.js +++ b/10-testing/01-first-test/calculateBasketTotal.js @@ -1,7 +1,7 @@ export function calculateBasketTotal(basket) { - let total = 0 + let total = 0; for (const item of basket.items) { - total += item.unitPrice * item.quantity + total += item.unitPrice * item.quantity; } - return total + return total; } diff --git a/10-testing/01-first-test/test.js b/10-testing/01-first-test/test.js index 936ae92..afaeba1 100644 --- a/10-testing/01-first-test/test.js +++ b/10-testing/01-first-test/test.js @@ -1,22 +1,22 @@ -import { equal } from 'node:assert/strict' -import { calculateBasketTotal } from './calculateBasketTotal.js' +import { equal } from "node:assert/strict"; +import { calculateBasketTotal } from "./calculateBasketTotal.js"; // arrange const basket = { items: [ - { name: 'Croissant', unitPrice: 2, quantity: 2 }, - { name: 'Olive bread', unitPrice: 3, quantity: 1 }, + { name: "Croissant", unitPrice: 2, quantity: 2 }, + { name: "Olive bread", unitPrice: 3, quantity: 1 }, ], -} +}; // act -const result = calculateBasketTotal(basket) +const result = calculateBasketTotal(basket); // assert -const expectedTotal = 7 // (2 * 2) + (3 * 1) = 7 +const expectedTotal = 7; // (2 * 2) + (3 * 1) = 7 equal( result, expectedTotal, - `Expected total to be ${expectedTotal}, but got ${result}` -) -console.log('Test passed!') + `Expected total to be ${expectedTotal}, but got ${result}`, +); +console.log("Test passed!"); diff --git a/10-testing/02-first-test-with-runner/calculateBasketTotal.js b/10-testing/02-first-test-with-runner/calculateBasketTotal.js index 6b47bf3..0f4788d 100644 --- a/10-testing/02-first-test-with-runner/calculateBasketTotal.js +++ b/10-testing/02-first-test-with-runner/calculateBasketTotal.js @@ -1,7 +1,7 @@ export function calculateBasketTotal(basket) { - let total = 0 + let total = 0; for (const item of basket.items) { - total += item.unitPrice * item.quantity + total += item.unitPrice * item.quantity; } - return total + return total; } diff --git a/10-testing/02-first-test-with-runner/calculateBasketTotal.test.js b/10-testing/02-first-test-with-runner/calculateBasketTotal.test.js index 462b448..db0a056 100644 --- a/10-testing/02-first-test-with-runner/calculateBasketTotal.test.js +++ b/10-testing/02-first-test-with-runner/calculateBasketTotal.test.js @@ -1,36 +1,36 @@ -import { equal } from 'node:assert/strict' -import { test } from 'node:test' -import { calculateBasketTotal } from './calculateBasketTotal.js' +import { equal } from "node:assert/strict"; +import { test } from "node:test"; +import { calculateBasketTotal } from "./calculateBasketTotal.js"; -test('Calculates basket total', () => { +test("Calculates basket total", () => { const basket = { items: [ - { name: 'Croissant', unitPrice: 2, quantity: 2 }, - { name: 'Olive bread', unitPrice: 3, quantity: 1 }, + { name: "Croissant", unitPrice: 2, quantity: 2 }, + { name: "Olive bread", unitPrice: 3, quantity: 1 }, ], - } + }; - const result = calculateBasketTotal(basket) + const result = calculateBasketTotal(basket); - const expectedTotal = 7 + const expectedTotal = 7; equal( result, expectedTotal, - `Expected total to be ${expectedTotal}, but got ${result}` - ) -}) + `Expected total to be ${expectedTotal}, but got ${result}`, + ); +}); -test('Calculates basket total with no items', () => { +test("Calculates basket total with no items", () => { const basket = { items: [], - } + }; - const result = calculateBasketTotal(basket) + const result = calculateBasketTotal(basket); - const expectedTotal = 0 + const expectedTotal = 0; equal( result, expectedTotal, - `Expected total to be ${expectedTotal}, but got ${result}` - ) -}) + `Expected total to be ${expectedTotal}, but got ${result}`, + ); +}); diff --git a/10-testing/03-test-fn/example.test.js b/10-testing/03-test-fn/example.test.js index 962894a..7cfe4cd 100644 --- a/10-testing/03-test-fn/example.test.js +++ b/10-testing/03-test-fn/example.test.js @@ -1,22 +1,23 @@ -import { test } from 'node:test' +import { test } from "node:test"; -test('passing sync test', _t => {}) -test('failing sync test', _t => { - throw new Error('fail') -}) +test("passing sync test", (_t) => {}); +test("failing sync test", (_t) => { + throw new Error("fail"); +}); -test('passing async test with promise', _t => Promise.resolve()) -test('failing async test with promise', _t => Promise.reject(new Error('fail'))) +test("passing async test with promise", (_t) => Promise.resolve()); +test("failing async test with promise", (_t) => + Promise.reject(new Error("fail"))); -test('passing async test with async', async _t => {}) +test("passing async test with async", async (_t) => {}); // biome-ignore lint/suspicious/useAwait: just for demonstration -test('failing async test with async', async _t => { - throw new Error('fail') -}) +test("failing async test with async", async (_t) => { + throw new Error("fail"); +}); -test('passing async test with callback', (_t, done) => done()) -test('failing async test with callback', (_t, done) => done(new Error('fail'))) +test("passing async test with callback", (_t, done) => done()); +test("failing async test with callback", (_t, done) => done(new Error("fail"))); // biome-ignore lint/suspicious/useAwait: just for demonstration -test('invalid test: both cb and promise (async)', async (_t, done) => { - done() -}) +test("invalid test: both cb and promise (async)", async (_t, done) => { + done(); +}); diff --git a/10-testing/04-subtests/example.test.js b/10-testing/04-subtests/example.test.js index 821cccf..1f1b811 100644 --- a/10-testing/04-subtests/example.test.js +++ b/10-testing/04-subtests/example.test.js @@ -1,11 +1,11 @@ -import { test } from 'node:test' +import { test } from "node:test"; -test('Top level test', t => { - t.test('Subtest 1', _t => { +test("Top level test", (t) => { + t.test("Subtest 1", (_t) => { // ... - }) + }); - t.test('Subtest 2', _t => { + t.test("Subtest 2", (_t) => { // ... - }) -}) + }); +}); diff --git a/10-testing/05-test-concurrency/example.test.js b/10-testing/05-test-concurrency/example.test.js index 2828a7a..8c41daa 100644 --- a/10-testing/05-test-concurrency/example.test.js +++ b/10-testing/05-test-concurrency/example.test.js @@ -1,11 +1,11 @@ -import { test } from 'node:test' +import { test } from "node:test"; -test('Top level test', { concurrency: true }, t => { - t.test('Subtest 1', _t => { +test("Top level test", { concurrency: true }, (t) => { + t.test("Subtest 1", (_t) => { // ... - }) + }); - t.test('Subtest 2', _t => { + t.test("Subtest 2", (_t) => { // ... - }) -}) + }); +}); diff --git a/10-testing/06-programmatic-tests/calculateBasketTotal.js b/10-testing/06-programmatic-tests/calculateBasketTotal.js index 6b47bf3..0f4788d 100644 --- a/10-testing/06-programmatic-tests/calculateBasketTotal.js +++ b/10-testing/06-programmatic-tests/calculateBasketTotal.js @@ -1,7 +1,7 @@ export function calculateBasketTotal(basket) { - let total = 0 + let total = 0; for (const item of basket.items) { - total += item.unitPrice * item.quantity + total += item.unitPrice * item.quantity; } - return total + return total; } diff --git a/10-testing/06-programmatic-tests/calculateBasketTotal.test.js b/10-testing/06-programmatic-tests/calculateBasketTotal.test.js index d5c8a54..12bd7c4 100644 --- a/10-testing/06-programmatic-tests/calculateBasketTotal.test.js +++ b/10-testing/06-programmatic-tests/calculateBasketTotal.test.js @@ -1,39 +1,39 @@ -import { equal } from 'node:assert/strict' -import { test } from 'node:test' -import { calculateBasketTotal } from './calculateBasketTotal.js' +import { equal } from "node:assert/strict"; +import { test } from "node:test"; +import { calculateBasketTotal } from "./calculateBasketTotal.js"; -test('Calculates basket total', { concurrency: true }, t => { +test("Calculates basket total", { concurrency: true }, (t) => { const cases = [ { - name: 'Empty basket', + name: "Empty basket", basket: { items: [] }, expectedTotal: 0, }, { - name: 'One croissant', - basket: { items: [{ name: 'Croissant', unitPrice: 2, quantity: 1 }] }, + name: "One croissant", + basket: { items: [{ name: "Croissant", unitPrice: 2, quantity: 1 }] }, expectedTotal: 2, }, { - name: 'Two croissants and one olive bread', + name: "Two croissants and one olive bread", basket: { items: [ - { name: 'Croissant', unitPrice: 2, quantity: 2 }, - { name: 'Olive bread', unitPrice: 3, quantity: 1 }, + { name: "Croissant", unitPrice: 2, quantity: 2 }, + { name: "Olive bread", unitPrice: 3, quantity: 1 }, ], }, expectedTotal: 7, }, - ] + ]; for (const { name, basket, expectedTotal } of cases) { t.test(name, () => { - const result = calculateBasketTotal(basket) + const result = calculateBasketTotal(basket); equal( result, expectedTotal, - `Expected total to be ${expectedTotal}, but got ${result}` - ) - }) + `Expected total to be ${expectedTotal}, but got ${result}`, + ); + }); } -}) +}); diff --git a/10-testing/07-test-suite/example.test.js b/10-testing/07-test-suite/example.test.js index cb730f6..f9cff84 100644 --- a/10-testing/07-test-suite/example.test.js +++ b/10-testing/07-test-suite/example.test.js @@ -1,9 +1,9 @@ -import { suite, test } from 'node:test' +import { suite, test } from "node:test"; -suite('Top level suite', { concurrency: true }, () => { - test('Test 1', () => {}) - test('Test 2', () => {}) -}) +suite("Top level suite", { concurrency: true }, () => { + test("Test 1", () => {}); + test("Test 2", () => {}); +}); // Equivalent to: // import { describe, it } from 'node:test' diff --git a/10-testing/08-test-coverage/calculateBasketTotal.js b/10-testing/08-test-coverage/calculateBasketTotal.js index 6b47bf3..0f4788d 100644 --- a/10-testing/08-test-coverage/calculateBasketTotal.js +++ b/10-testing/08-test-coverage/calculateBasketTotal.js @@ -1,7 +1,7 @@ export function calculateBasketTotal(basket) { - let total = 0 + let total = 0; for (const item of basket.items) { - total += item.unitPrice * item.quantity + total += item.unitPrice * item.quantity; } - return total + return total; } diff --git a/10-testing/08-test-coverage/calculateBasketTotal.test.js b/10-testing/08-test-coverage/calculateBasketTotal.test.js index fac18c6..c8c78c6 100644 --- a/10-testing/08-test-coverage/calculateBasketTotal.test.js +++ b/10-testing/08-test-coverage/calculateBasketTotal.test.js @@ -1,11 +1,11 @@ -import { equal } from 'node:assert/strict' -import { test } from 'node:test' -import { calculateBasketTotal } from './calculateBasketTotal.js' +import { equal } from "node:assert/strict"; +import { test } from "node:test"; +import { calculateBasketTotal } from "./calculateBasketTotal.js"; -test('Calculates basket total', { concurrency: true }, t => { +test("Calculates basket total", { concurrency: true }, (t) => { const cases = [ { - name: 'Empty basket', + name: "Empty basket", basket: { items: [] }, expectedTotal: 0, }, @@ -25,16 +25,16 @@ test('Calculates basket total', { concurrency: true }, t => { // }, // expectedTotal: 7, // }, - ] + ]; for (const { name, basket, expectedTotal } of cases) { t.test(name, () => { - const result = calculateBasketTotal(basket) + const result = calculateBasketTotal(basket); equal( result, expectedTotal, - `Expected total to be ${expectedTotal}, but got ${result}` - ) - }) + `Expected total to be ${expectedTotal}, but got ${result}`, + ); + }); } -}) +}); diff --git a/10-testing/09-test-with-typescript/calculateBasketTotal.test.ts b/10-testing/09-test-with-typescript/calculateBasketTotal.test.ts index ed7e391..09fbff3 100644 --- a/10-testing/09-test-with-typescript/calculateBasketTotal.test.ts +++ b/10-testing/09-test-with-typescript/calculateBasketTotal.test.ts @@ -1,39 +1,39 @@ -import { equal } from 'node:assert/strict' -import { test } from 'node:test' -import { calculateBasketTotal } from './calculateBasketTotal.ts' +import { equal } from "node:assert/strict"; +import { test } from "node:test"; +import { calculateBasketTotal } from "./calculateBasketTotal.ts"; -test('Calculates basket total', { concurrency: true }, t => { +test("Calculates basket total", { concurrency: true }, (t) => { const cases = [ { - name: 'Empty basket', + name: "Empty basket", basket: { items: [] }, expectedTotal: 0, }, { - name: 'One croissant', - basket: { items: [{ name: 'Croissant', unitPrice: 2, quantity: 1 }] }, + name: "One croissant", + basket: { items: [{ name: "Croissant", unitPrice: 2, quantity: 1 }] }, expectedTotal: 2, }, { - name: 'Two croissants and one olive bread', + name: "Two croissants and one olive bread", basket: { items: [ - { name: 'Croissant', unitPrice: 2, quantity: 2 }, - { name: 'Olive bread', unitPrice: 3, quantity: 1 }, + { name: "Croissant", unitPrice: 2, quantity: 2 }, + { name: "Olive bread", unitPrice: 3, quantity: 1 }, ], }, expectedTotal: 7, }, - ] + ]; for (const { name, basket, expectedTotal } of cases) { t.test(name, () => { - const result = calculateBasketTotal(basket) + const result = calculateBasketTotal(basket); equal( result, expectedTotal, - `Expected total to be ${expectedTotal}, but got ${result}` - ) - }) + `Expected total to be ${expectedTotal}, but got ${result}`, + ); + }); } -}) +}); diff --git a/10-testing/09-test-with-typescript/calculateBasketTotal.ts b/10-testing/09-test-with-typescript/calculateBasketTotal.ts index e2dec95..1e15932 100644 --- a/10-testing/09-test-with-typescript/calculateBasketTotal.ts +++ b/10-testing/09-test-with-typescript/calculateBasketTotal.ts @@ -1,17 +1,17 @@ export type BasketItem = { - name: string - unitPrice: number - quantity: number -} + name: string; + unitPrice: number; + quantity: number; +}; export type Basket = { - items: BasketItem[] -} + items: BasketItem[]; +}; export function calculateBasketTotal(basket: Basket): number { - let total = 0 + let total = 0; for (const item of basket.items) { - total += item.unitPrice * item.quantity + total += item.unitPrice * item.quantity; } - return total + return total; } diff --git a/10-testing/10-unit-test-async-code/TaskQueue.js b/10-testing/10-unit-test-async-code/TaskQueue.js index c1bd52f..4395f37 100644 --- a/10-testing/10-unit-test-async-code/TaskQueue.js +++ b/10-testing/10-unit-test-async-code/TaskQueue.js @@ -1,35 +1,35 @@ -import { EventEmitter } from 'node:events' +import { EventEmitter } from "node:events"; export class TaskQueue extends EventEmitter { constructor(concurrency) { - super() - this.concurrency = concurrency - this.running = 0 - this.queue = [] + super(); + this.concurrency = concurrency; + this.running = 0; + this.queue = []; } pushTask(task) { - this.queue.push(task) - process.nextTick(this.next.bind(this)) - return this + this.queue.push(task); + process.nextTick(this.next.bind(this)); + return this; } next() { if (this.running === 0 && this.queue.length === 0) { - return this.emit('empty') + return this.emit("empty"); } while (this.running < this.concurrency && this.queue.length > 0) { - const task = this.queue.shift() + const task = this.queue.shift(); task() - .catch(err => { - this.emit('taskError', err) + .catch((err) => { + this.emit("taskError", err); }) .finally(() => { - this.running-- - this.next() - }) - this.running++ + this.running--; + this.next(); + }); + this.running++; } } @@ -37,6 +37,6 @@ export class TaskQueue extends EventEmitter { return { running: this.running, scheduled: this.queue.length, - } + }; } } diff --git a/10-testing/10-unit-test-async-code/TaskQueue.test.js b/10-testing/10-unit-test-async-code/TaskQueue.test.js index af0ef3d..89844e7 100644 --- a/10-testing/10-unit-test-async-code/TaskQueue.test.js +++ b/10-testing/10-unit-test-async-code/TaskQueue.test.js @@ -1,113 +1,113 @@ -import assert from 'node:assert/strict' -import { once } from 'node:events' -import { mock, suite, test } from 'node:test' -import { setImmediate } from 'node:timers/promises' -import { TaskQueue } from './TaskQueue.js' - -suite('TaskQueue', { concurrency: true, timeout: 500 }, () => { - test('All tasks are executed and empty is emitted', async () => { - const queue = new TaskQueue(2) - const task1status = Promise.withResolvers() - let task1Completed = false - const task2status = Promise.withResolvers() - let task2Completed = false +import assert from "node:assert/strict"; +import { once } from "node:events"; +import { mock, suite, test } from "node:test"; +import { setImmediate } from "node:timers/promises"; +import { TaskQueue } from "./TaskQueue.js"; + +suite("TaskQueue", { concurrency: true, timeout: 500 }, () => { + test("All tasks are executed and empty is emitted", async () => { + const queue = new TaskQueue(2); + const task1status = Promise.withResolvers(); + let task1Completed = false; + const task2status = Promise.withResolvers(); + let task2Completed = false; const task1 = async () => { - await setImmediate() - task1Completed = true - task1status.resolve() - } + await setImmediate(); + task1Completed = true; + task1status.resolve(); + }; const task2 = async () => { - await setImmediate() - task2Completed = true - task2status.resolve() - } + await setImmediate(); + task2Completed = true; + task2status.resolve(); + }; - queue.pushTask(task1).pushTask(task2) - await Promise.allSettled([task1status.promise, task2status.promise]) + queue.pushTask(task1).pushTask(task2); + await Promise.allSettled([task1status.promise, task2status.promise]); - assert.ok(task1Completed, 'Task 1 completed') - assert.ok(task2Completed, 'Task 2 completed') - await once(queue, 'empty') - }) + assert.ok(task1Completed, "Task 1 completed"); + assert.ok(task2Completed, "Task 2 completed"); + await once(queue, "empty"); + }); - test('All tasks are executed and empty is emitted (v2)', async () => { - const queue = new TaskQueue(2) + test("All tasks are executed and empty is emitted (v2)", async () => { + const queue = new TaskQueue(2); const task1 = mock.fn(async () => { - await setImmediate() - return 'completed' - }) + await setImmediate(); + return "completed"; + }); const task2 = mock.fn(async () => { - await setImmediate() - return 'completed' - }) - - queue.pushTask(task1).pushTask(task2) - await once(queue, 'empty') - - assert.equal(task1.mock.callCount(), 1) - assert.equal(task2.mock.callCount(), 1) - }) - - test('Respect the concurrency limit', async () => { - const queue = new TaskQueue(4) - let runningTasks = 0 - let maxRunningTasks = 0 - let completedTasks = 0 + await setImmediate(); + return "completed"; + }); + + queue.pushTask(task1).pushTask(task2); + await once(queue, "empty"); + + assert.equal(task1.mock.callCount(), 1); + assert.equal(task2.mock.callCount(), 1); + }); + + test("Respect the concurrency limit", async () => { + const queue = new TaskQueue(4); + let runningTasks = 0; + let maxRunningTasks = 0; + let completedTasks = 0; const task = async () => { - runningTasks++ - maxRunningTasks = Math.max(maxRunningTasks, runningTasks) - await setImmediate() - runningTasks-- - completedTasks++ - } + runningTasks++; + maxRunningTasks = Math.max(maxRunningTasks, runningTasks); + await setImmediate(); + runningTasks--; + completedTasks++; + }; queue .pushTask(task) .pushTask(task) .pushTask(task) .pushTask(task) - .pushTask(task) - await once(queue, 'empty') + .pushTask(task); + await once(queue, "empty"); - assert.equal(maxRunningTasks, 4) - assert.equal(completedTasks, 5) - }) + assert.equal(maxRunningTasks, 4); + assert.equal(completedTasks, 5); + }); test('Emits "taskError" on task failure', async () => { - const queue = new TaskQueue(2) - const errors = [] - queue.on('taskError', error => { - errors.push(error) - }) + const queue = new TaskQueue(2); + const errors = []; + queue.on("taskError", (error) => { + errors.push(error); + }); queue.pushTask(async () => { - await setImmediate() - throw new Error('error1') - }) + await setImmediate(); + throw new Error("error1"); + }); queue.pushTask(async () => { - await setImmediate() - throw new Error('error2') - }) - await once(queue, 'empty') - - assert.equal(errors.length, 2) - assert.equal(errors[0].message, 'error1') - assert.equal(errors[1].message, 'error2') - }) - - test.todo('stats() returns correct counts', async () => { - const queue = new TaskQueue(1) + await setImmediate(); + throw new Error("error2"); + }); + await once(queue, "empty"); + + assert.equal(errors.length, 2); + assert.equal(errors[0].message, "error1"); + assert.equal(errors[1].message, "error2"); + }); + + test.todo("stats() returns correct counts", async () => { + const queue = new TaskQueue(1); const task = async () => { - await setImmediate() - } + await setImmediate(); + }; - queue.pushTask(task).pushTask(task) - await setImmediate() + queue.pushTask(task).pushTask(task); + await setImmediate(); - assert.deepEqual(queue.stats(), { running: 1, scheduled: 1 }) - await once(queue, 'empty') - assert.deepEqual(queue.stats(), { running: 0, scheduled: 0 }) - }) -}) + assert.deepEqual(queue.stats(), { running: 1, scheduled: 1 }); + await once(queue, "empty"); + assert.deepEqual(queue.stats(), { running: 0, scheduled: 0 }); + }); +}); diff --git a/10-testing/11-unit-test-mock-http/getPageLinks.js b/10-testing/11-unit-test-mock-http/getPageLinks.js index afcb6d3..fb689f2 100644 --- a/10-testing/11-unit-test-mock-http/getPageLinks.js +++ b/10-testing/11-unit-test-mock-http/getPageLinks.js @@ -1,33 +1,33 @@ -import { Parser } from 'htmlparser2' // v9.1.0 +import { Parser } from "htmlparser2"; // v9.1.0 export async function getInternalLinks(pageUrl) { - const url = new URL(pageUrl) - const response = await fetch(pageUrl) + const url = new URL(pageUrl); + const response = await fetch(pageUrl); if (!response.ok) { - throw new Error(`Failed to fetch ${pageUrl}: ${response.statusText}`) + throw new Error(`Failed to fetch ${pageUrl}: ${response.statusText}`); } - const contentType = response.headers.get('content-type') - if (contentType === null || !contentType.includes('text/html')) { - throw new Error('The current URL is not a HTML page') + const contentType = response.headers.get("content-type"); + if (contentType === null || !contentType.includes("text/html")) { + throw new Error("The current URL is not a HTML page"); } - const body = await response.text() + const body = await response.text(); - const internalLinks = new Set() + const internalLinks = new Set(); const parser = new Parser({ onopentag(name, attribs) { - if (name === 'a' && attribs.href) { - const newUrl = new URL(attribs.href, url) + if (name === "a" && attribs.href) { + const newUrl = new URL(attribs.href, url); if ( newUrl.hostname === url.hostname && newUrl.pathname !== url.pathname ) { - internalLinks.add(newUrl.toString()) + internalLinks.add(newUrl.toString()); } } }, - }) - parser.end(body) - return internalLinks + }); + parser.end(body); + return internalLinks; } diff --git a/10-testing/11-unit-test-mock-http/getPageLinks.test.js b/10-testing/11-unit-test-mock-http/getPageLinks.test.js index 8aa09ed..f79a10c 100644 --- a/10-testing/11-unit-test-mock-http/getPageLinks.test.js +++ b/10-testing/11-unit-test-mock-http/getPageLinks.test.js @@ -1,9 +1,9 @@ -import assert from 'node:assert/strict' -import { afterEach, beforeEach, suite, test } from 'node:test' -import { MockAgent, getGlobalDispatcher, setGlobalDispatcher } from 'undici' // v7.6.0 -import { getInternalLinks } from './getPageLinks.js' +import assert from "node:assert/strict"; +import { afterEach, beforeEach, suite, test } from "node:test"; +import { getGlobalDispatcher, MockAgent, setGlobalDispatcher } from "undici"; // v7.6.0 +import { getInternalLinks } from "./getPageLinks.js"; -suite('getPageLinks', { concurrency: true, timeout: 500 }, () => { +suite("getPageLinks", { concurrency: true, timeout: 500 }, () => { // naive implementation (real HTTP request) // test('It fetches all the internal links from a page', async () => { // const links = await getInternalLinks('https://loige.co') @@ -17,7 +17,7 @@ suite('getPageLinks', { concurrency: true, timeout: 500 }, () => { // ) // }) - test('It fetches all the internal links from a page', async t => { + test("It fetches all the internal links from a page", async (t) => { const mockHtml = ` @@ -28,34 +28,34 @@ suite('getPageLinks', { concurrency: true, timeout: 500 }, () => { About - ` + `; - t.mock.method(global, 'fetch', async _url => ({ + t.mock.method(global, "fetch", async (_url) => ({ ok: true, status: 200, headers: { - get: key => - key === 'content-type' ? 'text/html; charset=utf-8' : null, + get: (key) => + key === "content-type" ? "text/html; charset=utf-8" : null, }, text: async () => mockHtml, - })) + })); - const links = await getInternalLinks('https://loige.co') + const links = await getInternalLinks("https://loige.co"); assert.deepEqual( links, new Set([ - 'https://loige.co/blog', - 'https://loige.co/speaking', - 'https://loige.co/about', - ]) - ) - }) + "https://loige.co/blog", + "https://loige.co/speaking", + "https://loige.co/about", + ]), + ); + }); - test('It fetches all the internal links from a page (with undici)', async _t => { - const agent = new MockAgent() - agent.disableNetConnect() - setGlobalDispatcher(agent) + test("It fetches all the internal links from a page (with undici)", async (_t) => { + const agent = new MockAgent(); + agent.disableNetConnect(); + setGlobalDispatcher(agent); const mockHtml = ` @@ -67,51 +67,51 @@ suite('getPageLinks', { concurrency: true, timeout: 500 }, () => { About - ` + `; agent - .get('https://loige.co') + .get("https://loige.co") .intercept({ - path: '/', - method: 'GET', + path: "/", + method: "GET", }) .reply(200, mockHtml, { headers: { - 'content-type': 'text/html; charset=utf-8', + "content-type": "text/html; charset=utf-8", }, - }) + }); - const links = await getInternalLinks('https://loige.co') + const links = await getInternalLinks("https://loige.co"); assert.deepEqual( links, new Set([ - 'https://loige.co/blog', - 'https://loige.co/speaking', - 'https://loige.co/about', - ]) - ) - }) -}) + "https://loige.co/blog", + "https://loige.co/speaking", + "https://loige.co/about", + ]), + ); + }); +}); -suite('example with undici and beforeEach + afterEach', () => { - let agent - const originalGlobalDispatcher = getGlobalDispatcher() +suite("example with undici and beforeEach + afterEach", () => { + let agent; + const originalGlobalDispatcher = getGlobalDispatcher(); beforeEach(() => { - agent = new MockAgent() - agent.disableNetConnect() - setGlobalDispatcher(agent) - }) + agent = new MockAgent(); + agent.disableNetConnect(); + setGlobalDispatcher(agent); + }); afterEach(() => { - setGlobalDispatcher(originalGlobalDispatcher) - }) + setGlobalDispatcher(originalGlobalDispatcher); + }); - test('a test...', () => { + test("a test...", () => { /* ... */ - }) - test('another test...', () => { + }); + test("another test...", () => { /* ... */ - }) -}) + }); +}); diff --git a/10-testing/12-unit-test-mock-core-modules/saveConfig.js b/10-testing/12-unit-test-mock-core-modules/saveConfig.js index b59e3a4..31d05e6 100644 --- a/10-testing/12-unit-test-mock-core-modules/saveConfig.js +++ b/10-testing/12-unit-test-mock-core-modules/saveConfig.js @@ -1,15 +1,15 @@ -import { access, mkdir, writeFile } from 'node:fs/promises' -import { dirname } from 'node:path' +import { access, mkdir, writeFile } from "node:fs/promises"; +import { dirname } from "node:path"; export async function saveConfig(path, config) { - const folder = dirname(path) + const folder = dirname(path); try { - await access(folder) + await access(folder); } catch { - await mkdir(folder, { recursive: true }) + await mkdir(folder, { recursive: true }); } - const json = JSON.stringify(config, null, 2) - await writeFile(path, json, 'utf-8') + const json = JSON.stringify(config, null, 2); + await writeFile(path, json, "utf-8"); } diff --git a/10-testing/12-unit-test-mock-core-modules/saveConfig.test.js b/10-testing/12-unit-test-mock-core-modules/saveConfig.test.js index bba6727..2f1b748 100644 --- a/10-testing/12-unit-test-mock-core-modules/saveConfig.test.js +++ b/10-testing/12-unit-test-mock-core-modules/saveConfig.test.js @@ -1,48 +1,48 @@ // saveConfig.test.js -import assert from 'node:assert/strict' -import { mock, suite, test } from 'node:test' -import { setImmediate } from 'node:timers/promises' +import assert from "node:assert/strict"; +import { mock, suite, test } from "node:test"; +import { setImmediate } from "node:timers/promises"; // NOTE: concurrency: false is important here! -suite('saveConfig', { concurrency: false, timeout: 500 }, () => { - test('Creates folder (if needed)', async t => { - const mockMkdir = mock.fn() - const mockAccess = mock.fn(async _path => { - await setImmediate() - throw new Error('ENOENT') - }) - t.mock.module('node:fs/promises', { +suite("saveConfig", { concurrency: false, timeout: 500 }, () => { + test("Creates folder (if needed)", async (t) => { + const mockMkdir = mock.fn(); + const mockAccess = mock.fn(async (_path) => { + await setImmediate(); + throw new Error("ENOENT"); + }); + t.mock.module("node:fs/promises", { cache: false, namedExports: { access: mockAccess, mkdir: mockMkdir, writeFile: mock.fn(), }, - }) + }); - const { saveConfig } = await import('./saveConfig.js') - await saveConfig('./path/to/configs/app.json', { port: 3000 }) + const { saveConfig } = await import("./saveConfig.js"); + await saveConfig("./path/to/configs/app.json", { port: 3000 }); - assert.equal(mockMkdir.mock.callCount(), 1) - }) + assert.equal(mockMkdir.mock.callCount(), 1); + }); - test('Does not create folder (if exists)', async t => { - const mockMkdir = mock.fn() - const mockAccess = mock.fn(async _path => { - await setImmediate() - }) - t.mock.module('node:fs/promises', { + test("Does not create folder (if exists)", async (t) => { + const mockMkdir = mock.fn(); + const mockAccess = mock.fn(async (_path) => { + await setImmediate(); + }); + t.mock.module("node:fs/promises", { cache: false, namedExports: { access: mockAccess, mkdir: mockMkdir, writeFile: mock.fn(), }, - }) + }); - const { saveConfig } = await import('./saveConfig.js') - await saveConfig('./path/to/configs/app.json', { port: 3000 }) + const { saveConfig } = await import("./saveConfig.js"); + await saveConfig("./path/to/configs/app.json", { port: 3000 }); - assert.equal(mockMkdir.mock.callCount(), 0) - }) -}) + assert.equal(mockMkdir.mock.callCount(), 0); + }); +}); diff --git a/10-testing/13-unit-test-mock-other-modules/dbClient.js b/10-testing/13-unit-test-mock-other-modules/dbClient.js index e4cf6dc..39c54a2 100644 --- a/10-testing/13-unit-test-mock-other-modules/dbClient.js +++ b/10-testing/13-unit-test-mock-other-modules/dbClient.js @@ -2,6 +2,6 @@ export class DbClient { // biome-ignore lint/suspicious/useAwait: just for demonstration async query(_sql, _params) { // In real life, this would talk to a database - throw new Error('Not implemented') + throw new Error("Not implemented"); } } diff --git a/10-testing/13-unit-test-mock-other-modules/payments.js b/10-testing/13-unit-test-mock-other-modules/payments.js index 0f1da94..69ac4b0 100644 --- a/10-testing/13-unit-test-mock-other-modules/payments.js +++ b/10-testing/13-unit-test-mock-other-modules/payments.js @@ -1,6 +1,6 @@ -import { DbClient } from './dbClient.js' +import { DbClient } from "./dbClient.js"; -const db = new DbClient() +const db = new DbClient(); export async function canPayWithVouchers(userId, amount) { const vouchers = await db.query( @@ -8,10 +8,10 @@ export async function canPayWithVouchers(userId, amount) { WHERE user_id = ? AND balance > 0 AND expiresAt > NOW()`, - [userId] - ) + [userId], + ); - const availableBalance = vouchers.reduce((acc, v) => acc + v.balance, 0) + const availableBalance = vouchers.reduce((acc, v) => acc + v.balance, 0); - return availableBalance >= amount + return availableBalance >= amount; } diff --git a/10-testing/13-unit-test-mock-other-modules/payments.test.js b/10-testing/13-unit-test-mock-other-modules/payments.test.js index 7d11261..5279770 100644 --- a/10-testing/13-unit-test-mock-other-modules/payments.test.js +++ b/10-testing/13-unit-test-mock-other-modules/payments.test.js @@ -1,65 +1,65 @@ -import assert from 'node:assert/strict' -import { after, beforeEach, mock, suite, test } from 'node:test' -import { setImmediate } from 'node:timers/promises' +import assert from "node:assert/strict"; +import { after, beforeEach, mock, suite, test } from "node:test"; +import { setImmediate } from "node:timers/promises"; const sampleRecords = [ { id: 1, - userId: 'user1', + userId: "user1", balance: 10, expiresAt: new Date(Date.now() + 1000), }, { id: 2, - userId: 'user1', + userId: "user1", balance: 5, expiresAt: new Date(Date.now() + 1000), }, { id: 3, - userId: 'user1', + userId: "user1", balance: 3, expiresAt: new Date(Date.now() + 1000), }, -] +]; const queryMock = mock.fn(async (_sql, _params) => { - await setImmediate() - return sampleRecords -}) + await setImmediate(); + return sampleRecords; +}); -mock.module('./dbClient.js', { +mock.module("./dbClient.js", { cache: false, namedExports: { // biome-ignore lint/style/useNamingConvention: DbClient: class DbMock { - query = queryMock + query = queryMock; }, }, -}) +}); -const { canPayWithVouchers } = await import('./payments.js') +const { canPayWithVouchers } = await import("./payments.js"); -suite('canPayWithVouchers', { concurrency: false, timeout: 500 }, () => { +suite("canPayWithVouchers", { concurrency: false, timeout: 500 }, () => { beforeEach(() => { - queryMock.mock.resetCalls() - }) + queryMock.mock.resetCalls(); + }); after(() => { - queryMock.mock.restore() - }) + queryMock.mock.restore(); + }); - test('Returns true if balance is enough', async () => { - const result = await canPayWithVouchers('user1', 18) + test("Returns true if balance is enough", async () => { + const result = await canPayWithVouchers("user1", 18); - assert.equal(result, true) - assert.equal(queryMock.mock.callCount(), 1) - }) + assert.equal(result, true); + assert.equal(queryMock.mock.callCount(), 1); + }); - test('Returns false if balance is not enough', async () => { - const result = await canPayWithVouchers('user1', 19) + test("Returns false if balance is not enough", async () => { + const result = await canPayWithVouchers("user1", 19); - assert.equal(result, false) - assert.equal(queryMock.mock.callCount(), 1) - }) -}) + assert.equal(result, false); + assert.equal(queryMock.mock.callCount(), 1); + }); +}); diff --git a/10-testing/14-unit-test-dependency-injection/dbClient.js b/10-testing/14-unit-test-dependency-injection/dbClient.js index e4cf6dc..39c54a2 100644 --- a/10-testing/14-unit-test-dependency-injection/dbClient.js +++ b/10-testing/14-unit-test-dependency-injection/dbClient.js @@ -2,6 +2,6 @@ export class DbClient { // biome-ignore lint/suspicious/useAwait: just for demonstration async query(_sql, _params) { // In real life, this would talk to a database - throw new Error('Not implemented') + throw new Error("Not implemented"); } } diff --git a/10-testing/14-unit-test-dependency-injection/payments.js b/10-testing/14-unit-test-dependency-injection/payments.js index 4565c4a..05ea7ef 100644 --- a/10-testing/14-unit-test-dependency-injection/payments.js +++ b/10-testing/14-unit-test-dependency-injection/payments.js @@ -4,10 +4,10 @@ export async function canPayWithVouchers(db, userId, amount) { WHERE user_id = ? AND balance > 0 AND expiresAt > NOW()`, - [userId] - ) + [userId], + ); - const availableBalance = vouchers.reduce((acc, v) => acc + v.balance, 0) + const availableBalance = vouchers.reduce((acc, v) => acc + v.balance, 0); - return availableBalance >= amount + return availableBalance >= amount; } diff --git a/10-testing/14-unit-test-dependency-injection/payments.test.js b/10-testing/14-unit-test-dependency-injection/payments.test.js index 08f3a6c..f9b7520 100644 --- a/10-testing/14-unit-test-dependency-injection/payments.test.js +++ b/10-testing/14-unit-test-dependency-injection/payments.test.js @@ -1,55 +1,55 @@ -import assert from 'node:assert/strict' -import { suite, test } from 'node:test' -import { setImmediate } from 'node:timers/promises' -import { canPayWithVouchers } from './payments.js' +import assert from "node:assert/strict"; +import { suite, test } from "node:test"; +import { setImmediate } from "node:timers/promises"; +import { canPayWithVouchers } from "./payments.js"; const sampleRecords = [ { id: 1, - userId: 'user1', + userId: "user1", balance: 10, expiresAt: new Date(Date.now() + 1000), }, { id: 2, - userId: 'user1', + userId: "user1", balance: 5, expiresAt: new Date(Date.now() + 1000), }, { id: 3, - userId: 'user1', + userId: "user1", balance: 3, expiresAt: new Date(Date.now() + 1000), }, -] +]; -suite('canPayWithVouchers', { concurrency: true, timeout: 500 }, () => { - test('Returns true if balance is enough', async t => { +suite("canPayWithVouchers", { concurrency: true, timeout: 500 }, () => { + test("Returns true if balance is enough", async (t) => { const dbMock = { query: t.mock.fn(async (_sql, _params) => { - await setImmediate() - return sampleRecords + await setImmediate(); + return sampleRecords; }), - } + }; - const result = await canPayWithVouchers(dbMock, 'user1', 18) + const result = await canPayWithVouchers(dbMock, "user1", 18); - assert.equal(result, true) - assert.equal(dbMock.query.mock.callCount(), 1) - }) + assert.equal(result, true); + assert.equal(dbMock.query.mock.callCount(), 1); + }); - test('Returns false if balance is not enough', async t => { + test("Returns false if balance is not enough", async (t) => { const dbMock = { query: t.mock.fn(async (_sql, _params) => { - await setImmediate() - return sampleRecords + await setImmediate(); + return sampleRecords; }), - } + }; - const result = await canPayWithVouchers(dbMock, 'user1', 19) + const result = await canPayWithVouchers(dbMock, "user1", 19); - assert.equal(result, false) - assert.equal(dbMock.query.mock.callCount(), 1) - }) -}) + assert.equal(result, false); + assert.equal(dbMock.query.mock.callCount(), 1); + }); +}); diff --git a/10-testing/15-integration-test/dbClient.js b/10-testing/15-integration-test/dbClient.js index b81eafd..1d4a722 100644 --- a/10-testing/15-integration-test/dbClient.js +++ b/10-testing/15-integration-test/dbClient.js @@ -1,37 +1,37 @@ -import { open } from 'sqlite' -import sqlite3 from 'sqlite3' +import { open } from "sqlite"; +import sqlite3 from "sqlite3"; export class DbClient { - #dbPath - #db + #dbPath; + #db; constructor(dbPath) { - this.#dbPath = dbPath - this.#db = null + this.#dbPath = dbPath; + this.#db = null; } async #connect() { if (this.#db) { - return this.#db + return this.#db; } this.#db = await open({ filename: this.#dbPath, driver: sqlite3.Database, - }) + }); - return this.#db + return this.#db; } async query(sql, params = {}) { - const db = await this.#connect() - return db.all(sql, params) + const db = await this.#connect(); + return db.all(sql, params); } async close() { if (this.#db) { - await this.#db.close() - this.#db = null + await this.#db.close(); + this.#db = null; } } } diff --git a/10-testing/15-integration-test/dbSetup.js b/10-testing/15-integration-test/dbSetup.js index 49c806f..fd02d73 100644 --- a/10-testing/15-integration-test/dbSetup.js +++ b/10-testing/15-integration-test/dbSetup.js @@ -4,7 +4,7 @@ export async function createTables(db) { id TEXT PRIMARY KEY, name TEXT NOT NULL ) - `) + `); await db.query(` CREATE TABLE IF NOT EXISTS vouchers ( @@ -13,5 +13,5 @@ export async function createTables(db) { balance REAL NOT NULL, expiresAt TIMESTAMP NOT NULL ) - `) + `); } diff --git a/10-testing/15-integration-test/payments.int.test.js b/10-testing/15-integration-test/payments.int.test.js index 5513763..c0ef2bb 100644 --- a/10-testing/15-integration-test/payments.int.test.js +++ b/10-testing/15-integration-test/payments.int.test.js @@ -1,15 +1,15 @@ -import assert from 'node:assert/strict' -import { suite, test } from 'node:test' -import { DbClient } from './dbClient.js' -import { createTables } from './dbSetup.js' -import { canPayWithVouchers, getActiveVouchers } from './payments.js' +import assert from "node:assert/strict"; +import { suite, test } from "node:test"; +import { DbClient } from "./dbClient.js"; +import { createTables } from "./dbSetup.js"; +import { canPayWithVouchers, getActiveVouchers } from "./payments.js"; function addTestUser(db, id, name) { return db.query( `INSERT INTO users (id, name) VALUES (?, ?)`, - [id, name] - ) + [id, name], + ); } async function addTestVoucher(db, id, userId, balance, expiresAt) { @@ -18,70 +18,70 @@ async function addTestVoucher(db, id, userId, balance, expiresAt) { userId, balance, expiresAt: expiresAt ?? new Date(Date.now() + 1000).toISOString(), - } + }; await db.query( `INSERT INTO vouchers (id, userId, balance, expiresAt) VALUES (?, ?, ?, ?)`, - [record.id, record.userId, record.balance, record.expiresAt] - ) - return record + [record.id, record.userId, record.balance, record.expiresAt], + ); + return record; } -suite('activeVouchers', { concurrency: true, timeout: 500 }, () => { - test('queries for active vouchers', async () => { - const expected = [] - const db = new DbClient(':memory:') - await createTables(db) - await addTestUser(db, 'user1', 'Test User 1') - await addTestUser(db, 'user2', 'Test User 2') - expected.push(await addTestVoucher(db, 'voucher1', 'user1', 10)) - expected.push(await addTestVoucher(db, 'voucher2', 'user1', 5)) - expected.push(await addTestVoucher(db, 'voucher3', 'user1', 3)) +suite("activeVouchers", { concurrency: true, timeout: 500 }, () => { + test("queries for active vouchers", async () => { + const expected = []; + const db = new DbClient(":memory:"); + await createTables(db); + await addTestUser(db, "user1", "Test User 1"); + await addTestUser(db, "user2", "Test User 2"); + expected.push(await addTestVoucher(db, "voucher1", "user1", 10)); + expected.push(await addTestVoucher(db, "voucher2", "user1", 5)); + expected.push(await addTestVoucher(db, "voucher3", "user1", 3)); // expired await addTestVoucher( db, - 'voucher4', - 'user1', + "voucher4", + "user1", 10, - new Date(Date.now() - 1000).toISOString() - ) + new Date(Date.now() - 1000).toISOString(), + ); // different user - await addTestVoucher(db, 'voucher5', 'user2', 10) + await addTestVoucher(db, "voucher5", "user2", 10); // zero balance - await addTestVoucher(db, 'voucher6', 'user1', 0) + await addTestVoucher(db, "voucher6", "user1", 0); - const activeVouchers = await getActiveVouchers(db, 'user1') - db.close() - assert.deepEqual(activeVouchers, expected) - }) -}) + const activeVouchers = await getActiveVouchers(db, "user1"); + db.close(); + assert.deepEqual(activeVouchers, expected); + }); +}); -suite('canPayWithVouchers', { concurrency: true, timeout: 500 }, () => { - test('Returns true if balance is enough', async () => { - const db = new DbClient(':memory:') - await createTables(db) - await addTestUser(db, 'user1', 'Test User 1') - await addTestVoucher(db, 'voucher1', 'user1', 10) - await addTestVoucher(db, 'voucher2', 'user1', 5) - await addTestVoucher(db, 'voucher3', 'user1', 3) +suite("canPayWithVouchers", { concurrency: true, timeout: 500 }, () => { + test("Returns true if balance is enough", async () => { + const db = new DbClient(":memory:"); + await createTables(db); + await addTestUser(db, "user1", "Test User 1"); + await addTestVoucher(db, "voucher1", "user1", 10); + await addTestVoucher(db, "voucher2", "user1", 5); + await addTestVoucher(db, "voucher3", "user1", 3); - const result = await canPayWithVouchers(db, 'user1', 18) + const result = await canPayWithVouchers(db, "user1", 18); - db.close() - assert.equal(result, true) - }) + db.close(); + assert.equal(result, true); + }); - test('Returns false if balance is not enough', async () => { - const db = new DbClient(':memory:') - await createTables(db) - await addTestUser(db, 'user1', 'Test User 1') - await addTestVoucher(db, 'voucher1', 'user1', 10) - await addTestVoucher(db, 'voucher2', 'user1', 5) - await addTestVoucher(db, 'voucher3', 'user1', 3) + test("Returns false if balance is not enough", async () => { + const db = new DbClient(":memory:"); + await createTables(db); + await addTestUser(db, "user1", "Test User 1"); + await addTestVoucher(db, "voucher1", "user1", 10); + await addTestVoucher(db, "voucher2", "user1", 5); + await addTestVoucher(db, "voucher3", "user1", 3); - const result = await canPayWithVouchers(db, 'user1', 19) + const result = await canPayWithVouchers(db, "user1", 19); - db.close() - assert.equal(result, false) - }) -}) + db.close(); + assert.equal(result, false); + }); +}); diff --git a/10-testing/15-integration-test/payments.js b/10-testing/15-integration-test/payments.js index acf9422..2a9512a 100644 --- a/10-testing/15-integration-test/payments.js +++ b/10-testing/15-integration-test/payments.js @@ -4,15 +4,15 @@ export async function getActiveVouchers(db, userId) { WHERE userId = ? AND balance > 0 AND expiresAt > strftime('%FT%T:%fZ', 'now')`, - [userId] - ) + [userId], + ); - return vouchers + return vouchers; } export async function canPayWithVouchers(db, userId, amount) { - const vouchers = await getActiveVouchers(db, userId) - const availableBalance = vouchers.reduce((acc, v) => acc + v.balance, 0) + const vouchers = await getActiveVouchers(db, userId); + const availableBalance = vouchers.reduce((acc, v) => acc + v.balance, 0); - return availableBalance >= amount + return availableBalance >= amount; } diff --git a/10-testing/16-integration-test-http/app.js b/10-testing/16-integration-test-http/app.js index 00645fa..8c92102 100644 --- a/10-testing/16-integration-test-http/app.js +++ b/10-testing/16-integration-test-http/app.js @@ -1,13 +1,13 @@ -import Fastify from 'fastify' -import { bookEventRoute } from './routes/bookEvent.js' -import { createEventRoute } from './routes/createEvent.js' +import Fastify from "fastify"; +import { bookEventRoute } from "./routes/bookEvent.js"; +import { createEventRoute } from "./routes/createEvent.js"; export async function createApp(db) { - const app = Fastify() - app.decorate('db', db) + const app = Fastify(); + app.decorate("db", db); - await app.register(bookEventRoute) - await app.register(createEventRoute) + await app.register(bookEventRoute); + await app.register(createEventRoute); - return app + return app; } diff --git a/10-testing/16-integration-test-http/booking.int.test.js b/10-testing/16-integration-test-http/booking.int.test.js index 29bb1ac..a179de7 100644 --- a/10-testing/16-integration-test-http/booking.int.test.js +++ b/10-testing/16-integration-test-http/booking.int.test.js @@ -1,65 +1,65 @@ -import assert from 'node:assert/strict' -import { suite, test } from 'node:test' -import { createApp } from './app.js' -import { DbClient } from './dbClient.js' -import { createTables } from './dbSetup.js' +import assert from "node:assert/strict"; +import { suite, test } from "node:test"; +import { createApp } from "./app.js"; +import { DbClient } from "./dbClient.js"; +import { createTables } from "./dbSetup.js"; -suite('Booking integration tests', { concurrency: true }, () => { - test('Reserving a seat works until full', async () => { - const db = new DbClient(':memory:') - await createTables(db) - const app = await createApp(db) +suite("Booking integration tests", { concurrency: true }, () => { + test("Reserving a seat works until full", async () => { + const db = new DbClient(":memory:"); + await createTables(db); + const app = await createApp(db); const createEventResponse = await app.inject({ - method: 'POST', - url: '/events', - payload: { name: 'Event 1', totalSeats: 2 }, - }) - assert.equal(createEventResponse.statusCode, 201) - const eventData = createEventResponse.json() - const reserveUrl = `/events/${eventData.eventId}/reservations` + method: "POST", + url: "/events", + payload: { name: "Event 1", totalSeats: 2 }, + }); + assert.equal(createEventResponse.statusCode, 201); + const eventData = createEventResponse.json(); + const reserveUrl = `/events/${eventData.eventId}/reservations`; const res1 = await app.inject({ - method: 'POST', + method: "POST", url: reserveUrl, - payload: { userId: 'u1' }, - }) - assert.equal(res1.statusCode, 201) + payload: { userId: "u1" }, + }); + assert.equal(res1.statusCode, 201); const res2 = await app.inject({ - method: 'POST', + method: "POST", url: reserveUrl, - payload: { userId: 'u2' }, - }) - assert.equal(res2.statusCode, 201) + payload: { userId: "u2" }, + }); + assert.equal(res2.statusCode, 201); const res3 = await app.inject({ - method: 'POST', + method: "POST", url: reserveUrl, - payload: { userId: 'u3' }, - }) - assert.equal(res3.statusCode, 403) - assert.deepEqual(await res3.json(), { error: 'Event is fully booked' }) + payload: { userId: "u3" }, + }); + assert.equal(res3.statusCode, 403); + assert.deepEqual(await res3.json(), { error: "Event is fully booked" }); - await db.close() - await app.close() - }) + await db.close(); + await app.close(); + }); - test('Returns 404 if event does not exist', async () => { - const db = new DbClient(':memory:') - await createTables(db) - const app = await createApp(db) + test("Returns 404 if event does not exist", async () => { + const db = new DbClient(":memory:"); + await createTables(db); + const app = await createApp(db); const res = await app.inject({ - method: 'POST', - url: '/events/unknown/reservations', - payload: { userId: 'u1' }, - }) + method: "POST", + url: "/events/unknown/reservations", + payload: { userId: "u1" }, + }); - assert.equal(res.statusCode, 404) - assert.deepEqual(await res.json(), { error: 'Event not found' }) + assert.equal(res.statusCode, 404); + assert.deepEqual(await res.json(), { error: "Event not found" }); - await db.close() - await app.close() - }) -}) + await db.close(); + await app.close(); + }); +}); diff --git a/10-testing/16-integration-test-http/booking.js b/10-testing/16-integration-test-http/booking.js index 01501b1..e0b40e9 100644 --- a/10-testing/16-integration-test-http/booking.js +++ b/10-testing/16-integration-test-http/booking.js @@ -1,36 +1,38 @@ -import { randomUUID } from 'node:crypto' +import { randomUUID } from "node:crypto"; export async function reserveSeat(db, eventId, userId) { - const [event] = await db.query('SELECT * FROM events WHERE id = ?', [eventId]) + const [event] = await db.query("SELECT * FROM events WHERE id = ?", [ + eventId, + ]); if (!event) { - throw new Error('Event not found') + throw new Error("Event not found"); } const existing = await db.query( - 'SELECT COUNT(*) AS count FROM reservations WHERE eventId = ?', - [eventId] - ) + "SELECT COUNT(*) AS count FROM reservations WHERE eventId = ?", + [eventId], + ); if (existing[0].count >= event.totalSeats) { - throw new Error('Event is fully booked') + throw new Error("Event is fully booked"); } - const reservationId = randomUUID() + const reservationId = randomUUID(); await db.query( - 'INSERT INTO reservations (id, eventId, userId) VALUES (?, ?, ?)', - [reservationId, eventId, userId] - ) + "INSERT INTO reservations (id, eventId, userId) VALUES (?, ?, ?)", + [reservationId, eventId, userId], + ); - return reservationId + return reservationId; } export async function createEvent(db, name, totalSeats) { - const eventId = randomUUID() - await db.query('INSERT INTO events (id, name, totalSeats) VALUES (?, ?, ?)', [ + const eventId = randomUUID(); + await db.query("INSERT INTO events (id, name, totalSeats) VALUES (?, ?, ?)", [ eventId, name, totalSeats, - ]) - return eventId + ]); + return eventId; } diff --git a/10-testing/16-integration-test-http/dbClient.js b/10-testing/16-integration-test-http/dbClient.js index b81eafd..1d4a722 100644 --- a/10-testing/16-integration-test-http/dbClient.js +++ b/10-testing/16-integration-test-http/dbClient.js @@ -1,37 +1,37 @@ -import { open } from 'sqlite' -import sqlite3 from 'sqlite3' +import { open } from "sqlite"; +import sqlite3 from "sqlite3"; export class DbClient { - #dbPath - #db + #dbPath; + #db; constructor(dbPath) { - this.#dbPath = dbPath - this.#db = null + this.#dbPath = dbPath; + this.#db = null; } async #connect() { if (this.#db) { - return this.#db + return this.#db; } this.#db = await open({ filename: this.#dbPath, driver: sqlite3.Database, - }) + }); - return this.#db + return this.#db; } async query(sql, params = {}) { - const db = await this.#connect() - return db.all(sql, params) + const db = await this.#connect(); + return db.all(sql, params); } async close() { if (this.#db) { - await this.#db.close() - this.#db = null + await this.#db.close(); + this.#db = null; } } } diff --git a/10-testing/16-integration-test-http/dbSetup.js b/10-testing/16-integration-test-http/dbSetup.js index 1c546c6..e1f8227 100644 --- a/10-testing/16-integration-test-http/dbSetup.js +++ b/10-testing/16-integration-test-http/dbSetup.js @@ -5,7 +5,7 @@ export async function createTables(db) { name TEXT NOT NULL, totalSeats INTEGER NOT NULL ) - `) + `); await db.query(` CREATE TABLE IF NOT EXISTS reservations ( @@ -14,5 +14,5 @@ export async function createTables(db) { userId TEXT NOT NULL, UNIQUE(eventId, userId) ) - `) + `); } diff --git a/10-testing/16-integration-test-http/routes/bookEvent.js b/10-testing/16-integration-test-http/routes/bookEvent.js index 787d601..d6103ec 100644 --- a/10-testing/16-integration-test-http/routes/bookEvent.js +++ b/10-testing/16-integration-test-http/routes/bookEvent.js @@ -1,42 +1,42 @@ -import { reserveSeat } from '../booking.js' +import { reserveSeat } from "../booking.js"; export function bookEventRoute(fastify) { - fastify.post('/events/:eventId/reservations', { + fastify.post("/events/:eventId/reservations", { schema: { params: { - type: 'object', - required: ['eventId'], + type: "object", + required: ["eventId"], properties: { - eventId: { type: 'string' }, + eventId: { type: "string" }, }, }, body: { - type: 'object', - required: ['userId'], + type: "object", + required: ["userId"], properties: { - userId: { type: 'string' }, + userId: { type: "string" }, }, }, }, async handler(request, reply) { - const { eventId } = request.params - const { userId } = request.body + const { eventId } = request.params; + const { userId } = request.body; try { - const reservationId = await reserveSeat(fastify.db, eventId, userId) - return reply.status(201).send({ success: true, reservationId }) + const reservationId = await reserveSeat(fastify.db, eventId, userId); + return reply.status(201).send({ success: true, reservationId }); } catch (err) { - if (err.message === 'Event not found') { - return reply.status(404).send({ error: 'Event not found' }) + if (err.message === "Event not found") { + return reply.status(404).send({ error: "Event not found" }); } - if (err.message === 'Event is fully booked') { - return reply.status(403).send({ error: 'Event is fully booked' }) + if (err.message === "Event is fully booked") { + return reply.status(403).send({ error: "Event is fully booked" }); } - fastify.log.error(err) - return reply.status(500).send({ error: 'Server error' }) + fastify.log.error(err); + return reply.status(500).send({ error: "Server error" }); } }, - }) + }); } diff --git a/10-testing/16-integration-test-http/routes/createEvent.js b/10-testing/16-integration-test-http/routes/createEvent.js index ef22163..f4bd2b8 100644 --- a/10-testing/16-integration-test-http/routes/createEvent.js +++ b/10-testing/16-integration-test-http/routes/createEvent.js @@ -1,21 +1,21 @@ -import { createEvent } from '../booking.js' +import { createEvent } from "../booking.js"; export function createEventRoute(fastify) { - fastify.post('/events', { + fastify.post("/events", { schema: { body: { - type: 'object', - required: ['name', 'totalSeats'], + type: "object", + required: ["name", "totalSeats"], properties: { - name: { type: 'string' }, - totalSeats: { type: 'integer' }, + name: { type: "string" }, + totalSeats: { type: "integer" }, }, }, }, async handler(request, reply) { - const { name, totalSeats } = request.body - const eventId = await createEvent(fastify.db, name, totalSeats) - return reply.status(201).send({ success: true, eventId }) + const { name, totalSeats } = request.body; + const eventId = await createEvent(fastify.db, name, totalSeats); + return reply.status(201).send({ success: true, eventId }); }, - }) + }); } diff --git a/10-testing/16-integration-test-http/server.js b/10-testing/16-integration-test-http/server.js index 95e10f7..47f0468 100644 --- a/10-testing/16-integration-test-http/server.js +++ b/10-testing/16-integration-test-http/server.js @@ -1,9 +1,9 @@ -import { createApp } from './app.js' -import { DbClient } from './dbClient.js' -import { createTables } from './dbSetup.js' +import { createApp } from "./app.js"; +import { DbClient } from "./dbClient.js"; +import { createTables } from "./dbSetup.js"; -const db = new DbClient('data/db.sqlite') -await createTables(db) +const db = new DbClient("data/db.sqlite"); +await createTables(db); -const app = await createApp(db) -app.listen({ port: 3000 }) +const app = await createApp(db); +app.listen({ port: 3000 }); diff --git a/10-testing/17-e2e-test/e2e/example.spec.ts b/10-testing/17-e2e-test/e2e/example.spec.ts index 838adc2..0a75246 100644 --- a/10-testing/17-e2e-test/e2e/example.spec.ts +++ b/10-testing/17-e2e-test/e2e/example.spec.ts @@ -1,21 +1,21 @@ -import { expect, test } from '@playwright/test' +import { expect, test } from "@playwright/test"; -test('has title', async ({ page }) => { - await page.goto('https://playwright.dev/') +test("has title", async ({ page }) => { + await page.goto("https://playwright.dev/"); // Expect a title "to contain" a substring. // biome-ignore lint/performance/useTopLevelRegex: example from docs - await expect(page).toHaveTitle(/Playwright/) -}) + await expect(page).toHaveTitle(/Playwright/); +}); -test('get started link', async ({ page }) => { - await page.goto('https://playwright.dev/') +test("get started link", async ({ page }) => { + await page.goto("https://playwright.dev/"); // Click the get started link. - await page.getByRole('link', { name: 'Get started' }).click() + await page.getByRole("link", { name: "Get started" }).click(); // Expects page to have a heading with the name of Installation. await expect( - page.getByRole('heading', { name: 'Installation' }) - ).toBeVisible() -}) + page.getByRole("heading", { name: "Installation" }), + ).toBeVisible(); +}); diff --git a/10-testing/17-e2e-test/e2e/userFlow.spec.ts b/10-testing/17-e2e-test/e2e/userFlow.spec.ts index c16f2a1..b4097a4 100644 --- a/10-testing/17-e2e-test/e2e/userFlow.spec.ts +++ b/10-testing/17-e2e-test/e2e/userFlow.spec.ts @@ -1,7 +1,8 @@ -import { expect, test } from '@playwright/test' +import { expect, test } from "@playwright/test"; + // import { GenericContainer } from 'testcontainers' -test('A user can sign up and book an event', async ({ page }) => { +test("A user can sign up and book an event", async ({ page }) => { // starts the app in a container // const appContainer = await new GenericContainer( // 'ghcr.io/lmammino/sample-events-website:main' @@ -9,55 +10,57 @@ test('A user can sign up and book an event', async ({ page }) => { // .withExposedPorts(3000) // .start() - await page.goto('http://localhost:3000') + await page.goto("http://localhost:3000"); - await page.getByRole('link', { name: 'Sign In' }).click() + await page.getByRole("link", { name: "Sign In" }).click(); - await page.getByRole('link', { name: 'Sign up' }).click() + await page.getByRole("link", { name: "Sign up" }).click(); - const seed = Date.now().toString() - const name = `TestUser ${seed}` - const email = `test${seed}@example.com` - const password = `someRandomPassword${seed}` + const seed = Date.now().toString(); + const name = `TestUser ${seed}`; + const email = `test${seed}@example.com`; + const password = `someRandomPassword${seed}`; - await page.getByRole('textbox', { name: 'name' }).fill(name) - await page.getByRole('textbox', { name: 'email' }).fill(email) - await page.getByRole('textbox', { name: 'password' }).fill(password) + await page.getByRole("textbox", { name: "name" }).fill(name); + await page.getByRole("textbox", { name: "email" }).fill(email); + await page.getByRole("textbox", { name: "password" }).fill(password); - await page.getByRole('button', { name: 'Create account' }).click() + await page.getByRole("button", { name: "Create account" }).click(); - await page.getByRole('link', { name: 'Marathon City Run' }).click() + await page.getByRole("link", { name: "Marathon City Run" }).click(); // get the value of the current available capacity const availableCapacity = Number.parseInt( - (await page.getByTestId('available-capacity').textContent()) as string - ) + (await page.getByTestId("available-capacity").textContent()) as string, + 10, + ); - await page.getByRole('button', { name: 'Reserve your spot' }).click() + await page.getByRole("button", { name: "Reserve your spot" }).click(); // check that the reservation was successful by looking that we have // a "Booked" note on the page and that // the "reserve" button says "You have booked this event!" and is now disabled - await expect(page.getByTestId('badge').first()).toHaveText('Booked') - const bookButton = await page.getByRole('button', { - name: 'You have booked this event!', - }) - await expect(bookButton).toBeDisabled() - await expect(bookButton).toBeVisible() + await expect(page.getByTestId("badge").first()).toHaveText("Booked"); + const bookButton = await page.getByRole("button", { + name: "You have booked this event!", + }); + await expect(bookButton).toBeDisabled(); + await expect(bookButton).toBeVisible(); // check that the count of spots available went down const newAvailableCapacity = Number.parseInt( - (await page.getByTestId('available-capacity').textContent()) as string - ) - expect(newAvailableCapacity).toBeLessThan(availableCapacity) + (await page.getByTestId("available-capacity").textContent()) as string, + 10, + ); + expect(newAvailableCapacity).toBeLessThan(availableCapacity); - await page.getByRole('link', { name: 'My Reservations' }).click() + await page.getByRole("link", { name: "My Reservations" }).click(); await expect( - page.getByRole('heading', { name: 'My Reservations' }) - ).toBeVisible() + page.getByRole("heading", { name: "My Reservations" }), + ).toBeVisible(); expect( - await page.getByRole('heading', { name: 'Marathon City Run' }) - ).toBeVisible() -}) + await page.getByRole("heading", { name: "Marathon City Run" }), + ).toBeVisible(); +}); diff --git a/10-testing/17-e2e-test/playwright.config.ts b/10-testing/17-e2e-test/playwright.config.ts index 65f44be..87f2d42 100644 --- a/10-testing/17-e2e-test/playwright.config.ts +++ b/10-testing/17-e2e-test/playwright.config.ts @@ -1,4 +1,4 @@ -import { defineConfig, devices } from '@playwright/test' +import { defineConfig, devices } from "@playwright/test"; /** * Read environment variables from file. @@ -14,7 +14,7 @@ import { defineConfig, devices } from '@playwright/test' // biome-ignore lint/style/noDefaultExport: export default defineConfig({ - testDir: './e2e', + testDir: "./e2e", /* Run tests in files in parallel */ fullyParallel: true, /* Fail the build on CI if you accidentally left test.only in the source code. */ @@ -24,21 +24,21 @@ export default defineConfig({ /* Opt out of parallel tests on CI. */ workers: process.env.CI ? 1 : undefined, /* Reporter to use. See https://playwright.dev/docs/test-reporters */ - reporter: 'html', + reporter: "html", /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ use: { /* Base URL to use in actions like `await page.goto('/')`. */ // baseURL: 'http://127.0.0.1:3000', /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ - trace: 'on-first-retry', + trace: "on-first-retry", }, /* Configure projects for major browsers */ projects: [ { - name: 'chromium', - use: { ...devices['Desktop Chrome'] }, + name: "chromium", + use: { ...devices["Desktop Chrome"] }, }, // { @@ -78,4 +78,4 @@ export default defineConfig({ // url: 'http://127.0.0.1:3000', // reuseExistingServer: !process.env.CI, // }, -}) +}); diff --git a/11-advanced-recipes/01-local-initialization-check/db.js b/11-advanced-recipes/01-local-initialization-check/db.js index cc23769..74909b2 100644 --- a/11-advanced-recipes/01-local-initialization-check/db.js +++ b/11-advanced-recipes/01-local-initialization-check/db.js @@ -1,30 +1,30 @@ -import { setTimeout } from 'node:timers/promises' +import { setTimeout } from "node:timers/promises"; class Database { - connected = false - #pendingConnection = null + connected = false; + #pendingConnection = null; async connect() { if (!this.connected) { if (this.#pendingConnection) { - return this.#pendingConnection + return this.#pendingConnection; } // simulate the delay of the connection - this.#pendingConnection = setTimeout(500) - await this.#pendingConnection - this.connected = true - this.#pendingConnection = null + this.#pendingConnection = setTimeout(500); + await this.#pendingConnection; + this.connected = true; + this.#pendingConnection = null; } } async query(queryString) { if (!this.connected) { - throw new Error('Not connected yet') + throw new Error("Not connected yet"); } // simulate the delay of the query execution - await setTimeout(100) - console.log(`Query executed: ${queryString}`) + await setTimeout(100); + console.log(`Query executed: ${queryString}`); } } -export const db = new Database() +export const db = new Database(); diff --git a/11-advanced-recipes/01-local-initialization-check/delayedStartup.js b/11-advanced-recipes/01-local-initialization-check/delayedStartup.js index 6e62376..4a11e4c 100644 --- a/11-advanced-recipes/01-local-initialization-check/delayedStartup.js +++ b/11-advanced-recipes/01-local-initialization-check/delayedStartup.js @@ -1,13 +1,13 @@ -import { db } from './db.js' +import { db } from "./db.js"; async function getConnectedDb() { - await db.connect() - return db + await db.connect(); + return db; } async function getUsers(db) { - await db.query('SELECT * FROM users') + await db.query("SELECT * FROM users"); } -const connectedDb = await getConnectedDb() -await getUsers(connectedDb) +const connectedDb = await getConnectedDb(); +await getUsers(connectedDb); diff --git a/11-advanced-recipes/01-local-initialization-check/localInitializationCheck.js b/11-advanced-recipes/01-local-initialization-check/localInitializationCheck.js index 19faa24..09bdead 100644 --- a/11-advanced-recipes/01-local-initialization-check/localInitializationCheck.js +++ b/11-advanced-recipes/01-local-initialization-check/localInitializationCheck.js @@ -1,11 +1,11 @@ -import { db } from './db.js' +import { db } from "./db.js"; async function getUsers() { if (!db.connected) { - await db.connect() + await db.connect(); } - await db.query('SELECT * FROM users') + await db.query("SELECT * FROM users"); } -await getUsers() +await getUsers(); diff --git a/11-advanced-recipes/01-local-initialization-check/wrong-init-no-await.js b/11-advanced-recipes/01-local-initialization-check/wrong-init-no-await.js index 7c989d7..5ae3f0a 100644 --- a/11-advanced-recipes/01-local-initialization-check/wrong-init-no-await.js +++ b/11-advanced-recipes/01-local-initialization-check/wrong-init-no-await.js @@ -1,5 +1,5 @@ -import { db } from './db.js' +import { db } from "./db.js"; -db.connect() -const users = await db.query('SELECT * FROM users') -console.log(users) +db.connect(); +const users = await db.query("SELECT * FROM users"); +console.log(users); diff --git a/11-advanced-recipes/01-local-initialization-check/wrong-init-no-connect.js b/11-advanced-recipes/01-local-initialization-check/wrong-init-no-connect.js index dafb36a..0ae9b17 100644 --- a/11-advanced-recipes/01-local-initialization-check/wrong-init-no-connect.js +++ b/11-advanced-recipes/01-local-initialization-check/wrong-init-no-connect.js @@ -1,4 +1,4 @@ -import { db } from './db.js' +import { db } from "./db.js"; -const users = await db.query('SELECT * FROM users') -console.log(users) +const users = await db.query("SELECT * FROM users"); +console.log(users); diff --git a/11-advanced-recipes/02-async-init-queue/db.js b/11-advanced-recipes/02-async-init-queue/db.js index 6f99688..a78794b 100644 --- a/11-advanced-recipes/02-async-init-queue/db.js +++ b/11-advanced-recipes/02-async-init-queue/db.js @@ -1,44 +1,44 @@ -import { setTimeout } from 'node:timers/promises' +import { setTimeout } from "node:timers/promises"; class Database { - connected = false - #pendingConnection = null - commandsQueue = [] + connected = false; + #pendingConnection = null; + commandsQueue = []; async connect() { if (!this.connected) { if (this.#pendingConnection) { - return this.#pendingConnection + return this.#pendingConnection; } // simulate the delay of the connection - this.#pendingConnection = setTimeout(500) - await this.#pendingConnection - this.connected = true - this.#pendingConnection = null + this.#pendingConnection = setTimeout(500); + await this.#pendingConnection; + this.connected = true; + this.#pendingConnection = null; // once connected executes all the queued commands while (this.commandsQueue.length > 0) { - const command = this.commandsQueue.shift() - command() + const command = this.commandsQueue.shift(); + command(); } } } async query(queryString) { if (!this.connected) { - console.log(`Request queued: ${queryString}`) + console.log(`Request queued: ${queryString}`); return new Promise((resolve, reject) => { const command = () => { - this.query(queryString).then(resolve, reject) - } - this.commandsQueue.push(command) - }) + this.query(queryString).then(resolve, reject); + }; + this.commandsQueue.push(command); + }); } // simulate the delay of the query execution - await setTimeout(100) - console.log(`Query executed: ${queryString}`) + await setTimeout(100); + console.log(`Query executed: ${queryString}`); } } -export const db = new Database() +export const db = new Database(); diff --git a/11-advanced-recipes/02-async-init-queue/index.js b/11-advanced-recipes/02-async-init-queue/index.js index 1b4314b..4806a22 100644 --- a/11-advanced-recipes/02-async-init-queue/index.js +++ b/11-advanced-recipes/02-async-init-queue/index.js @@ -1,12 +1,12 @@ -import { setTimeout } from 'node:timers/promises' -import { db } from './db.js' +import { setTimeout } from "node:timers/promises"; +import { db } from "./db.js"; -db.connect() +db.connect(); async function updateLastAccess() { - await db.query(`INSERT (${Date.now()}) INTO "LastAccesses"`) + await db.query(`INSERT (${Date.now()}) INTO "LastAccesses"`); } -updateLastAccess() -await setTimeout(600) -updateLastAccess() +updateLastAccess(); +await setTimeout(600); +updateLastAccess(); diff --git a/11-advanced-recipes/03-async-init-queues-state/db.js b/11-advanced-recipes/03-async-init-queues-state/db.js index 4c6013e..7586738 100644 --- a/11-advanced-recipes/03-async-init-queues-state/db.js +++ b/11-advanced-recipes/03-async-init-queues-state/db.js @@ -1,72 +1,72 @@ -import { setTimeout } from 'node:timers/promises' +import { setTimeout } from "node:timers/promises"; -const deactivate = Symbol('deactivate') +const deactivate = Symbol("deactivate"); class InitializedState { constructor(db) { - this.db = db + this.db = db; } async query(queryString) { // simulate the delay of the query execution - await setTimeout(100) - console.log(`Query executed: ${queryString}`) + await setTimeout(100); + console.log(`Query executed: ${queryString}`); } } class QueuingState { constructor(db) { - this.db = db - this.commandsQueue = [] + this.db = db; + this.commandsQueue = []; } async query(queryString) { - console.log(`Request queued: ${queryString}`) + console.log(`Request queued: ${queryString}`); return new Promise((resolve, reject) => { const command = () => { - db.query(queryString).then(resolve, reject) - } - this.commandsQueue.push(command) - }) + db.query(queryString).then(resolve, reject); + }; + this.commandsQueue.push(command); + }); } [deactivate]() { while (this.commandsQueue.length > 0) { - const command = this.commandsQueue.shift() - command() + const command = this.commandsQueue.shift(); + command(); } } } class Database { - connected = false - #pendingConnection = null + connected = false; + #pendingConnection = null; constructor() { - this.state = new QueuingState(this) + this.state = new QueuingState(this); } // biome-ignore lint/suspicious/useAwait: async query(queryString) { - return this.state.query(queryString) + return this.state.query(queryString); } async connect() { if (!this.connected) { if (this.#pendingConnection) { - return this.#pendingConnection + return this.#pendingConnection; } // simulate the delay of the connection - this.#pendingConnection = setTimeout(500) - await this.#pendingConnection - this.connected = true - this.#pendingConnection = null + this.#pendingConnection = setTimeout(500); + await this.#pendingConnection; + this.connected = true; + this.#pendingConnection = null; // once connected update the state - const oldState = this.state - this.state = new InitializedState(this) - oldState[deactivate]?.() + const oldState = this.state; + this.state = new InitializedState(this); + oldState[deactivate]?.(); } } } -export const db = new Database() +export const db = new Database(); diff --git a/11-advanced-recipes/03-async-init-queues-state/index.js b/11-advanced-recipes/03-async-init-queues-state/index.js index 1b4314b..4806a22 100644 --- a/11-advanced-recipes/03-async-init-queues-state/index.js +++ b/11-advanced-recipes/03-async-init-queues-state/index.js @@ -1,12 +1,12 @@ -import { setTimeout } from 'node:timers/promises' -import { db } from './db.js' +import { setTimeout } from "node:timers/promises"; +import { db } from "./db.js"; -db.connect() +db.connect(); async function updateLastAccess() { - await db.query(`INSERT (${Date.now()}) INTO "LastAccesses"`) + await db.query(`INSERT (${Date.now()}) INTO "LastAccesses"`); } -updateLastAccess() -await setTimeout(600) -updateLastAccess() +updateLastAccess(); +await setTimeout(600); +updateLastAccess(); diff --git a/11-advanced-recipes/04-batching-and-caching/populateDb.js b/11-advanced-recipes/04-batching-and-caching/populateDb.js index 2f38984..ade44be 100644 --- a/11-advanced-recipes/04-batching-and-caching/populateDb.js +++ b/11-advanced-recipes/04-batching-and-caching/populateDb.js @@ -1,18 +1,18 @@ -import { Level } from 'level' -import { nanoid } from 'nanoid' +import { Level } from "level"; +import { nanoid } from "nanoid"; -const db = new Level('sales', { valueEncoding: 'json' }) -const products = ['book', 'game', 'app', 'song', 'movie'] +const db = new Level("sales", { valueEncoding: "json" }); +const products = ["book", "game", "app", "song", "movie"]; async function populate() { for (let i = 0; i < 100000; i++) { await db.put(nanoid(), { amount: Math.ceil(Math.random() * 100), product: products[Math.floor(Math.random() * 5)], - }) + }); } - console.log('DB populated') + console.log("DB populated"); } -populate() +populate(); diff --git a/11-advanced-recipes/04-batching-and-caching/server.js b/11-advanced-recipes/04-batching-and-caching/server.js index b2af951..16340ea 100644 --- a/11-advanced-recipes/04-batching-and-caching/server.js +++ b/11-advanced-recipes/04-batching-and-caching/server.js @@ -1,21 +1,21 @@ -import { createServer } from 'node:http' +import { createServer } from "node:http"; // import { totalSales } from './totalSales.js' // import { totalSales } from './totalSalesBatch.js' -import { totalSales } from './totalSalesCache.js' +import { totalSales } from "./totalSalesCache.js"; createServer(async (req, res) => { - const url = new URL(req.url, 'http://localhost') - const product = url.searchParams.get('product') - console.log(`Processing query: ${url.search}`) + const url = new URL(req.url, "http://localhost"); + const product = url.searchParams.get("product"); + console.log(`Processing query: ${url.search}`); - const sum = await totalSales(product) + const sum = await totalSales(product); - res.setHeader('Content-Type', 'application/json') - res.writeHead(200) + res.setHeader("Content-Type", "application/json"); + res.writeHead(200); res.end( JSON.stringify({ product, sum, - }) - ) -}).listen(8000, () => console.log('Server started')) + }), + ); +}).listen(8000, () => console.log("Server started")); diff --git a/11-advanced-recipes/04-batching-and-caching/totalSales.js b/11-advanced-recipes/04-batching-and-caching/totalSales.js index a1a4f6c..f702f24 100644 --- a/11-advanced-recipes/04-batching-and-caching/totalSales.js +++ b/11-advanced-recipes/04-batching-and-caching/totalSales.js @@ -1,17 +1,17 @@ -import { Level } from 'level' +import { Level } from "level"; -const db = new Level('sales', { valueEncoding: 'json' }) +const db = new Level("sales", { valueEncoding: "json" }); export async function totalSales(product) { - const now = Date.now() - let sum = 0 + const now = Date.now(); + let sum = 0; for await (const [_transactionId, transaction] of db.iterator()) { if (!product || transaction.product === product) { - sum += transaction.amount + sum += transaction.amount; } } - console.log(`totalSales() took: ${Date.now() - now}ms`) + console.log(`totalSales() took: ${Date.now() - now}ms`); - return sum + return sum; } diff --git a/11-advanced-recipes/04-batching-and-caching/totalSalesBatch.js b/11-advanced-recipes/04-batching-and-caching/totalSalesBatch.js index d049e75..04911bf 100644 --- a/11-advanced-recipes/04-batching-and-caching/totalSalesBatch.js +++ b/11-advanced-recipes/04-batching-and-caching/totalSalesBatch.js @@ -1,18 +1,18 @@ -import { totalSales as totalSalesRaw } from './totalSales.js' +import { totalSales as totalSalesRaw } from "./totalSales.js"; -const runningRequests = new Map() +const runningRequests = new Map(); export function totalSales(product) { if (runningRequests.has(product)) { - console.log('Batching') - return runningRequests.get(product) + console.log("Batching"); + return runningRequests.get(product); } - const resultPromise = totalSalesRaw(product) - runningRequests.set(product, resultPromise) + const resultPromise = totalSalesRaw(product); + runningRequests.set(product, resultPromise); resultPromise.finally(() => { - runningRequests.delete(product) - }) + runningRequests.delete(product); + }); - return resultPromise + return resultPromise; } diff --git a/11-advanced-recipes/04-batching-and-caching/totalSalesCache.js b/11-advanced-recipes/04-batching-and-caching/totalSalesCache.js index c8aea70..8463841 100644 --- a/11-advanced-recipes/04-batching-and-caching/totalSalesCache.js +++ b/11-advanced-recipes/04-batching-and-caching/totalSalesCache.js @@ -1,27 +1,27 @@ -import { totalSales as totalSalesRaw } from './totalSales.js' +import { totalSales as totalSalesRaw } from "./totalSales.js"; -const CACHE_TTL = 30 * 1000 // 30 seconds TTL -const cache = new Map() +const CACHE_TTL = 30 * 1000; // 30 seconds TTL +const cache = new Map(); export function totalSales(product) { if (cache.has(product)) { - console.log('Cache hit') - return cache.get(product) + console.log("Cache hit"); + return cache.get(product); } - const resultPromise = totalSalesRaw(product) - cache.set(product, resultPromise) + const resultPromise = totalSalesRaw(product); + cache.set(product, resultPromise); resultPromise.then( () => { setTimeout(() => { - cache.delete(product) - }, CACHE_TTL) + cache.delete(product); + }, CACHE_TTL); }, - err => { - cache.delete(product) - throw err - } - ) + (err) => { + cache.delete(product); + throw err; + }, + ); - return resultPromise + return resultPromise; } diff --git a/11-advanced-recipes/05-canceling-async-simple/asyncRoutine.js b/11-advanced-recipes/05-canceling-async-simple/asyncRoutine.js index 6960b49..137ad26 100644 --- a/11-advanced-recipes/05-canceling-async-simple/asyncRoutine.js +++ b/11-advanced-recipes/05-canceling-async-simple/asyncRoutine.js @@ -1,9 +1,9 @@ export function asyncRoutine(label) { - console.log(`Starting async routine ${label}`) - return new Promise(resolve => { + console.log(`Starting async routine ${label}`); + return new Promise((resolve) => { setTimeout(() => { - console.log(`Async routine ${label} completed`) - resolve(`Async routine ${label} result`) - }, 100) - }) + console.log(`Async routine ${label} completed`); + resolve(`Async routine ${label} result`); + }, 100); + }); } diff --git a/11-advanced-recipes/05-canceling-async-simple/cancelError.js b/11-advanced-recipes/05-canceling-async-simple/cancelError.js index d1f5c1a..eba4fad 100644 --- a/11-advanced-recipes/05-canceling-async-simple/cancelError.js +++ b/11-advanced-recipes/05-canceling-async-simple/cancelError.js @@ -1,6 +1,6 @@ export class CancelError extends Error { constructor() { - super('Canceled') - this.isCanceled = true + super("Canceled"); + this.isCanceled = true; } } diff --git a/11-advanced-recipes/05-canceling-async-simple/index.js b/11-advanced-recipes/05-canceling-async-simple/index.js index a681560..e0f0564 100644 --- a/11-advanced-recipes/05-canceling-async-simple/index.js +++ b/11-advanced-recipes/05-canceling-async-simple/index.js @@ -1,34 +1,34 @@ -import { asyncRoutine } from './asyncRoutine.js' -import { CancelError } from './cancelError.js' +import { asyncRoutine } from "./asyncRoutine.js"; +import { CancelError } from "./cancelError.js"; async function cancelable(cancelObj) { - const resA = await asyncRoutine('A') - console.log(resA) + const resA = await asyncRoutine("A"); + console.log(resA); if (cancelObj.cancelRequested) { - throw new CancelError() + throw new CancelError(); } - const resB = await asyncRoutine('B') - console.log(resB) + const resB = await asyncRoutine("B"); + console.log(resB); if (cancelObj.cancelRequested) { - throw new CancelError() + throw new CancelError(); } - const resC = await asyncRoutine('C') - console.log(resC) + const resC = await asyncRoutine("C"); + console.log(resC); } -const cancelObj = { cancelRequested: false } +const cancelObj = { cancelRequested: false }; setTimeout(() => { - cancelObj.cancelRequested = true -}, 100) + cancelObj.cancelRequested = true; +}, 100); try { - await cancelable(cancelObj) + await cancelable(cancelObj); } catch (err) { if (err instanceof CancelError) { - console.log('Function canceled') + console.log("Function canceled"); } else { - console.error(err) + console.error(err); } } diff --git a/11-advanced-recipes/06-canceling-async-wrapper/asyncRoutine.js b/11-advanced-recipes/06-canceling-async-wrapper/asyncRoutine.js index 6960b49..137ad26 100644 --- a/11-advanced-recipes/06-canceling-async-wrapper/asyncRoutine.js +++ b/11-advanced-recipes/06-canceling-async-wrapper/asyncRoutine.js @@ -1,9 +1,9 @@ export function asyncRoutine(label) { - console.log(`Starting async routine ${label}`) - return new Promise(resolve => { + console.log(`Starting async routine ${label}`); + return new Promise((resolve) => { setTimeout(() => { - console.log(`Async routine ${label} completed`) - resolve(`Async routine ${label} result`) - }, 100) - }) + console.log(`Async routine ${label} completed`); + resolve(`Async routine ${label} result`); + }, 100); + }); } diff --git a/11-advanced-recipes/06-canceling-async-wrapper/cancelError.js b/11-advanced-recipes/06-canceling-async-wrapper/cancelError.js index d1f5c1a..eba4fad 100644 --- a/11-advanced-recipes/06-canceling-async-wrapper/cancelError.js +++ b/11-advanced-recipes/06-canceling-async-wrapper/cancelError.js @@ -1,6 +1,6 @@ export class CancelError extends Error { constructor() { - super('Canceled') - this.isCanceled = true + super("Canceled"); + this.isCanceled = true; } } diff --git a/11-advanced-recipes/06-canceling-async-wrapper/cancelWrapper.js b/11-advanced-recipes/06-canceling-async-wrapper/cancelWrapper.js index a7052f2..efc86b2 100644 --- a/11-advanced-recipes/06-canceling-async-wrapper/cancelWrapper.js +++ b/11-advanced-recipes/06-canceling-async-wrapper/cancelWrapper.js @@ -1,18 +1,18 @@ -import { CancelError } from './cancelError.js' +import { CancelError } from "./cancelError.js"; export function createCancelWrapper() { - let cancelRequested = false + let cancelRequested = false; function cancel() { - cancelRequested = true + cancelRequested = true; } function callIfNotCanceled(func, ...args) { if (cancelRequested) { - return Promise.reject(new CancelError()) + return Promise.reject(new CancelError()); } - return func(...args) + return func(...args); } - return { callIfNotCanceled, cancel } + return { callIfNotCanceled, cancel }; } diff --git a/11-advanced-recipes/06-canceling-async-wrapper/index.js b/11-advanced-recipes/06-canceling-async-wrapper/index.js index 30adb32..054379e 100644 --- a/11-advanced-recipes/06-canceling-async-wrapper/index.js +++ b/11-advanced-recipes/06-canceling-async-wrapper/index.js @@ -1,25 +1,25 @@ -import { asyncRoutine } from './asyncRoutine.js' -import { CancelError } from './cancelError.js' -import { createCancelWrapper } from './cancelWrapper.js' +import { asyncRoutine } from "./asyncRoutine.js"; +import { CancelError } from "./cancelError.js"; +import { createCancelWrapper } from "./cancelWrapper.js"; async function cancelable(callIfNotCanceled) { - const resA = await callIfNotCanceled(asyncRoutine, 'A') - console.log(resA) - const resB = await callIfNotCanceled(asyncRoutine, 'B') - console.log(resB) - const resC = await callIfNotCanceled(asyncRoutine, 'C') - console.log(resC) + const resA = await callIfNotCanceled(asyncRoutine, "A"); + console.log(resA); + const resB = await callIfNotCanceled(asyncRoutine, "B"); + console.log(resB); + const resC = await callIfNotCanceled(asyncRoutine, "C"); + console.log(resC); } -const { callIfNotCanceled, cancel } = createCancelWrapper() -setTimeout(cancel, 100) +const { callIfNotCanceled, cancel } = createCancelWrapper(); +setTimeout(cancel, 100); try { - await cancelable(callIfNotCanceled) + await cancelable(callIfNotCanceled); } catch (err) { if (err instanceof CancelError) { - console.log('Function canceled') + console.log("Function canceled"); } else { - console.error(err) + console.error(err); } } diff --git a/11-advanced-recipes/07-canceling-abort-controller/asyncRoutine.js b/11-advanced-recipes/07-canceling-abort-controller/asyncRoutine.js index 6960b49..137ad26 100644 --- a/11-advanced-recipes/07-canceling-abort-controller/asyncRoutine.js +++ b/11-advanced-recipes/07-canceling-abort-controller/asyncRoutine.js @@ -1,9 +1,9 @@ export function asyncRoutine(label) { - console.log(`Starting async routine ${label}`) - return new Promise(resolve => { + console.log(`Starting async routine ${label}`); + return new Promise((resolve) => { setTimeout(() => { - console.log(`Async routine ${label} completed`) - resolve(`Async routine ${label} result`) - }, 100) - }) + console.log(`Async routine ${label} completed`); + resolve(`Async routine ${label} result`); + }, 100); + }); } diff --git a/11-advanced-recipes/07-canceling-abort-controller/index.js b/11-advanced-recipes/07-canceling-abort-controller/index.js index ddee3e5..3f0597a 100644 --- a/11-advanced-recipes/07-canceling-abort-controller/index.js +++ b/11-advanced-recipes/07-canceling-abort-controller/index.js @@ -1,28 +1,28 @@ -import { asyncRoutine } from './asyncRoutine.js' +import { asyncRoutine } from "./asyncRoutine.js"; async function cancelable(abortSignal) { - abortSignal.throwIfAborted() - const resA = await asyncRoutine('A') - console.log(resA) + abortSignal.throwIfAborted(); + const resA = await asyncRoutine("A"); + console.log(resA); - abortSignal.throwIfAborted() - const resB = await asyncRoutine('B') - console.log(resB) + abortSignal.throwIfAborted(); + const resB = await asyncRoutine("B"); + console.log(resB); - abortSignal.throwIfAborted() - const resC = await asyncRoutine('C') - console.log(resC) + abortSignal.throwIfAborted(); + const resC = await asyncRoutine("C"); + console.log(resC); } -const ac = new AbortController() -setTimeout(() => ac.abort(), 100) +const ac = new AbortController(); +setTimeout(() => ac.abort(), 100); try { - await cancelable(ac.signal) + await cancelable(ac.signal); } catch (err) { - if (err.name === 'AbortError') { - console.log('Function canceled') + if (err.name === "AbortError") { + console.log("Function canceled"); } else { - console.error(err) + console.error(err); } } diff --git a/11-advanced-recipes/08-canceling-async-generator/asyncRoutine.js b/11-advanced-recipes/08-canceling-async-generator/asyncRoutine.js index 6960b49..137ad26 100644 --- a/11-advanced-recipes/08-canceling-async-generator/asyncRoutine.js +++ b/11-advanced-recipes/08-canceling-async-generator/asyncRoutine.js @@ -1,9 +1,9 @@ export function asyncRoutine(label) { - console.log(`Starting async routine ${label}`) - return new Promise(resolve => { + console.log(`Starting async routine ${label}`); + return new Promise((resolve) => { setTimeout(() => { - console.log(`Async routine ${label} completed`) - resolve(`Async routine ${label} result`) - }, 100) - }) + console.log(`Async routine ${label} completed`); + resolve(`Async routine ${label} result`); + }, 100); + }); } diff --git a/11-advanced-recipes/08-canceling-async-generator/cancelError.js b/11-advanced-recipes/08-canceling-async-generator/cancelError.js index d1f5c1a..eba4fad 100644 --- a/11-advanced-recipes/08-canceling-async-generator/cancelError.js +++ b/11-advanced-recipes/08-canceling-async-generator/cancelError.js @@ -1,6 +1,6 @@ export class CancelError extends Error { constructor() { - super('Canceled') - this.isCanceled = true + super("Canceled"); + this.isCanceled = true; } } diff --git a/11-advanced-recipes/08-canceling-async-generator/createAsyncCancelable.js b/11-advanced-recipes/08-canceling-async-generator/createAsyncCancelable.js index 336019d..7638099 100644 --- a/11-advanced-recipes/08-canceling-async-generator/createAsyncCancelable.js +++ b/11-advanced-recipes/08-canceling-async-generator/createAsyncCancelable.js @@ -1,39 +1,39 @@ -import { CancelError } from './cancelError.js' +import { CancelError } from "./cancelError.js"; export function createAsyncCancelable(generatorFunction) { return function asyncCancelable(...args) { - const generatorObject = generatorFunction(...args) - let cancelRequested = false + const generatorObject = generatorFunction(...args); + let cancelRequested = false; function cancel() { - cancelRequested = true + cancelRequested = true; } const promise = new Promise((resolve, reject) => { // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: async function nextStep(prevResult) { if (cancelRequested) { - return reject(new CancelError()) + return reject(new CancelError()); } if (prevResult.done) { - return resolve(prevResult.value) + return resolve(prevResult.value); } try { - nextStep(generatorObject.next(await prevResult.value)) + nextStep(generatorObject.next(await prevResult.value)); } catch (err) { try { - nextStep(generatorObject.throw(err)) + nextStep(generatorObject.throw(err)); } catch (err2) { - reject(err2) + reject(err2); } } } - nextStep({}) - }) + nextStep({}); + }); - return { promise, cancel } - } + return { promise, cancel }; + }; } diff --git a/11-advanced-recipes/08-canceling-async-generator/index.js b/11-advanced-recipes/08-canceling-async-generator/index.js index 165cbab..eadd3f4 100644 --- a/11-advanced-recipes/08-canceling-async-generator/index.js +++ b/11-advanced-recipes/08-canceling-async-generator/index.js @@ -1,25 +1,25 @@ -import { asyncRoutine } from './asyncRoutine.js' -import { CancelError } from './cancelError.js' -import { createAsyncCancelable } from './createAsyncCancelable.js' +import { asyncRoutine } from "./asyncRoutine.js"; +import { CancelError } from "./cancelError.js"; +import { createAsyncCancelable } from "./createAsyncCancelable.js"; const cancelable = createAsyncCancelable(function* () { - const resA = yield asyncRoutine('A') - console.log(resA) - const resB = yield asyncRoutine('B') - console.log(resB) - const resC = yield asyncRoutine('C') - console.log(resC) -}) + const resA = yield asyncRoutine("A"); + console.log(resA); + const resB = yield asyncRoutine("B"); + console.log(resB); + const resC = yield asyncRoutine("C"); + console.log(resC); +}); -const { promise, cancel } = cancelable() -setTimeout(cancel, 100) +const { promise, cancel } = cancelable(); +setTimeout(cancel, 100); try { - await promise + await promise; } catch (err) { if (err instanceof CancelError) { - console.log('Function canceled') + console.log("Function canceled"); } else { - console.error(err) + console.error(err); } } diff --git a/11-advanced-recipes/09-cpu-bound/index.js b/11-advanced-recipes/09-cpu-bound/index.js index 0df1b38..1540cf4 100644 --- a/11-advanced-recipes/09-cpu-bound/index.js +++ b/11-advanced-recipes/09-cpu-bound/index.js @@ -1,25 +1,26 @@ -import { createServer } from 'node:http' -import { SubsetSum } from './subsetSum.js' +import { createServer } from "node:http"; +import { SubsetSum } from "./subsetSum.js"; + // import { SubsetSum } from './subsetSumDefer.js' // import { SubsetSum } from './subsetSumFork.js' // import { SubsetSum } from './subsetSumThreads.js' createServer((req, res) => { - const url = new URL(req.url, 'http://localhost') - if (url.pathname !== '/subsetSum') { - res.writeHead(200) - return res.end("I'm alive!\n") + const url = new URL(req.url, "http://localhost"); + if (url.pathname !== "/subsetSum") { + res.writeHead(200); + return res.end("I'm alive!\n"); } - const data = JSON.parse(url.searchParams.get('data')) - const sum = JSON.parse(url.searchParams.get('sum')) - res.writeHead(200) - const subsetSum = new SubsetSum(sum, data) - subsetSum.on('match', match => { - res.cork() - res.write(`Match: ${JSON.stringify(match)}\n`) - res.uncork() - }) - subsetSum.on('end', () => res.end()) - subsetSum.start() -}).listen(8000, () => console.log('Server started')) + const data = JSON.parse(url.searchParams.get("data")); + const sum = JSON.parse(url.searchParams.get("sum")); + res.writeHead(200); + const subsetSum = new SubsetSum(sum, data); + subsetSum.on("match", (match) => { + res.cork(); + res.write(`Match: ${JSON.stringify(match)}\n`); + res.uncork(); + }); + subsetSum.on("end", () => res.end()); + subsetSum.start(); +}).listen(8000, () => console.log("Server started")); diff --git a/11-advanced-recipes/09-cpu-bound/processPool.js b/11-advanced-recipes/09-cpu-bound/processPool.js index 630fe35..008655a 100644 --- a/11-advanced-recipes/09-cpu-bound/processPool.js +++ b/11-advanced-recipes/09-cpu-bound/processPool.js @@ -1,50 +1,50 @@ -import { fork } from 'node:child_process' +import { fork } from "node:child_process"; export class ProcessPool { constructor(file, poolMax) { - this.file = file - this.poolMax = poolMax - this.pool = [] - this.active = [] - this.waiting = [] + this.file = file; + this.poolMax = poolMax; + this.pool = []; + this.active = []; + this.waiting = []; } acquire() { return new Promise((resolve, reject) => { - let worker + let worker; if (this.pool.length > 0) { - worker = this.pool.pop() - this.active.push(worker) - return resolve(worker) + worker = this.pool.pop(); + this.active.push(worker); + return resolve(worker); } if (this.active.length >= this.poolMax) { - return this.waiting.push({ resolve, reject }) + return this.waiting.push({ resolve, reject }); } - worker = fork(this.file) - worker.once('message', message => { - if (message === 'ready') { - this.active.push(worker) - return resolve(worker) + worker = fork(this.file); + worker.once("message", (message) => { + if (message === "ready") { + this.active.push(worker); + return resolve(worker); } - worker.kill() - reject(new Error('Improper process start')) - }) - worker.once('exit', code => { - console.log(`Worker exited with code ${code}`) - this.active = this.active.filter(w => worker !== w) - this.pool = this.pool.filter(w => worker !== w) - }) - }) + worker.kill(); + reject(new Error("Improper process start")); + }); + worker.once("exit", (code) => { + console.log(`Worker exited with code ${code}`); + this.active = this.active.filter((w) => worker !== w); + this.pool = this.pool.filter((w) => worker !== w); + }); + }); } release(worker) { if (this.waiting.length > 0) { - const { resolve } = this.waiting.shift() - return resolve(worker) + const { resolve } = this.waiting.shift(); + return resolve(worker); } - this.active = this.active.filter(w => worker !== w) - this.pool.push(worker) + this.active = this.active.filter((w) => worker !== w); + this.pool.push(worker); } } diff --git a/11-advanced-recipes/09-cpu-bound/subsetSum.js b/11-advanced-recipes/09-cpu-bound/subsetSum.js index 0cb0689..8194b62 100644 --- a/11-advanced-recipes/09-cpu-bound/subsetSum.js +++ b/11-advanced-recipes/09-cpu-bound/subsetSum.js @@ -1,31 +1,31 @@ -import { EventEmitter } from 'node:events' +import { EventEmitter } from "node:events"; export class SubsetSum extends EventEmitter { constructor(sum, set) { - super() - this.sum = sum - this.set = set - this.totalSubsets = 0 + super(); + this.sum = sum; + this.set = set; + this.totalSubsets = 0; } _combine(set, subset) { for (let i = 0; i < set.length; i++) { - const newSubset = subset.concat(set[i]) - this._combine(set.slice(i + 1), newSubset) - this._processSubset(newSubset) + const newSubset = subset.concat(set[i]); + this._combine(set.slice(i + 1), newSubset); + this._processSubset(newSubset); } } _processSubset(subset) { - console.log('Subset', ++this.totalSubsets, subset) - const res = subset.reduce((prev, item) => prev + item, 0) + console.log("Subset", ++this.totalSubsets, subset); + const res = subset.reduce((prev, item) => prev + item, 0); if (res === this.sum) { - this.emit('match', subset) + this.emit("match", subset); } } start() { - this._combine(this.set, []) - this.emit('end') + this._combine(this.set, []); + this.emit("end"); } } diff --git a/11-advanced-recipes/09-cpu-bound/subsetSumDefer.js b/11-advanced-recipes/09-cpu-bound/subsetSumDefer.js index c1c014a..07b8fe0 100644 --- a/11-advanced-recipes/09-cpu-bound/subsetSumDefer.js +++ b/11-advanced-recipes/09-cpu-bound/subsetSumDefer.js @@ -1,41 +1,41 @@ -import { EventEmitter } from 'node:events' +import { EventEmitter } from "node:events"; export class SubsetSum extends EventEmitter { constructor(sum, set) { - super() - this.sum = sum - this.set = set - this.totalSubsets = 0 + super(); + this.sum = sum; + this.set = set; + this.totalSubsets = 0; } _combineInterleaved(set, subset) { - this.runningCombine++ + this.runningCombine++; setImmediate(() => { - this._combine(set, subset) + this._combine(set, subset); if (--this.runningCombine === 0) { - this.emit('end') + this.emit("end"); } - }) + }); } _combine(set, subset) { for (let i = 0; i < set.length; i++) { - const newSubset = subset.concat(set[i]) - this._combineInterleaved(set.slice(i + 1), newSubset) - this._processSubset(newSubset) + const newSubset = subset.concat(set[i]); + this._combineInterleaved(set.slice(i + 1), newSubset); + this._processSubset(newSubset); } } _processSubset(subset) { - console.log('Subset', ++this.totalSubsets, subset) - const res = subset.reduce((prev, item) => prev + item, 0) + console.log("Subset", ++this.totalSubsets, subset); + const res = subset.reduce((prev, item) => prev + item, 0); if (res === this.sum) { - this.emit('match', subset) + this.emit("match", subset); } } start() { - this.runningCombine = 0 - this._combineInterleaved(this.set, []) + this.runningCombine = 0; + this._combineInterleaved(this.set, []); } } diff --git a/11-advanced-recipes/09-cpu-bound/subsetSumFork.js b/11-advanced-recipes/09-cpu-bound/subsetSumFork.js index 0e2bdd8..bf86163 100644 --- a/11-advanced-recipes/09-cpu-bound/subsetSumFork.js +++ b/11-advanced-recipes/09-cpu-bound/subsetSumFork.js @@ -1,34 +1,34 @@ -import { EventEmitter } from 'node:events' -import { join } from 'node:path' -import { ProcessPool } from './processPool.js' +import { EventEmitter } from "node:events"; +import { join } from "node:path"; +import { ProcessPool } from "./processPool.js"; const workerFile = join( import.meta.dirname, - 'workers', - 'subsetSumProcessWorker.js' -) -const workers = new ProcessPool(workerFile, 2) + "workers", + "subsetSumProcessWorker.js", +); +const workers = new ProcessPool(workerFile, 2); export class SubsetSum extends EventEmitter { constructor(sum, set) { - super() - this.sum = sum - this.set = set + super(); + this.sum = sum; + this.set = set; } async start() { - const worker = await workers.acquire() - worker.send({ sum: this.sum, set: this.set }) + const worker = await workers.acquire(); + worker.send({ sum: this.sum, set: this.set }); - const onMessage = msg => { - if (msg.event === 'end') { - worker.removeListener('message', onMessage) - workers.release(worker) + const onMessage = (msg) => { + if (msg.event === "end") { + worker.removeListener("message", onMessage); + workers.release(worker); } - this.emit(msg.event, msg.data) - } + this.emit(msg.event, msg.data); + }; - worker.on('message', onMessage) + worker.on("message", onMessage); } } diff --git a/11-advanced-recipes/09-cpu-bound/subsetSumThreads.js b/11-advanced-recipes/09-cpu-bound/subsetSumThreads.js index 38dc73f..55fe1d4 100644 --- a/11-advanced-recipes/09-cpu-bound/subsetSumThreads.js +++ b/11-advanced-recipes/09-cpu-bound/subsetSumThreads.js @@ -1,34 +1,34 @@ -import { EventEmitter } from 'node:events' -import { join } from 'node:path' -import { ThreadPool } from './threadPool.js' +import { EventEmitter } from "node:events"; +import { join } from "node:path"; +import { ThreadPool } from "./threadPool.js"; const workerFile = join( import.meta.dirname, - 'workers', - 'subsetSumThreadWorker.js' -) -const workers = new ThreadPool(workerFile, 2) + "workers", + "subsetSumThreadWorker.js", +); +const workers = new ThreadPool(workerFile, 2); export class SubsetSum extends EventEmitter { constructor(sum, set) { - super() - this.sum = sum - this.set = set + super(); + this.sum = sum; + this.set = set; } async start() { - const worker = await workers.acquire() - worker.postMessage({ sum: this.sum, set: this.set }) + const worker = await workers.acquire(); + worker.postMessage({ sum: this.sum, set: this.set }); - const onMessage = msg => { - if (msg.event === 'end') { - worker.removeListener('message', onMessage) - workers.release(worker) + const onMessage = (msg) => { + if (msg.event === "end") { + worker.removeListener("message", onMessage); + workers.release(worker); } - this.emit(msg.event, msg.data) - } + this.emit(msg.event, msg.data); + }; - worker.on('message', onMessage) + worker.on("message", onMessage); } } diff --git a/11-advanced-recipes/09-cpu-bound/threadPool.js b/11-advanced-recipes/09-cpu-bound/threadPool.js index 5fd8688..6119fd4 100644 --- a/11-advanced-recipes/09-cpu-bound/threadPool.js +++ b/11-advanced-recipes/09-cpu-bound/threadPool.js @@ -1,46 +1,46 @@ -import { Worker } from 'node:worker_threads' +import { Worker } from "node:worker_threads"; export class ThreadPool { constructor(file, poolMax) { - this.file = file - this.poolMax = poolMax - this.pool = [] - this.active = [] - this.waiting = [] + this.file = file; + this.poolMax = poolMax; + this.pool = []; + this.active = []; + this.waiting = []; } acquire() { return new Promise((resolve, reject) => { - let worker + let worker; if (this.pool.length > 0) { - worker = this.pool.pop() - this.active.push(worker) - return resolve(worker) + worker = this.pool.pop(); + this.active.push(worker); + return resolve(worker); } if (this.active.length >= this.poolMax) { - return this.waiting.push({ resolve, reject }) + return this.waiting.push({ resolve, reject }); } - worker = new Worker(this.file) - worker.once('online', () => { - this.active.push(worker) - resolve(worker) - }) - worker.once('exit', code => { - console.log(`Worker exited with code ${code}`) - this.active = this.active.filter(w => worker !== w) - this.pool = this.pool.filter(w => worker !== w) - }) - }) + worker = new Worker(this.file); + worker.once("online", () => { + this.active.push(worker); + resolve(worker); + }); + worker.once("exit", (code) => { + console.log(`Worker exited with code ${code}`); + this.active = this.active.filter((w) => worker !== w); + this.pool = this.pool.filter((w) => worker !== w); + }); + }); } release(worker) { if (this.waiting.length > 0) { - const { resolve } = this.waiting.shift() - return resolve(worker) + const { resolve } = this.waiting.shift(); + return resolve(worker); } - this.active = this.active.filter(w => worker !== w) - this.pool.push(worker) + this.active = this.active.filter((w) => worker !== w); + this.pool.push(worker); } } diff --git a/11-advanced-recipes/09-cpu-bound/workers/subsetSumProcessWorker.js b/11-advanced-recipes/09-cpu-bound/workers/subsetSumProcessWorker.js index dd2eab1..ba5823f 100644 --- a/11-advanced-recipes/09-cpu-bound/workers/subsetSumProcessWorker.js +++ b/11-advanced-recipes/09-cpu-bound/workers/subsetSumProcessWorker.js @@ -1,17 +1,17 @@ -import { SubsetSum } from '../subsetSum.js' +import { SubsetSum } from "../subsetSum.js"; -process.on('message', msg => { - const subsetSum = new SubsetSum(msg.sum, msg.set) +process.on("message", (msg) => { + const subsetSum = new SubsetSum(msg.sum, msg.set); - subsetSum.on('match', data => { - process.send({ event: 'match', data: data }) - }) + subsetSum.on("match", (data) => { + process.send({ event: "match", data: data }); + }); - subsetSum.on('end', data => { - process.send({ event: 'end', data: data }) - }) + subsetSum.on("end", (data) => { + process.send({ event: "end", data: data }); + }); - subsetSum.start() -}) + subsetSum.start(); +}); -process.send('ready') +process.send("ready"); diff --git a/11-advanced-recipes/09-cpu-bound/workers/subsetSumThreadWorker.js b/11-advanced-recipes/09-cpu-bound/workers/subsetSumThreadWorker.js index 2085c8c..9d446a9 100644 --- a/11-advanced-recipes/09-cpu-bound/workers/subsetSumThreadWorker.js +++ b/11-advanced-recipes/09-cpu-bound/workers/subsetSumThreadWorker.js @@ -1,16 +1,16 @@ -import { parentPort } from 'node:worker_threads' -import { SubsetSum } from '../subsetSum.js' +import { parentPort } from "node:worker_threads"; +import { SubsetSum } from "../subsetSum.js"; -parentPort.on('message', msg => { - const subsetSum = new SubsetSum(msg.sum, msg.set) +parentPort.on("message", (msg) => { + const subsetSum = new SubsetSum(msg.sum, msg.set); - subsetSum.on('match', data => { - parentPort.postMessage({ event: 'match', data: data }) - }) + subsetSum.on("match", (data) => { + parentPort.postMessage({ event: "match", data: data }); + }); - subsetSum.on('end', data => { - parentPort.postMessage({ event: 'end', data: data }) - }) + subsetSum.on("end", (data) => { + parentPort.postMessage({ event: "end", data: data }); + }); - subsetSum.start() -}) + subsetSum.start(); +}); diff --git a/12-scalability-and-architectural-patterns/01-plain-http/app.js b/12-scalability-and-architectural-patterns/01-plain-http/app.js index 97b5e30..17ef5a4 100644 --- a/12-scalability-and-architectural-patterns/01-plain-http/app.js +++ b/12-scalability-and-architectural-patterns/01-plain-http/app.js @@ -1,14 +1,14 @@ -import { createServer } from 'node:http' +import { createServer } from "node:http"; const server = createServer((_req, res) => { // simulates CPU intensive work - let i = 1e7 + let i = 1e7; while (i > 0) { - i-- + i--; } - console.log(`Handling request from ${process.pid}`) - res.end(`Hello from ${process.pid}\n`) -}) + console.log(`Handling request from ${process.pid}`); + res.end(`Hello from ${process.pid}\n`); +}); -server.listen(8080, () => console.log(`Started at ${process.pid}`)) +server.listen(8080, () => console.log(`Started at ${process.pid}`)); diff --git a/12-scalability-and-architectural-patterns/02-http-cluster/app.js b/12-scalability-and-architectural-patterns/02-http-cluster/app.js index 5ee3dac..ddc560c 100644 --- a/12-scalability-and-architectural-patterns/02-http-cluster/app.js +++ b/12-scalability-and-architectural-patterns/02-http-cluster/app.js @@ -1,24 +1,24 @@ -import cluster from 'node:cluster' -import { createServer } from 'node:http' -import { cpus } from 'node:os' +import cluster from "node:cluster"; +import { createServer } from "node:http"; +import { cpus } from "node:os"; if (cluster.isPrimary) { - const availableCpus = cpus() - console.log(`Clustering to ${availableCpus.length} processes`) + const availableCpus = cpus(); + console.log(`Clustering to ${availableCpus.length} processes`); for (const _ of availableCpus) { - cluster.fork() + cluster.fork(); } } else { const server = createServer((_req, res) => { // simulates CPU intensive work - let i = 1e7 + let i = 1e7; while (i > 0) { - i-- + i--; } - console.log(`Handling request from ${process.pid}`) - res.end(`Hello from ${process.pid}\n`) - }) + console.log(`Handling request from ${process.pid}`); + res.end(`Hello from ${process.pid}\n`); + }); - server.listen(8080, () => console.log(`Started at ${process.pid}`)) + server.listen(8080, () => console.log(`Started at ${process.pid}`)); } diff --git a/12-scalability-and-architectural-patterns/03-http-cluster-resilient/app.js b/12-scalability-and-architectural-patterns/03-http-cluster-resilient/app.js index 79aa3ca..09bf644 100644 --- a/12-scalability-and-architectural-patterns/03-http-cluster-resilient/app.js +++ b/12-scalability-and-architectural-patterns/03-http-cluster-resilient/app.js @@ -1,38 +1,40 @@ -import cluster from 'node:cluster' -import { createServer } from 'node:http' -import { cpus } from 'node:os' +import cluster from "node:cluster"; +import { createServer } from "node:http"; +import { cpus } from "node:os"; if (cluster.isPrimary) { - const availableCpus = cpus() - console.log(`Clustering to ${availableCpus.length} processes`) + const availableCpus = cpus(); + console.log(`Clustering to ${availableCpus.length} processes`); for (const _ of availableCpus) { - cluster.fork() + cluster.fork(); } - cluster.on('exit', (worker, code) => { + cluster.on("exit", (worker, code) => { if (code !== 0 && !worker.exitedAfterDisconnect) { - console.log(`Worker ${worker.process.pid} crashed. Starting a new worker`) - cluster.fork() + console.log( + `Worker ${worker.process.pid} crashed. Starting a new worker`, + ); + cluster.fork(); } - }) + }); } else { setInterval( () => { if (Math.random() < 0.5) { - throw new Error(`Ooops... ${process.pid} crashed!`) + throw new Error(`Ooops... ${process.pid} crashed!`); } }, - Math.ceil(Math.random() * 3) * 1000 - ) + Math.ceil(Math.random() * 3) * 1000, + ); const server = createServer((_req, res) => { // simulates CPU intensive work - let i = 1e7 + let i = 1e7; while (i > 0) { - i-- + i--; } - console.log(`Handling request from ${process.pid}`) - res.end(`Hello from ${process.pid}\n`) - }) + console.log(`Handling request from ${process.pid}`); + res.end(`Hello from ${process.pid}\n`); + }); - server.listen(8080, () => console.log(`Started at ${process.pid}`)) + server.listen(8080, () => console.log(`Started at ${process.pid}`)); } diff --git a/12-scalability-and-architectural-patterns/04-http-cluster-zero-downtime/app.js b/12-scalability-and-architectural-patterns/04-http-cluster-zero-downtime/app.js index 66f1d8e..143b013 100644 --- a/12-scalability-and-architectural-patterns/04-http-cluster-zero-downtime/app.js +++ b/12-scalability-and-architectural-patterns/04-http-cluster-zero-downtime/app.js @@ -1,44 +1,46 @@ -import cluster from 'node:cluster' -import { once } from 'node:events' -import { createServer } from 'node:http' -import { cpus } from 'node:os' +import cluster from "node:cluster"; +import { once } from "node:events"; +import { createServer } from "node:http"; +import { cpus } from "node:os"; if (cluster.isPrimary) { - const availableCpus = cpus() - console.log(`Clustering to ${availableCpus.length} processes`) + const availableCpus = cpus(); + console.log(`Clustering to ${availableCpus.length} processes`); for (const _ of availableCpus) { - cluster.fork() + cluster.fork(); } - cluster.on('exit', (worker, code) => { + cluster.on("exit", (worker, code) => { if (code !== 0 && !worker.exitedAfterDisconnect) { - console.log(`Worker ${worker.process.pid} crashed. Starting a new worker`) - cluster.fork() + console.log( + `Worker ${worker.process.pid} crashed. Starting a new worker`, + ); + cluster.fork(); } - }) - process.on('SIGUSR2', async () => { - const workers = Object.values(cluster.workers) + }); + process.on("SIGUSR2", async () => { + const workers = Object.values(cluster.workers); for (const worker of workers) { - console.log(`Stopping worker: ${worker.process.pid}`) - worker.disconnect() - await once(worker, 'exit') + console.log(`Stopping worker: ${worker.process.pid}`); + worker.disconnect(); + await once(worker, "exit"); if (!worker.exitedAfterDisconnect) { - continue + continue; } - const newWorker = cluster.fork() - await once(newWorker, 'listening') + const newWorker = cluster.fork(); + await once(newWorker, "listening"); } - }) + }); } else { const server = createServer((_req, res) => { // simulates CPU intensive work - let i = 1e7 + let i = 1e7; while (i > 0) { - i-- + i--; } - console.log(`Handling request from ${process.pid}`) - res.end(`Hello from ${process.pid}\n`) - }) + console.log(`Handling request from ${process.pid}`); + res.end(`Hello from ${process.pid}\n`); + }); - server.listen(8080, () => console.log(`Started at ${process.pid}`)) + server.listen(8080, () => console.log(`Started at ${process.pid}`)); } diff --git a/12-scalability-and-architectural-patterns/05-http-load-balancer/app.js b/12-scalability-and-architectural-patterns/05-http-load-balancer/app.js index 9e5db7f..0a945f2 100644 --- a/12-scalability-and-architectural-patterns/05-http-load-balancer/app.js +++ b/12-scalability-and-architectural-patterns/05-http-load-balancer/app.js @@ -1,13 +1,13 @@ -import { createServer } from 'node:http' +import { createServer } from "node:http"; const server = createServer((_req, res) => { - let i = 1e7 + let i = 1e7; while (i > 0) { - i-- + i--; } - console.log(`Handling request from ${process.pid}`) - res.end(`Hello from ${process.pid}\n`) -}) + console.log(`Handling request from ${process.pid}`); + res.end(`Hello from ${process.pid}\n`); +}); -const port = Number.parseInt(process.env.PORT || process.argv[2]) || 8080 -server.listen(port, () => console.log(`Started at ${process.pid}`)) +const port = Number.parseInt(process.env.PORT || process.argv[2], 10) || 8080; +server.listen(port, () => console.log(`Started at ${process.pid}`)); diff --git a/12-scalability-and-architectural-patterns/06-http-dynamic-load-balancer/app.js b/12-scalability-and-architectural-patterns/06-http-dynamic-load-balancer/app.js index d38bca6..76ccba2 100644 --- a/12-scalability-and-architectural-patterns/06-http-dynamic-load-balancer/app.js +++ b/12-scalability-and-architectural-patterns/06-http-dynamic-load-balancer/app.js @@ -1,19 +1,19 @@ -import { randomUUID } from 'node:crypto' -import { createServer } from 'node:http' -import portfinder from 'portfinder' // v1.0.37 -import { ConsulClient } from './consul.js' +import { randomUUID } from "node:crypto"; +import { createServer } from "node:http"; +import portfinder from "portfinder"; // v1.0.37 +import { ConsulClient } from "./consul.js"; -const serviceType = process.argv[2] +const serviceType = process.argv[2]; if (!serviceType) { - console.error('Usage: node app.js ') - process.exit(1) + console.error("Usage: node app.js "); + process.exit(1); } -const consulClient = new ConsulClient() +const consulClient = new ConsulClient(); -const port = await portfinder.getPort() -const address = process.env.ADDRESS || 'localhost' -const serviceId = randomUUID() +const port = await portfinder.getPort(); +const address = process.env.ADDRESS || "localhost"; +const serviceId = randomUUID(); async function registerService() { await consulClient.registerService({ @@ -22,36 +22,38 @@ async function registerService() { address, port, tags: [serviceType], - }) + }); - console.log(`${serviceType} registered as ${serviceId} on ${address}:${port}`) + console.log( + `${serviceType} registered as ${serviceId} on ${address}:${port}`, + ); } async function unregisterService(err) { - err && console.error(err) - console.log(`deregistering ${serviceId}`) + err && console.error(err); + console.log(`deregistering ${serviceId}`); try { - await consulClient.deregisterService(serviceId) + await consulClient.deregisterService(serviceId); } catch (deregisterError) { - console.error(`Failed to deregister service: ${deregisterError.message}`) + console.error(`Failed to deregister service: ${deregisterError.message}`); } - process.exit(err ? 1 : 0) + process.exit(err ? 1 : 0); } -process.on('uncaughtException', unregisterService) -process.on('SIGINT', unregisterService) +process.on("uncaughtException", unregisterService); +process.on("SIGINT", unregisterService); const server = createServer((_req, res) => { // Simulate some processing time - let i = 1e7 + let i = 1e7; while (i > 0) { - i-- + i--; } - console.log(`Handling request from ${process.pid}`) - res.end(`${serviceType} response from ${process.pid}\n`) -}) + console.log(`Handling request from ${process.pid}`); + res.end(`${serviceType} response from ${process.pid}\n`); +}); server.listen(port, address, async () => { - console.log(`Started ${serviceType} on port ${port} with PID ${process.pid}`) - await registerService() -}) + console.log(`Started ${serviceType} on port ${port} with PID ${process.pid}`); + await registerService(); +}); diff --git a/12-scalability-and-architectural-patterns/06-http-dynamic-load-balancer/consul.js b/12-scalability-and-architectural-patterns/06-http-dynamic-load-balancer/consul.js index 1e42b62..a42c88d 100644 --- a/12-scalability-and-architectural-patterns/06-http-dynamic-load-balancer/consul.js +++ b/12-scalability-and-architectural-patterns/06-http-dynamic-load-balancer/consul.js @@ -1,10 +1,10 @@ export class ConsulClient { - constructor(baseUrl = 'http://localhost:8500') { - this.baseUrl = baseUrl + constructor(baseUrl = "http://localhost:8500") { + this.baseUrl = baseUrl; } async registerService({ name, id, address, port, tags = [] }) { - const url = `${this.baseUrl}/v1/agent/service/register` + const url = `${this.baseUrl}/v1/agent/service/register`; const body = { // biome-ignore lint/style/useNamingConvention: Consul API Name: name, @@ -16,45 +16,45 @@ export class ConsulClient { Port: port, // biome-ignore lint/style/useNamingConvention: Consul API Tags: tags, - } + }; const response = await fetch(url, { - method: 'PUT', - headers: { 'Content-Type': 'application/json' }, + method: "PUT", + headers: { "Content-Type": "application/json" }, body: JSON.stringify(body), - }) + }); if (!response.ok) { - const responseText = await response.text() // Read the response body to get more details + const responseText = await response.text(); // Read the response body to get more details throw new Error( - `Failed to register service: ${response.statusText}\n${responseText}` - ) + `Failed to register service: ${response.statusText}\n${responseText}`, + ); } } async deregisterService(serviceId) { - const url = `${this.baseUrl}/v1/agent/service/deregister/${serviceId}` - const response = await fetch(url, { method: 'PUT' }) + const url = `${this.baseUrl}/v1/agent/service/deregister/${serviceId}`; + const response = await fetch(url, { method: "PUT" }); if (!response.ok) { - const responseText = await response.text() + const responseText = await response.text(); throw new Error( - `Failed to deregister service: ${response.statusText}\n${responseText}` - ) + `Failed to deregister service: ${response.statusText}\n${responseText}`, + ); } } async getAllServices() { - const url = `${this.baseUrl}/v1/agent/services` - const response = await fetch(url) + const url = `${this.baseUrl}/v1/agent/services`; + const response = await fetch(url); if (!response.ok) { - const responseText = await response.text() + const responseText = await response.text(); throw new Error( - `Failed to get all services: ${response.statusText}\n${responseText}` - ) + `Failed to get all services: ${response.statusText}\n${responseText}`, + ); } - return response.json() + return response.json(); } } diff --git a/12-scalability-and-architectural-patterns/06-http-dynamic-load-balancer/loadBalancer.js b/12-scalability-and-architectural-patterns/06-http-dynamic-load-balancer/loadBalancer.js index 0d6e753..e2c136d 100644 --- a/12-scalability-and-architectural-patterns/06-http-dynamic-load-balancer/loadBalancer.js +++ b/12-scalability-and-architectural-patterns/06-http-dynamic-load-balancer/loadBalancer.js @@ -1,48 +1,48 @@ -import { createServer } from 'node:http' -import { createProxyServer } from 'httpxy' // v0.1.7 -import { ConsulClient } from './consul.js' +import { createServer } from "node:http"; +import { createProxyServer } from "httpxy"; // v0.1.7 +import { ConsulClient } from "./consul.js"; const routing = [ { - path: '/api', - service: 'api-service', + path: "/api", + service: "api-service", index: 0, }, { - path: '/', - service: 'webapp-service', + path: "/", + service: "webapp-service", index: 0, }, -] +]; -const consulClient = new ConsulClient() -const proxy = createProxyServer() +const consulClient = new ConsulClient(); +const proxy = createProxyServer(); const server = createServer(async (req, res) => { - const route = routing.find(route => req.url.startsWith(route.path)) + const route = routing.find((route) => req.url.startsWith(route.path)); try { - const services = await consulClient.getAllServices() - const servers = Object.values(services).filter(service => - service.Tags.includes(route.service) - ) + const services = await consulClient.getAllServices(); + const servers = Object.values(services).filter((service) => + service.Tags.includes(route.service), + ); if (servers.length > 0) { - route.index = (route.index + 1) % servers.length - const server = servers[route.index] - const target = `http://${server.Address}:${server.Port}` - proxy.web(req, res, { target }) - return + route.index = (route.index + 1) % servers.length; + const server = servers[route.index]; + const target = `http://${server.Address}:${server.Port}`; + proxy.web(req, res, { target }); + return; } } catch (err) { - console.error(err) + console.error(err); } // if servers not found or error occurs - res.writeHead(502) - return res.end('Bad gateway') -}) + res.writeHead(502); + return res.end("Bad gateway"); +}); server.listen(8080, () => { - console.log('Load balancer started on port 8080') -}) + console.log("Load balancer started on port 8080"); +}); diff --git a/12-scalability-and-architectural-patterns/07-peer-to-peer-load-balancing/app.js b/12-scalability-and-architectural-patterns/07-peer-to-peer-load-balancing/app.js index a9d3313..23bf2b0 100644 --- a/12-scalability-and-architectural-patterns/07-peer-to-peer-load-balancing/app.js +++ b/12-scalability-and-architectural-patterns/07-peer-to-peer-load-balancing/app.js @@ -1,13 +1,13 @@ -import { createServer } from 'node:http' +import { createServer } from "node:http"; -const { pid } = process +const { pid } = process; const server = createServer((req, res) => { - const url = new URL(req.url, `http://${req.headers.host}`) - const searchParams = url.searchParams + const url = new URL(req.url, `http://${req.headers.host}`); + const searchParams = url.searchParams; - console.log(`Request ${searchParams.get('request')} from ${pid}`) - res.end(`Hello from ${pid}\n`) -}) + console.log(`Request ${searchParams.get("request")} from ${pid}`); + res.end(`Hello from ${pid}\n`); +}); -const port = Number.parseInt(process.env.PORT || process.argv[2]) || 8080 -server.listen(port, () => console.log(`Started at ${pid}`)) +const port = Number.parseInt(process.env.PORT || process.argv[2], 10) || 8080; +server.listen(port, () => console.log(`Started at ${pid}`)); diff --git a/12-scalability-and-architectural-patterns/07-peer-to-peer-load-balancing/balancedRequest.js b/12-scalability-and-architectural-patterns/07-peer-to-peer-load-balancing/balancedRequest.js index 40d9611..cb894a5 100644 --- a/12-scalability-and-architectural-patterns/07-peer-to-peer-load-balancing/balancedRequest.js +++ b/12-scalability-and-architectural-patterns/07-peer-to-peer-load-balancing/balancedRequest.js @@ -1,15 +1,15 @@ const servers = [ - { host: 'localhost', port: 8081 }, - { host: 'localhost', port: 8082 }, -] -let i = 0 + { host: "localhost", port: 8081 }, + { host: "localhost", port: 8082 }, +]; +let i = 0; export function balancedRequest(url, fetchOptions = {}) { - i = (i + 1) % servers.length - const server = servers[i] + i = (i + 1) % servers.length; + const server = servers[i]; - const rewrittenUrl = new URL(url, `http://${server.host}:${server.port}`) - rewrittenUrl.host = `${server.host}:${server.port}` + const rewrittenUrl = new URL(url, `http://${server.host}:${server.port}`); + rewrittenUrl.host = `${server.host}:${server.port}`; - return fetch(rewrittenUrl.toString(), fetchOptions) + return fetch(rewrittenUrl.toString(), fetchOptions); } diff --git a/12-scalability-and-architectural-patterns/07-peer-to-peer-load-balancing/client.js b/12-scalability-and-architectural-patterns/07-peer-to-peer-load-balancing/client.js index 1e52ecf..20df27a 100644 --- a/12-scalability-and-architectural-patterns/07-peer-to-peer-load-balancing/client.js +++ b/12-scalability-and-architectural-patterns/07-peer-to-peer-load-balancing/client.js @@ -1,7 +1,7 @@ -import { balancedRequest } from './balancedRequest.js' +import { balancedRequest } from "./balancedRequest.js"; for (let i = 0; i < 10; i++) { - const response = await balancedRequest(`/?request=${i}`) - const body = await response.text() - console.log(`Req ${i} completed\nStatus: ${response.status}\nBody: ${body}`) + const response = await balancedRequest(`/?request=${i}`); + const body = await response.text(); + console.log(`Req ${i} completed\nStatus: ${response.status}\nBody: ${body}`); } diff --git a/12-scalability-and-architectural-patterns/08-scaling-an-app-on-kubernetes/app.js b/12-scalability-and-architectural-patterns/08-scaling-an-app-on-kubernetes/app.js index d3917cc..f395789 100644 --- a/12-scalability-and-architectural-patterns/08-scaling-an-app-on-kubernetes/app.js +++ b/12-scalability-and-architectural-patterns/08-scaling-an-app-on-kubernetes/app.js @@ -1,10 +1,10 @@ -import { createServer } from 'node:http' -import { hostname } from 'node:os' +import { createServer } from "node:http"; +import { hostname } from "node:os"; -const version = 1 +const version = 1; const server = createServer((_req, res) => { - res.end(`Hello from ${hostname()} (v${version})`) -}) + res.end(`Hello from ${hostname()} (v${version})`); +}); -server.listen(8080) +server.listen(8080); diff --git a/13-messaging-and-integration-patterns/01-pubsub-basic-chat/index.js b/13-messaging-and-integration-patterns/01-pubsub-basic-chat/index.js index 387d01d..5c67367 100644 --- a/13-messaging-and-integration-patterns/01-pubsub-basic-chat/index.js +++ b/13-messaging-and-integration-patterns/01-pubsub-basic-chat/index.js @@ -1,27 +1,27 @@ -import { createServer } from 'node:http' -import staticHandler from 'serve-handler' // v6.1.6 -import { WebSocketServer } from 'ws' // v8.18.2 +import { createServer } from "node:http"; +import staticHandler from "serve-handler"; // v6.1.6 +import { WebSocketServer } from "ws"; // v8.18.2 // serve static files const server = createServer((req, res) => { - return staticHandler(req, res, { public: 'web' }) -}) + return staticHandler(req, res, { public: "web" }); +}); -const wss = new WebSocketServer({ server }) -wss.on('connection', client => { - console.log('Client connected') - client.on('message', msg => { - console.log(`Message: ${msg}`) - broadcast(msg) - }) -}) +const wss = new WebSocketServer({ server }); +wss.on("connection", (client) => { + console.log("Client connected"); + client.on("message", (msg) => { + console.log(`Message: ${msg}`); + broadcast(msg); + }); +}); function broadcast(msg) { for (const client of wss.clients) { if (client.readyState === WebSocket.OPEN) { - client.send(msg) + client.send(msg); } } } -server.listen(process.argv[2] || 8080) +server.listen(process.argv[2] || 8080); diff --git a/13-messaging-and-integration-patterns/01-pubsub-basic-chat/web/index.html b/13-messaging-and-integration-patterns/01-pubsub-basic-chat/web/index.html index 4462c1d..70bec20 100644 --- a/13-messaging-and-integration-patterns/01-pubsub-basic-chat/web/index.html +++ b/13-messaging-and-integration-patterns/01-pubsub-basic-chat/web/index.html @@ -253,14 +253,14 @@