From 07988e7e2001871eeb170b28d49af2bc67974ad0 Mon Sep 17 00:00:00 2001 From: Akhmad Badalov Date: Wed, 4 Mar 2026 17:22:42 +0500 Subject: [PATCH 01/22] feat: cli first task --- package-lock.json | 17 +++++++++++++++++ src/cli/interactive.js | 42 ++++++++++++++++++++++++++++++++++++++---- 2 files changed, 55 insertions(+), 4 deletions(-) create mode 100644 package-lock.json diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..755c365b --- /dev/null +++ b/package-lock.json @@ -0,0 +1,17 @@ +{ + "name": "node-nodejs-fundamentals", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "node-nodejs-fundamentals", + "version": "1.0.0", + "license": "ISC", + "engines": { + "node": ">=24.10.0", + "npm": ">=10.9.2" + } + } + } +} diff --git a/src/cli/interactive.js b/src/cli/interactive.js index d0e3e0d9..f3a6af83 100644 --- a/src/cli/interactive.js +++ b/src/cli/interactive.js @@ -1,8 +1,42 @@ +import { createInterface } from 'node:readline/promises'; +import { stdin, stdout } from 'node:process'; + const interactive = () => { - // Write your code here - // Use readline module for interactive CLI - // Support commands: uptime, cwd, date, exit - // Handle Ctrl+C and unknown commands + const rl = createInterface({ + input: stdin, + output: stdout, + }); + + rl.setPrompt('> '); + rl.prompt(); + + rl.on('line', (cmd) => { + switch(cmd) { + case 'uptime': + const processUptime = process.uptime(); + console.log(`Uptime: ${processUptime}`); + break; + case 'cwd': + const currDir = process.cwd(); + console.log(currDir.split('/')[currDir.split('/').length-1]); + break; + case 'date': + const currentDate = new Date(); + const ISOstring = currentDate.toISOString(); + console.log(ISOstring); + break; + case 'exit': + console.log('Goodbye!'); + process.exit(0); + default: + console.log('Unknown command'); + } + }) + + rl.on('SIGINT', () => { + console.log('Goodbye!'); + rl.close(); + }); }; interactive(); From deeb7983908309dd4f63614e56c0c25628821520 Mon Sep 17 00:00:00 2001 From: Akhmad Badalov Date: Wed, 4 Mar 2026 23:43:16 +0500 Subject: [PATCH 02/22] feat: cli 2 task --- src/cli/progress.js | 79 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 75 insertions(+), 4 deletions(-) diff --git a/src/cli/progress.js b/src/cli/progress.js index 3e060763..35b5caf7 100644 --- a/src/cli/progress.js +++ b/src/cli/progress.js @@ -1,8 +1,79 @@ +import { parseArgs } from 'node:util'; + const progress = () => { - // Write your code here - // Simulate progress bar from 0% to 100% over ~5 seconds - // Update in place using \r every 100ms - // Format: [████████████████████ ] 67% + const moderatedArgv = process.argv.slice(2); + const { values } = parseArgs({ + moderatedArgv, + options: { + duration: { + type: 'string', + default: '5000', + }, + interval: { + type: 'string', + default: '100', + }, + length: { + type: 'string', + default: '30', + }, + color: { + type: 'string', + default: '#FFFFFF', + } + } + }) + validateValueType(values); + + const fullWidth = parseInt(values.length); + const interv = parseInt(values.interval); + const steps = parseInt(values.duration) / parseInt(values.interval); + let currentStep = 0; + + const progressFill = setInterval(() => { + currentStep++; + const progress = currentStep / steps; + const percent = Math.round(progress * 100); + const filledCnt = Math.round(fullWidth * progress); + + const asni = hexToAsnii(values.color); + const filled = asni + '█'.repeat(filledCnt) + '\x1b[0m'; + const empty = " ".repeat(fullWidth - filledCnt); + process.stdout.write(`\r[${filled}${empty}] ${percent}%`); + + if (currentStep >= steps) { + clearInterval(progressFill); + process.stdout.write('\nDone!\n'); + } + }, interv); }; progress(); + +function validateValueType(argsKeyValueForm) { + + if (isNaN(argsKeyValueForm.duration)) { + throw new Error('No valid duration value!') + } + + if (isNaN(argsKeyValueForm.interval)) { + throw new Error('No valid interval value!') + } + + if (isNaN(argsKeyValueForm.length)) { + throw new Error('No valid length value!') + } + + const regHex = /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i + + if (!regHex.test(argsKeyValueForm.color)) { + argsKeyValueForm.color = '#FFFFFF'; + } +} + +function hexToAsnii(hexString) { + const red = parseInt(hexString.slice(1, 3), 16); + const green = parseInt(hexString.slice(3, 5), 16); + const blue = parseInt(hexString.slice(5), 16); + return `\x1b[38;2;${red};${green};${blue}m`; +} \ No newline at end of file From 25d84e93db03b1ddd713a0ad988a3fe5315a7a9e Mon Sep 17 00:00:00 2001 From: Akhmad Badalov Date: Thu, 5 Mar 2026 02:27:24 +0500 Subject: [PATCH 03/22] feat: exec cmd --- src/cp/execCommand.js | 43 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/src/cp/execCommand.js b/src/cp/execCommand.js index 34a89c8d..e89b0b54 100644 --- a/src/cp/execCommand.js +++ b/src/cp/execCommand.js @@ -1,10 +1,41 @@ +import { spawn } from 'child_process'; + const execCommand = () => { - // Write your code here - // Take command from CLI argument - // Spawn child process - // Pipe child stdout/stderr to parent stdout/stderr - // Pass environment variables - // Exit with same code as child + const arg = process.argv.slice(2); + + const cmdAndArgs = arg[0].split(" "); + console.log(cmdAndArgs, 'sdsd') + const [childCMD, ...childCMDComment] = cmdAndArgs; + let cmd; + + console.log(childCMDComment); + if (childCMDComment.length) { + cmd = spawn(childCMD, [...childCMDComment], { + stdio: 'inherit', + env: process.env, + }); + } + + cmd = spawn(childCMD); + + cmd.stdout.on('data', (data) => { + process.stdout.write(`${data} from child`); + }); + + cmd.stderr.on('data', (data) => { + console.log(data, 'p') + process.stderr.write(`Error look: ${data}`); + }); + + cmd.on('exit', (code, signal) => { + if (code !== null) { + process.exit(code); + } else { + cmd.close('close', (code) => { + process.exit(code); + }) + } + }); }; execCommand(); From 11c6df8bdd506f78c86ade2861b2cfdf2b57f012 Mon Sep 17 00:00:00 2001 From: Akhmad Badalov Date: Thu, 5 Mar 2026 04:34:54 +0500 Subject: [PATCH 04/22] fs 1task --- package.json | 2 +- src/fs/findByExt.js | 80 ++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 76 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index dfecb12a..0d24a00e 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "scripts": { "fs:snapshot": "node src/fs/snapshot.js", "fs:restore": "node src/fs/restore.js", - "fs:findByExt": "node src/fs/findByExt.js --ext txt", + "fs:findByExt": "node src/fs/findByExt.js --ext js", "fs:merge": "node src/fs/merge.js", "cli:interactive": "node src/cli/interactive.js", "cli:progress": "node src/cli/progress.js", diff --git a/src/fs/findByExt.js b/src/fs/findByExt.js index 24f06cb8..869ac722 100644 --- a/src/fs/findByExt.js +++ b/src/fs/findByExt.js @@ -1,7 +1,77 @@ -const findByExt = async () => { - // Write your code here - // Recursively find all files with specific extension - // Parse --ext CLI argument (default: .txt) +import { parseArgs } from 'node:util'; +import path from 'path'; +import fs from 'fs'; +const extFromCLI = process.argv.slice(2); +const { values } = parseArgs({ + extFromCLI, + options: { + ext: { + type: 'string', + default: 'text' + } + } +}); +let deepestPath = ''; + + +const findByExt = async (paths, fileArr) => { + const dir = paths ? paths : '.'; + isWorksapceExisted(dir); + const fileList = fileArr ? fileArr : []; + + try { + const files = fs.readdirSync(dir, { withFileTypes: true }); + + if (!files && !files.length) { + throw new Error('Not found!') + } + + for (const file of files) { + const fullPath = file.parentPath + '/' + file.name; + + const stat = fs.statSync(fullPath); + if (stat.isDirectory()) { + await findByExt(fullPath, fileList); + } else if (stat.isFile() + && path.basename(fullPath).split('.')[path.basename(fullPath).split('.').length - 1] === values.ext) { + fileList.push(fullPath); + deepestPath = deepestPath.split('/').length > fullPath.split('/') ? deepestPath : fullPath; + } + } + } catch (error) { + console.error('Error: ', error); + } + + return fileList; }; -await findByExt(); +const listArr = await findByExt(); + +for (const path of listArr) { + const paths = relativePaths(path); + paths.forEach((el) => { + console.log(`\n${el}`) + }) +} + +async function isWorksapceExisted(path) { + const errorMsg = 'Worspace does not exist!'; + try { + fs.accessSync(path); + } catch (error) { + if (error.code === 'ENOENT') { + console.error('Error: ', errorMsg); + } + console.error('Error: ', error); + } +} + +function relativePaths(path) { + const deepestPathSet = new Set(); + let pathStepByStep = ''; + for (let i = 0; i < path.split('/').length - 1; i++) { + pathStepByStep = pathStepByStep + deepestPath.split('/')[i] + '/'; + deepestPathSet.add(pathStepByStep) + } + return deepestPathSet; +} \ No newline at end of file From 857ea098fd515994f013b173e361569255c6c1da Mon Sep 17 00:00:00 2001 From: Akhmad Badalov Date: Thu, 5 Mar 2026 05:05:11 +0500 Subject: [PATCH 05/22] feat: fs 2 task --- package.json | 2 +- src/fs/merge.js | 50 +++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 47 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 0d24a00e..dfecb12a 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "scripts": { "fs:snapshot": "node src/fs/snapshot.js", "fs:restore": "node src/fs/restore.js", - "fs:findByExt": "node src/fs/findByExt.js --ext js", + "fs:findByExt": "node src/fs/findByExt.js --ext txt", "fs:merge": "node src/fs/merge.js", "cli:interactive": "node src/cli/interactive.js", "cli:progress": "node src/cli/progress.js", diff --git a/src/fs/merge.js b/src/fs/merge.js index cb8e0d8f..e115f9ae 100644 --- a/src/fs/merge.js +++ b/src/fs/merge.js @@ -1,8 +1,50 @@ +import path from 'path'; +import fs from 'fs'; + const merge = async () => { - // Write your code here - // Default: read all .txt files from workspace/parts in alphabetical order - // Optional: support --files filename1,filename2,... to merge specific files in provided order - // Concatenate content and write to workspace/merged.txt + const dir = './src/cp'; + const argvs = process.argv.slice(2); + let listFiles = []; + + try { + if (argvs[0] === '--files' && argvs[1]) { + listFiles = process.argv[1].split(','); + + for (const file of listFiles) { + console.log(dir, file.name) + fs.accessSync(path.join(dir, file.name)); + } + } else { + const files = fs.readdirSync(dir, { withFileTypes: true }); + + listFiles = files + .filter((file) => { + const fullPath = file.parentPath + '/' + file.name; + const stat = fs.statSync(fullPath); + console.log(fullPath) + if (stat.isFile() + && path.basename(fullPath).split('.')[path.basename(fullPath).split('.').length - 1] === 'js') { + return file; + } + }).sort(); + + if (listFiles.length === 0) { + console.error('No text file found!'); + } + } + + for (const file of listFiles) { + const content = fs.readFileSync(path.join(dir, file.name), 'utf-8'); + process.stdout.write(content + '\n'); + } + } catch (error) { + console.error('Error:', error); + } }; await merge(); + +processFiles().catch(err => { + console.error(err.message); + process.exit(1); +}); \ No newline at end of file From cab3daa66d8a3da9864c9f6d219fb5b531373d64 Mon Sep 17 00:00:00 2001 From: Akhmad Badalov Date: Thu, 5 Mar 2026 07:47:36 +0500 Subject: [PATCH 06/22] feat: snapshot --- src/fs/findByExt.js | 6 ++-- src/fs/restore.js | 1 + src/fs/snapshot.js | 72 +++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 70 insertions(+), 9 deletions(-) diff --git a/src/fs/findByExt.js b/src/fs/findByExt.js index 869ac722..77467d14 100644 --- a/src/fs/findByExt.js +++ b/src/fs/findByExt.js @@ -22,11 +22,11 @@ const findByExt = async (paths, fileArr) => { try { const files = fs.readdirSync(dir, { withFileTypes: true }); - if (!files && !files.length) { - throw new Error('Not found!') + if (!files || !files.length) { + throw new Error('File not found!') } - for (const file of files) { + for await (const file of files) { const fullPath = file.parentPath + '/' + file.name; const stat = fs.statSync(fullPath); diff --git a/src/fs/restore.js b/src/fs/restore.js index 96ae1ffb..2217d0e1 100644 --- a/src/fs/restore.js +++ b/src/fs/restore.js @@ -1,4 +1,5 @@ const restore = async () => { + // Write your code here // Read snapshot.json // Treat snapshot.rootPath as metadata only diff --git a/src/fs/snapshot.js b/src/fs/snapshot.js index 050103d3..99e4011e 100644 --- a/src/fs/snapshot.js +++ b/src/fs/snapshot.js @@ -1,9 +1,69 @@ -const snapshot = async () => { - // Write your code here - // Recursively scan workspace directory - // Write snapshot.json with: - // - rootPath: absolute path to workspace - // - entries: flat array of relative paths and metadata +import fs, { constants } from 'node:fs/promises'; + const readableFileContent = { + rootPath: process.cwd(), + etnries: [] + }; + +const snapshot = async (paths) => { + const dir = paths ? paths : process.cwd(); + isWorksapceExisted(dir); + + try { + const files = await fs.readdir(dir, { withFileTypes: true }); + + for await (const file of files) { + const fullPath = dir + '/' + file.name; + + if (file.isDirectory()) { + await snapshot(fullPath); + readableFileContent.etnries.push({ + path: fullPath, + type: 'directory', + }); + } else { + await fs.access(fullPath, constants.R_OK); + const content = (await fs.readFile(fullPath)).toString('base64'); + const size = (await fs.stat(fullPath)).size; + readableFileContent.etnries.push({ + path: fullPath, + type: 'file', + size: size, + content: content + }); + } + + fs.writeFile('./snapshot.json', JSON.stringify(readableFileContent, null, 2), 'utf8', (err) => { + if (err) { + console.error('Error: ', err); + } else { + console.log('Successfully completed!'); + } + }) + } + } catch (error) { + if (error === 'ENOENT') { + console.error('Error: ', "No such file exist!"); + } else if (error === 'EACCES') { + console.error('Error: ', "Access Denied!") + } else { + console.error('Error: ', error) + } + } }; await snapshot(); + + +async function isWorksapceExisted(path) { + const errorMsg = 'Worspace does not exist!'; + try { + await fs.access(path, constants.R_OK); + } catch (error) { + if (error.code === 'ENOENT') { + console.error('Error: ', errorMsg); + } else if (error.code === 'EACCES') { + console.error('Error: ', 'Access Denied!'); + } + console.error('Error: ', error); + } +} \ No newline at end of file From a313d285d8cb641b647aa6fee24391d065835e3c Mon Sep 17 00:00:00 2001 From: Akhmad Badalov Date: Thu, 5 Mar 2026 08:41:25 +0500 Subject: [PATCH 07/22] feat: 4 task fs --- src/fs/restore.js | 55 +++++++++++++++++++++++++++++++++++++++++----- src/fs/snapshot.js | 8 +++---- 2 files changed, 54 insertions(+), 9 deletions(-) diff --git a/src/fs/restore.js b/src/fs/restore.js index 2217d0e1..a255f2b0 100644 --- a/src/fs/restore.js +++ b/src/fs/restore.js @@ -1,9 +1,54 @@ +import fs, { constants } from 'node:fs/promises'; + const restore = async () => { - - // Write your code here - // Read snapshot.json - // Treat snapshot.rootPath as metadata only - // Recreate directory/file structure in workspace_restored + const dir = process.cwd(); + const jsonDir = dir + '/snapshot.json'; + isJSONEXist(jsonDir); + + try { + const data = await fs.readFile('./snapshot.json', 'utf8'); + const content = JSON.parse(data); + + for (const entry of content.entries) { + let pathFromJSON = content.rootPath; + + pathFromJSON = pathFromJSON + '/' + entry.path; + + if (entry.type === 'directory') { + await fs.mkdir(pathFromJSON); + } else if (entry.type === 'file') { + const base64String = entry.content; + const buffer = Buffer.from(base64String, 'base64'); + await fs.writeFile(pathFromJSON, buffer); + + } + } + + } catch (error) { + if (error === 'ENOENT') { + console.error('Error: ', "No such file exist!"); + } else if (error === 'EACCES') { + console.error('Error: ', "Access Denied!") + } else { + console.error('Error: ', error) + } + } }; await restore(); + + +async function isJSONEXist(path) { + console.log(path) + const errorMsg = 'FS operation failed'; + try { + await fs.access(path, constants.R_OK); + } catch (error) { + if (error.code === 'ENOENT') { + console.error('Error: ', errorMsg); + } else if (error.code === 'EACCES') { + console.error('Error: ', 'Access Denied!'); + } + console.error('Error: ', error); + } +} \ No newline at end of file diff --git a/src/fs/snapshot.js b/src/fs/snapshot.js index 99e4011e..f0a56695 100644 --- a/src/fs/snapshot.js +++ b/src/fs/snapshot.js @@ -17,7 +17,7 @@ const snapshot = async (paths) => { if (file.isDirectory()) { await snapshot(fullPath); readableFileContent.etnries.push({ - path: fullPath, + path: dir.split(process.cwd() + '/')[1], type: 'directory', }); } else { @@ -25,13 +25,13 @@ const snapshot = async (paths) => { const content = (await fs.readFile(fullPath)).toString('base64'); const size = (await fs.stat(fullPath)).size; readableFileContent.etnries.push({ - path: fullPath, + path: dir.split(process.cwd() + '/')[1] + file.name, type: 'file', size: size, content: content }); } - + fs.writeFile('./snapshot.json', JSON.stringify(readableFileContent, null, 2), 'utf8', (err) => { if (err) { console.error('Error: ', err); @@ -55,7 +55,7 @@ await snapshot(); async function isWorksapceExisted(path) { - const errorMsg = 'Worspace does not exist!'; + const errorMsg = 'FS operation failed'; try { await fs.access(path, constants.R_OK); } catch (error) { From 70370295b74b21332605242e2265aa600d59ea2c Mon Sep 17 00:00:00 2001 From: Akhmad Badalov Date: Thu, 5 Mar 2026 20:56:05 +0500 Subject: [PATCH 08/22] feat: module dynamic --- src/modules/dynamic.js | 45 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 40 insertions(+), 5 deletions(-) diff --git a/src/modules/dynamic.js b/src/modules/dynamic.js index 008ca387..6392aa20 100644 --- a/src/modules/dynamic.js +++ b/src/modules/dynamic.js @@ -1,9 +1,44 @@ +import fs from 'node:fs/promises'; + +const plugins = ['uppercase', 'reverse', 'repeat']; + const dynamic = async () => { - // Write your code here - // Accept plugin name as CLI argument - // Dynamically import plugin from plugins/ directory - // Call run() function and print result - // Handle missing plugin case + const dir = process.cwd() + '/src/modules'; + const argv = process.argv.slice(2); + if (!plugins.includes(argv[0])) { + console.error('Plugin not found!'); + process.exit(1); + } + let module; + let result; + + try { + await fs.access(`${dir}/plugins`); + const arrModulePromises = plugins.map(async (el) => { + await fs.access(`${dir}/plugins/${el}.js`); + return `./plugins/${el}.js`; + }); + + const arrModules = await Promise.all(arrModulePromises); + + for (const mod of arrModules) { + if (mod === `./plugins/${argv[0]}.js`) { + module = await import(mod); + break; + } + } + result = module.run(); + } catch (error) { + if (error === 'ENOENT') { + console.error('Error: ', "No such file exist!"); + } else if (error === 'EACCES') { + console.error('Error: ', "Access Denied!") + } else { + console.error('Error: ', error) + } + } finally { + process.stdin.write(result); + } }; await dynamic(); From 7b0ff88720598999647691590df04159255402be Mon Sep 17 00:00:00 2001 From: Akhmad Badalov Date: Fri, 6 Mar 2026 03:32:59 +0500 Subject: [PATCH 09/22] feat: hash --- file1.txt | 1 + file2.txt | 0 src/fs/restore.js | 1 - src/hash/verify.js | 45 ++++++++++++++++++++++++++++++++++++++++----- src/utils/util.js | 16 ++++++++++++++++ 5 files changed, 57 insertions(+), 6 deletions(-) create mode 100644 file1.txt create mode 100644 file2.txt create mode 100644 src/utils/util.js diff --git a/file1.txt b/file1.txt new file mode 100644 index 00000000..b6fc4c62 --- /dev/null +++ b/file1.txt @@ -0,0 +1 @@ +hello \ No newline at end of file diff --git a/file2.txt b/file2.txt new file mode 100644 index 00000000..e69de29b diff --git a/src/fs/restore.js b/src/fs/restore.js index a255f2b0..da782168 100644 --- a/src/fs/restore.js +++ b/src/fs/restore.js @@ -39,7 +39,6 @@ await restore(); async function isJSONEXist(path) { - console.log(path) const errorMsg = 'FS operation failed'; try { await fs.access(path, constants.R_OK); diff --git a/src/hash/verify.js b/src/hash/verify.js index 7f1e8961..2927cdb2 100644 --- a/src/hash/verify.js +++ b/src/hash/verify.js @@ -1,8 +1,43 @@ +import fs from 'node:fs/promises'; +import { pipeline } from 'node:stream/promises'; +import { createReadStream } from 'node:fs'; +import { createHash } from 'node:crypto'; +import { isFileOrDirectoryExist } from '../utils/util.js'; + const verify = async () => { - // Write your code here - // Read checksums.json - // Calculate SHA256 hash using Streams API - // Print result: filename — OK/FAIL + const dir = process.cwd(); + const pathToFile = `${dir}/checksums.json`; + isFileOrDirectoryExist(pathToFile); + let content; + + const data = await fs.readFile('./checksums.json', 'utf8'); + content = JSON.parse(data); + + for (const [name, expectedHash] of Object.entries(content)) { + try { + await fs.access(`${dir}/${name}`); + } catch (error) { + console.error(error) + } + + try { + const createdHash = createHash('sha256'); + + const readStream = createReadStream(`${dir}/${name}`); + + await pipeline(readStream, createdHash); + const hashed = createdHash.digest('hex'); + + const isOK = hashed === expectedHash; + console.log(`${name} - ${isOK}`); + } catch (error) { + console.error(error) + } + } }; -await verify(); +try { + await verify(); +} catch (error) { + console.error(error) +} diff --git a/src/utils/util.js b/src/utils/util.js new file mode 100644 index 00000000..7260aaae --- /dev/null +++ b/src/utils/util.js @@ -0,0 +1,16 @@ +import fs, { constants } from 'node:fs/promises'; + +export const isFileOrDirectoryExist = async (path) => { + const errorMsg = 'FS operation failed'; + + try { + await fs.access(path, constants.R_OK); + } catch (error) { + if (error.code === 'ENOENT') { + console.error(errorMsg); + } else if (error.code === 'EACCES') { + console.error('Error: ', 'Access Denied!'); + } + console.error('Error: ', error); + } +} \ No newline at end of file From 6d6e1d92baf3633bf17303b44a91985113318981 Mon Sep 17 00:00:00 2001 From: Akhmad Badalov Date: Fri, 6 Mar 2026 03:33:27 +0500 Subject: [PATCH 10/22] fix: files --- file1.txt | 1 - file2.txt | 0 2 files changed, 1 deletion(-) delete mode 100644 file1.txt delete mode 100644 file2.txt diff --git a/file1.txt b/file1.txt deleted file mode 100644 index b6fc4c62..00000000 --- a/file1.txt +++ /dev/null @@ -1 +0,0 @@ -hello \ No newline at end of file diff --git a/file2.txt b/file2.txt deleted file mode 100644 index e69de29b..00000000 From 65071c3e33ad500f7d266dd9fd0c979907a4ac2d Mon Sep 17 00:00:00 2001 From: Akhmad Badalov Date: Fri, 6 Mar 2026 03:58:59 +0500 Subject: [PATCH 11/22] fix: correcting paths --- src/fs/findByExt.js | 2 +- src/fs/merge.js | 3 +++ src/fs/restore.js | 10 +++++++++- src/fs/snapshot.js | 15 ++++++++------- 4 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/fs/findByExt.js b/src/fs/findByExt.js index 77467d14..55ae7f18 100644 --- a/src/fs/findByExt.js +++ b/src/fs/findByExt.js @@ -15,7 +15,7 @@ let deepestPath = ''; const findByExt = async (paths, fileArr) => { - const dir = paths ? paths : '.'; + const dir = paths ? paths : './workspace'; isWorksapceExisted(dir); const fileList = fileArr ? fileArr : []; diff --git a/src/fs/merge.js b/src/fs/merge.js index e115f9ae..272775b3 100644 --- a/src/fs/merge.js +++ b/src/fs/merge.js @@ -1,6 +1,8 @@ import path from 'path'; import fs from 'fs'; +// need to redo + const merge = async () => { const dir = './src/cp'; const argvs = process.argv.slice(2); @@ -36,6 +38,7 @@ const merge = async () => { for (const file of listFiles) { const content = fs.readFileSync(path.join(dir, file.name), 'utf-8'); process.stdout.write(content + '\n'); + } } catch (error) { console.error('Error:', error); diff --git a/src/fs/restore.js b/src/fs/restore.js index da782168..2564292d 100644 --- a/src/fs/restore.js +++ b/src/fs/restore.js @@ -5,6 +5,14 @@ const restore = async () => { const jsonDir = dir + '/snapshot.json'; isJSONEXist(jsonDir); + try { + await fs.mkdir(dir + '/workspace_restored'); + } catch (error) { + if (error === 'ENOENT') { + console.error('FS operation failed'); + } + } + try { const data = await fs.readFile('./snapshot.json', 'utf8'); const content = JSON.parse(data); @@ -12,7 +20,7 @@ const restore = async () => { for (const entry of content.entries) { let pathFromJSON = content.rootPath; - pathFromJSON = pathFromJSON + '/' + entry.path; + pathFromJSON = pathFromJSON.split('/workspace')[0] +'/workspace_restored/' + entry.path; if (entry.type === 'directory') { await fs.mkdir(pathFromJSON); diff --git a/src/fs/snapshot.js b/src/fs/snapshot.js index f0a56695..bfecbcc2 100644 --- a/src/fs/snapshot.js +++ b/src/fs/snapshot.js @@ -1,11 +1,12 @@ import fs, { constants } from 'node:fs/promises'; +const rootPath = process.cwd() + '/workspace'; const readableFileContent = { - rootPath: process.cwd(), - etnries: [] + rootPath: rootPath, + entries: [] }; const snapshot = async (paths) => { - const dir = paths ? paths : process.cwd(); + const dir = paths ? paths : rootPath; isWorksapceExisted(dir); try { @@ -16,16 +17,16 @@ const snapshot = async (paths) => { if (file.isDirectory()) { await snapshot(fullPath); - readableFileContent.etnries.push({ - path: dir.split(process.cwd() + '/')[1], + readableFileContent.entries.push({ + path: dir.split(process.cwd() + '/workspace')[1], type: 'directory', }); } else { await fs.access(fullPath, constants.R_OK); const content = (await fs.readFile(fullPath)).toString('base64'); const size = (await fs.stat(fullPath)).size; - readableFileContent.etnries.push({ - path: dir.split(process.cwd() + '/')[1] + file.name, + readableFileContent.entries.push({ + path: dir.split(process.cwd() + '/workspace')[1] + file.name, type: 'file', size: size, content: content From 08dd83c5c1459dd2db800de791853ea3c5346d5b Mon Sep 17 00:00:00 2001 From: Akhmad Badalov Date: Fri, 6 Mar 2026 07:05:29 +0500 Subject: [PATCH 12/22] feat: linenumberer stream --- src/streams/customized/transformStream.js | 31 +++++++++++++++++++++++ src/streams/filter.js | 1 + src/streams/lineNumberer.js | 11 +++++--- 3 files changed, 39 insertions(+), 4 deletions(-) create mode 100644 src/streams/customized/transformStream.js diff --git a/src/streams/customized/transformStream.js b/src/streams/customized/transformStream.js new file mode 100644 index 00000000..8989670b --- /dev/null +++ b/src/streams/customized/transformStream.js @@ -0,0 +1,31 @@ +import { Transform } from 'node:stream'; + +export class StartLineWithNumberTranform extends Transform { + constructor(options) { + super(options); + this.lineNumber = 1; + this.lineBuffer = ''; + } + + _transform(chunk, encoding, callback) { + this.lineBuffer += chunk.toString(); + const lines = this.lineBuffer.split('\n'); + + this.lineBuffer = lines.pop(); + + for (const line of lines) { + this.push(`${this.lineNumber} | ${line}\n`); + this.lineNumber++; + } + + callback(); + } + + _flush(callback) { + if (this.lineBuffer.length > 0) { + this.push(`${this.lineNumber++} | ${this.lineBuffer}\n`); + } + + callback(); + } +} \ No newline at end of file diff --git a/src/streams/filter.js b/src/streams/filter.js index 3868ab46..ac82bc07 100644 --- a/src/streams/filter.js +++ b/src/streams/filter.js @@ -1,4 +1,5 @@ const filter = () => { + // Write your code here // Read from process.stdin // Filter lines by --pattern CLI argument diff --git a/src/streams/lineNumberer.js b/src/streams/lineNumberer.js index 579d662e..74c47309 100644 --- a/src/streams/lineNumberer.js +++ b/src/streams/lineNumberer.js @@ -1,8 +1,11 @@ +import { StartLineWithNumberTranform } from './customized/transformStream.js'; + const lineNumberer = () => { - // Write your code here - // Read from process.stdin - // Use Transform Stream to prepend line numbers - // Write to process.stdout + const customLine = new StartLineWithNumberTranform(); + + process.stdin + .pipe(customLine) + .pipe(process.stdout); }; lineNumberer(); From 2b9a00d228a42b8484ea52b280d457c4fe9e3d03 Mon Sep 17 00:00:00 2001 From: Akhmad Badalov Date: Fri, 6 Mar 2026 07:44:27 +0500 Subject: [PATCH 13/22] feat: filter task stream --- .../customized/filterTranformStream.js | 41 +++++++++++++++++++ ...formStream.js => numberTransformStream.js} | 0 src/streams/filter.js | 14 +++---- 3 files changed, 48 insertions(+), 7 deletions(-) create mode 100644 src/streams/customized/filterTranformStream.js rename src/streams/customized/{transformStream.js => numberTransformStream.js} (100%) diff --git a/src/streams/customized/filterTranformStream.js b/src/streams/customized/filterTranformStream.js new file mode 100644 index 00000000..61f6b962 --- /dev/null +++ b/src/streams/customized/filterTranformStream.js @@ -0,0 +1,41 @@ +import { Transform } from 'node:stream'; +import { parseArgs } from 'node:util'; +const patternArgv = process.argv.slice(2); +const { values } = parseArgs({ + patternArgv, + options: { + pattern: { + type: 'string' + } + } +}); + +export class FilterByArgumentTranform extends Transform { + constructor(options) { + super(options); + this.buffer = ''; + } + + _transform(chunk, encoding, callback) { + this.buffer += chunk; + const lines = this.buffer.split('\n'); + + this.buffer = lines.pop(); + + for (const line of lines) { + if (line.includes(values.pattern)) { + this.push(`${line}\n`); + } + } + + callback(); + } + + _flush(callback) { + if (this.buffer.length > 0 && this.buffer.includes(values.pattern)) { + this.push(`${this.buffer}\n`); + } + + callback(); + } +} \ No newline at end of file diff --git a/src/streams/customized/transformStream.js b/src/streams/customized/numberTransformStream.js similarity index 100% rename from src/streams/customized/transformStream.js rename to src/streams/customized/numberTransformStream.js diff --git a/src/streams/filter.js b/src/streams/filter.js index ac82bc07..f1a5addb 100644 --- a/src/streams/filter.js +++ b/src/streams/filter.js @@ -1,10 +1,10 @@ +import { FilterByArgumentTranform } from './customized/filterTranformStream.js'; + const filter = () => { - - // Write your code here - // Read from process.stdin - // Filter lines by --pattern CLI argument - // Use Transform Stream - // Write to process.stdout + const filterTransform = new FilterByArgumentTranform(); + process.stdin + .pipe(filterTransform) + .pipe(process.stdout); }; -filter(); +filter(); \ No newline at end of file From 79c98564b66d7f7e03d633b9be3b489572d8c104 Mon Sep 17 00:00:00 2001 From: Akhmad Badalov Date: Fri, 6 Mar 2026 08:36:17 +0500 Subject: [PATCH 14/22] feat: last task stream --- src/streams/split.js | 48 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/src/streams/split.js b/src/streams/split.js index f8f814fa..38ff42b9 100644 --- a/src/streams/split.js +++ b/src/streams/split.js @@ -1,8 +1,48 @@ +import { parseArgs } from 'node:util'; +import readline from 'readline'; +import fs from 'node:fs'; +const splitArgv = process.argv.slice(2); +const { values } = parseArgs({ + splitArgv, + options: { + lines: { + type: 'string', + default: '10', + } + } +}); + const split = async () => { - // Write your code here - // Read source.txt using Readable Stream - // Split into chunk_1.txt, chunk_2.txt, etc. - // Each chunk max N lines (--lines CLI argument, default: 10) + const dir = process.cwd() + '/source.txt'; + try { + // Readable Stream, I didnot get that is it okay fs.createReadStream or I need to use from Web Stream + const readableStream = fs.createReadStream(dir, { encoding: 'utf8' }); + + const rl = readline.createInterface({ + input: readableStream, + crlfDelay: Infinity + }); + + let fileCnt = 1; + let lineCnt = 0; + let writebleStream = null; + + for await (const line of rl) { + if (lineCnt % parseInt(values.lines) === 0) { + if (writebleStream) { + writebleStream.end(); + } + writebleStream = fs.createWriteStream(`chunk_${fileCnt++}.txt`); + } + + writebleStream.write(`${line}\n`); + lineCnt++; + } + + if (writebleStream) writebleStream.end(); + } catch (error) { + console.error(error); + } }; await split(); From a4e7c78ad14ca829d3cb79c2175689c353d79c0b Mon Sep 17 00:00:00 2001 From: Akhmad Badalov Date: Fri, 6 Mar 2026 23:28:19 +0500 Subject: [PATCH 15/22] feat: worker threads --- src/utils/util.js | 30 ++++++++++++++++++++++++++- src/wt/main.js | 53 ++++++++++++++++++++++++++++++++++++++++------- src/wt/worker.js | 13 ++++++------ 3 files changed, 81 insertions(+), 15 deletions(-) diff --git a/src/utils/util.js b/src/utils/util.js index 7260aaae..543f9f91 100644 --- a/src/utils/util.js +++ b/src/utils/util.js @@ -13,4 +13,32 @@ export const isFileOrDirectoryExist = async (path) => { } console.error('Error: ', error); } -} \ No newline at end of file +} + +export const splitArrayByNumberOfCores = (arr, numberOfCores) => { + let result = []; + + const chunkSize = Math.ceil(arr.length / numberOfCores); + + for (let i = 0; i < arr.length; i += chunkSize) { + result.push(arr.slice(i, i + chunkSize)); + } + + return result; +} + +export const keyMerge = (sortedArr) => { + let result = []; + + result = sortedArr.reduce((merged, current) => { + let i = 0, j = 0; + const combined = []; + while (i < merged.length && j < current.length) { + if (merged[i] < current[j]) combined.push(merged[i++]); + else combined.push(current[j++]); + } + return [...combined, ...merged.slice(i), ...current.slice(j)]; + }, []); + + return result; +} \ No newline at end of file diff --git a/src/wt/main.js b/src/wt/main.js index d7d21f0c..6f701781 100644 --- a/src/wt/main.js +++ b/src/wt/main.js @@ -1,11 +1,48 @@ +import fs from 'node:fs/promises'; +import os from 'node:os'; +import { Worker } from 'node:worker_threads'; +import { splitArrayByNumberOfCores, keyMerge } from '../utils/util.js'; + const main = async () => { - // Write your code here - // Read data.json containing array of numbers - // Split into N chunks (N = CPU cores) - // Create N workers, send one chunk to each - // Collect sorted chunks - // Merge using k-way merge algorithm - // Log final sorted array + const jsonFilepath = './data.json'; + const workerPath = process.cwd() + '/src/wt/worker.js'; + + try { + await fs.access(jsonFilepath); + + const content = await fs.readFile(jsonFilepath, { encoding: 'utf8' }); + const data = JSON.parse(content); + const numberOfCores = os.cpus().length; + const chunks = splitArrayByNumberOfCores(data, numberOfCores); + + const workerPromises = chunks.map((chunk) => { + return new Promise((resolve, reject) => { + const worker = new Worker(workerPath); + + worker.postMessage({ data: chunk }); + + worker.on('message', (data) => { + resolve(data); + worker.terminate(); + }); + + worker.on('error', reject); + worker.on('exit', (code) => { + if (code !== 0) { + reject(new Error('Worker Stopped!')); + } + }); + }); + }); + + const sortedArr = await Promise.all(workerPromises); + + const result = keyMerge(sortedArr); + console.log(result); + } catch (error) { + console.error(error); + } finally { + } }; -await main(); +await main(); \ No newline at end of file diff --git a/src/wt/worker.js b/src/wt/worker.js index 15f42fc8..61109c0b 100644 --- a/src/wt/worker.js +++ b/src/wt/worker.js @@ -1,9 +1,10 @@ -import { parentPort } from 'worker_threads'; +import { parentPort } from 'node:worker_threads'; -// Receive array from main thread -// Sort in ascending order -// Send back to main thread +parentPort.on('message', (content) => { + if (!Array.isArray(content.data)) { + return; + } -parentPort.on('message', (data) => { - // Write your code here + const sortedArr = content.data.sort((a, b) => a - b); + parentPort.postMessage(sortedArr); }); From e9ae13d2312a04d93a0603399cf3bee4d2dbea40 Mon Sep 17 00:00:00 2001 From: Akhmad Badalov Date: Fri, 6 Mar 2026 23:55:25 +0500 Subject: [PATCH 16/22] fix: value not throw err --- src/cli/progress.js | 42 +++++++++--------------------------------- src/utils/util.js | 27 +++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 33 deletions(-) diff --git a/src/cli/progress.js b/src/cli/progress.js index 35b5caf7..645ad4ea 100644 --- a/src/cli/progress.js +++ b/src/cli/progress.js @@ -1,4 +1,5 @@ import { parseArgs } from 'node:util'; +import { hexToAsnii, validateValueType } from '../utils/util.js'; const progress = () => { const moderatedArgv = process.argv.slice(2); @@ -17,12 +18,15 @@ const progress = () => { type: 'string', default: '30', }, - color: { - type: 'string', - default: '#FFFFFF', - } - } + }, + strict: false, + allowPositionals: true }) + if (values.color) { + values.color = moderatedArgv[moderatedArgv.indexOf('--color') + 1]; + } + + console.log(values.color) validateValueType(values); const fullWidth = parseInt(values.length); @@ -49,31 +53,3 @@ const progress = () => { }; progress(); - -function validateValueType(argsKeyValueForm) { - - if (isNaN(argsKeyValueForm.duration)) { - throw new Error('No valid duration value!') - } - - if (isNaN(argsKeyValueForm.interval)) { - throw new Error('No valid interval value!') - } - - if (isNaN(argsKeyValueForm.length)) { - throw new Error('No valid length value!') - } - - const regHex = /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i - - if (!regHex.test(argsKeyValueForm.color)) { - argsKeyValueForm.color = '#FFFFFF'; - } -} - -function hexToAsnii(hexString) { - const red = parseInt(hexString.slice(1, 3), 16); - const green = parseInt(hexString.slice(3, 5), 16); - const blue = parseInt(hexString.slice(5), 16); - return `\x1b[38;2;${red};${green};${blue}m`; -} \ No newline at end of file diff --git a/src/utils/util.js b/src/utils/util.js index 543f9f91..3a0c4629 100644 --- a/src/utils/util.js +++ b/src/utils/util.js @@ -41,4 +41,31 @@ export const keyMerge = (sortedArr) => { }, []); return result; +} + +export const hexToAsnii = (hexString) => { + const red = parseInt(hexString.slice(1, 3), 16); + const green = parseInt(hexString.slice(3, 5), 16); + const blue = parseInt(hexString.slice(5), 16); + return `\x1b[38;2;${red};${green};${blue}m`; +} + +export const validateValueType = (argsKeyValueForm) => { + if (isNaN(argsKeyValueForm.duration)) { + process.stderr.write(new Error('No valid duration value!')) + } + + if (isNaN(argsKeyValueForm.interval)) { + process.stderr.write(new Error('No valid interval value!')); + } + + if (isNaN(argsKeyValueForm.length)) { + process.stderr.write(new Error('No valid length value!')); + } + + const regHex = /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i + + if (!regHex.test(argsKeyValueForm.color)) { + argsKeyValueForm.color = '#FFFFFF'; + } } \ No newline at end of file From aa271f4669be5179d8209a32fa526c4afabe58db Mon Sep 17 00:00:00 2001 From: Akhmad Badalov Date: Sat, 7 Mar 2026 00:03:06 +0500 Subject: [PATCH 17/22] fix: console result module --- src/modules/dynamic.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/dynamic.js b/src/modules/dynamic.js index 6392aa20..366fb13b 100644 --- a/src/modules/dynamic.js +++ b/src/modules/dynamic.js @@ -37,7 +37,7 @@ const dynamic = async () => { console.error('Error: ', error) } } finally { - process.stdin.write(result); + process.stdout.write(result+'\n'); } }; From f295b7e09b8983da12c657652444a8bd8059bc9f Mon Sep 17 00:00:00 2001 From: Akhmad Badalov Date: Sat, 7 Mar 2026 03:35:54 +0500 Subject: [PATCH 18/22] fix: error Error correct usage --- src/fs/findByExt.js | 8 ++++---- src/fs/merge.js | 4 ++-- src/fs/restore.js | 14 +++++++------- src/fs/snapshot.js | 12 ++++++------ src/modules/dynamic.js | 4 ++-- src/utils/util.js | 6 +++--- src/zip/compressDir.js | 17 +++++++++++++++++ 7 files changed, 41 insertions(+), 24 deletions(-) diff --git a/src/fs/findByExt.js b/src/fs/findByExt.js index 55ae7f18..63c9a24b 100644 --- a/src/fs/findByExt.js +++ b/src/fs/findByExt.js @@ -39,7 +39,7 @@ const findByExt = async (paths, fileArr) => { } } } catch (error) { - console.error('Error: ', error); + console.error(Error(error)); } return fileList; @@ -55,14 +55,14 @@ for (const path of listArr) { } async function isWorksapceExisted(path) { - const errorMsg = 'Worspace does not exist!'; + const errorMsg = 'FS operation failed'; try { fs.accessSync(path); } catch (error) { if (error.code === 'ENOENT') { - console.error('Error: ', errorMsg); + console.error(Error(errorMsg)); } - console.error('Error: ', error); + console.error(Error(error)); } } diff --git a/src/fs/merge.js b/src/fs/merge.js index 272775b3..95f5d5b0 100644 --- a/src/fs/merge.js +++ b/src/fs/merge.js @@ -31,7 +31,7 @@ const merge = async () => { }).sort(); if (listFiles.length === 0) { - console.error('No text file found!'); + console.error(Error('No text file found!')); } } @@ -41,7 +41,7 @@ const merge = async () => { } } catch (error) { - console.error('Error:', error); + console.error(Error(error)); } }; diff --git a/src/fs/restore.js b/src/fs/restore.js index 2564292d..f20e50f5 100644 --- a/src/fs/restore.js +++ b/src/fs/restore.js @@ -9,7 +9,7 @@ const restore = async () => { await fs.mkdir(dir + '/workspace_restored'); } catch (error) { if (error === 'ENOENT') { - console.error('FS operation failed'); + console.error(Error('FS operation failed')); } } @@ -34,11 +34,11 @@ const restore = async () => { } catch (error) { if (error === 'ENOENT') { - console.error('Error: ', "No such file exist!"); + console.error(Error('FS operation failed')); } else if (error === 'EACCES') { - console.error('Error: ', "Access Denied!") + console.error(Error("Access Denied!")); } else { - console.error('Error: ', error) + console.error(Error(error)) } } }; @@ -52,10 +52,10 @@ async function isJSONEXist(path) { await fs.access(path, constants.R_OK); } catch (error) { if (error.code === 'ENOENT') { - console.error('Error: ', errorMsg); + console.error(Error(errorMsg)); } else if (error.code === 'EACCES') { - console.error('Error: ', 'Access Denied!'); + console.error(Error('Access Denied!')); } - console.error('Error: ', error); + console.error(Error(error)); } } \ No newline at end of file diff --git a/src/fs/snapshot.js b/src/fs/snapshot.js index bfecbcc2..cd607460 100644 --- a/src/fs/snapshot.js +++ b/src/fs/snapshot.js @@ -43,11 +43,11 @@ const snapshot = async (paths) => { } } catch (error) { if (error === 'ENOENT') { - console.error('Error: ', "No such file exist!"); + console.error(Error("FS operation failed")); } else if (error === 'EACCES') { - console.error('Error: ', "Access Denied!") + console.error(Error("Access Denied!")); } else { - console.error('Error: ', error) + console.error(Error(error)); } } }; @@ -61,10 +61,10 @@ async function isWorksapceExisted(path) { await fs.access(path, constants.R_OK); } catch (error) { if (error.code === 'ENOENT') { - console.error('Error: ', errorMsg); + console.error(Error(errorMsg)); } else if (error.code === 'EACCES') { - console.error('Error: ', 'Access Denied!'); + console.error(Error('Access Denied!')); } - console.error('Error: ', error); + console.error(Error(error)); } } \ No newline at end of file diff --git a/src/modules/dynamic.js b/src/modules/dynamic.js index 366fb13b..5d5c1a53 100644 --- a/src/modules/dynamic.js +++ b/src/modules/dynamic.js @@ -30,9 +30,9 @@ const dynamic = async () => { result = module.run(); } catch (error) { if (error === 'ENOENT') { - console.error('Error: ', "No such file exist!"); + console.error(Error("FS operation failed")); } else if (error === 'EACCES') { - console.error('Error: ', "Access Denied!") + console.error(Error("Access Denied!")); } else { console.error('Error: ', error) } diff --git a/src/utils/util.js b/src/utils/util.js index 3a0c4629..6a2d3305 100644 --- a/src/utils/util.js +++ b/src/utils/util.js @@ -7,11 +7,11 @@ export const isFileOrDirectoryExist = async (path) => { await fs.access(path, constants.R_OK); } catch (error) { if (error.code === 'ENOENT') { - console.error(errorMsg); + console.error(Error(errorMsg)); } else if (error.code === 'EACCES') { - console.error('Error: ', 'Access Denied!'); + console.error(Error('Access Denied!')); } - console.error('Error: ', error); + console.error(Error(error)); } } diff --git a/src/zip/compressDir.js b/src/zip/compressDir.js index 3a3c5089..42884e1a 100644 --- a/src/zip/compressDir.js +++ b/src/zip/compressDir.js @@ -1,4 +1,21 @@ +import { isFileOrDirectoryExist } from "../utils/util.js"; +import fs from 'node:fs/promises'; + const compressDir = async () => { + const targetPath = './workspace/toCompress/'; + isFileOrDirectoryExist('./workspace'); + isFileOrDirectoryExist(targetPath); + + try { + const files = await fs.readdir(targetPath); + + for (const file of files) { + + } + + } catch (error) { + + } // Write your code here // Read all files from workspace/toCompress/ // Compress entire directory structure into archive.br From 7cbdf5904d8843988212c08aef996ca181c28463 Mon Sep 17 00:00:00 2001 From: Akhmad Badalov Date: Mon, 9 Mar 2026 01:01:27 +0500 Subject: [PATCH 19/22] fix: no console --- src/cli/progress.js | 3 +-- src/zip/compressDir.js | 33 +++++++++------------------------ 2 files changed, 10 insertions(+), 26 deletions(-) diff --git a/src/cli/progress.js b/src/cli/progress.js index 645ad4ea..61efb4ce 100644 --- a/src/cli/progress.js +++ b/src/cli/progress.js @@ -25,8 +25,7 @@ const progress = () => { if (values.color) { values.color = moderatedArgv[moderatedArgv.indexOf('--color') + 1]; } - - console.log(values.color) + validateValueType(values); const fullWidth = parseInt(values.length); diff --git a/src/zip/compressDir.js b/src/zip/compressDir.js index 42884e1a..67600c9e 100644 --- a/src/zip/compressDir.js +++ b/src/zip/compressDir.js @@ -1,26 +1,11 @@ -import { isFileOrDirectoryExist } from "../utils/util.js"; -import fs from 'node:fs/promises'; +import { createReadStream, createWriteStream, existsSync, mkdirSync } from 'fs'; +import { readdir, stat } from 'fs/promises'; +import { join, relative } from 'path'; +import { pipeline } from 'stream/promises'; +import zlib from 'zlib'; +import { Readable } from 'stream'; -const compressDir = async () => { - const targetPath = './workspace/toCompress/'; - isFileOrDirectoryExist('./workspace'); - isFileOrDirectoryExist(targetPath); +const compress = async () => { +} - try { - const files = await fs.readdir(targetPath); - - for (const file of files) { - - } - - } catch (error) { - - } - // Write your code here - // Read all files from workspace/toCompress/ - // Compress entire directory structure into archive.br - // Save to workspace/compressed/ - // Use Streams API -}; - -await compressDir(); +await compress(); From 2b99ca005542f0c14dca200f409549e2a79633c8 Mon Sep 17 00:00:00 2001 From: Akhmad Badalov Date: Mon, 9 Mar 2026 02:31:13 +0500 Subject: [PATCH 20/22] feat: compressing --- src/utils/util.js | 12 ++++++++++ src/zip/compressDir.js | 50 ++++++++++++++++++++++++++++++++++++++---- 2 files changed, 58 insertions(+), 4 deletions(-) diff --git a/src/utils/util.js b/src/utils/util.js index 6a2d3305..69e8fcbb 100644 --- a/src/utils/util.js +++ b/src/utils/util.js @@ -1,4 +1,5 @@ import fs, { constants } from 'node:fs/promises'; +import { join } from 'path'; export const isFileOrDirectoryExist = async (path) => { const errorMsg = 'FS operation failed'; @@ -68,4 +69,15 @@ export const validateValueType = (argsKeyValueForm) => { if (!regHex.test(argsKeyValueForm.color)) { argsKeyValueForm.color = '#FFFFFF'; } +} + +export async function getFilesRecursively(fullPath) { + const files = await fs.readdir(fullPath, { withFileTypes: true }); + const promisedFiles = files.map(async (el) => { + const fullPth = join(fullPath, el.name); + return el.isDirectory() ? (await getFilesRecursively(fullPth)) : fullPth; + }); + + const mappedFiles = await Promise.all(promisedFiles); + return mappedFiles.flat(); } \ No newline at end of file diff --git a/src/zip/compressDir.js b/src/zip/compressDir.js index 67600c9e..f91266a3 100644 --- a/src/zip/compressDir.js +++ b/src/zip/compressDir.js @@ -1,11 +1,53 @@ -import { createReadStream, createWriteStream, existsSync, mkdirSync } from 'fs'; -import { readdir, stat } from 'fs/promises'; +import { createReadStream, createWriteStream } from 'fs'; import { join, relative } from 'path'; +import fs, { constants } from 'node:fs/promises'; import { pipeline } from 'stream/promises'; -import zlib from 'zlib'; -import { Readable } from 'stream'; +import { createBrotliCompress } from 'node:zlib'; +import { isFileOrDirectoryExist, getFilesRecursively } from '../utils/util.js'; +import { Readable } from 'node:stream'; const compress = async () => { + const sourcePath = './workspace/toCompress'; + const finalToPath = './workspace/compressed'; + const toFile = join(finalToPath, 'archive.br'); + isFileOrDirectoryExist(sourcePath); + + try { + await fs.access(finalToPath, constants.F_OK); + } catch (error) { + if (error.code === 'ENOENT') { + console.log('a') + await fs.mkdir(finalToPath, { recursive: true }); + } + } + + + try { + const files = await getFilesRecursively(sourcePath); + + async function* iterativeWriteinArchive() { + for await (const file of files) { + const relativePath = relative(sourcePath, file); + const fileStat = await fs.stat(file); + + const header = Buffer.from(`${relativePath.length}:${relativePath}:${fileStat.size}:`); + yield header; + + const readStream = createReadStream(file); + for await (const chunk of readStream) { + yield chunk; + } + } + } + + await pipeline( + Readable.from(iterativeWriteinArchive()), + createBrotliCompress(), + createWriteStream(toFile) + ) + } catch (error) { + console.error(Error(error)); + } } await compress(); From b7a721950027dfbc94d3ed7febb7afdcaa86e0bb Mon Sep 17 00:00:00 2001 From: Akhmad Badalov Date: Mon, 9 Mar 2026 02:56:14 +0500 Subject: [PATCH 21/22] fix: space --- src/zip/compressDir.js | 19 +++++++++---------- src/zip/decompressDir.js | 33 +++++++++++++++++++++++++++++---- 2 files changed, 38 insertions(+), 14 deletions(-) diff --git a/src/zip/compressDir.js b/src/zip/compressDir.js index f91266a3..09077f2f 100644 --- a/src/zip/compressDir.js +++ b/src/zip/compressDir.js @@ -16,27 +16,26 @@ const compress = async () => { await fs.access(finalToPath, constants.F_OK); } catch (error) { if (error.code === 'ENOENT') { - console.log('a') await fs.mkdir(finalToPath, { recursive: true }); } } - + try { const files = await getFilesRecursively(sourcePath); async function* iterativeWriteinArchive() { for await (const file of files) { - const relativePath = relative(sourcePath, file); - const fileStat = await fs.stat(file); + const relativePath = relative(sourcePath, file); + const fileStat = await fs.stat(file); - const header = Buffer.from(`${relativePath.length}:${relativePath}:${fileStat.size}:`); - yield header; + const header = Buffer.from(`${relativePath.length}:${relativePath}:${fileStat.size}:`); + yield header; - const readStream = createReadStream(file); - for await (const chunk of readStream) { - yield chunk; - } + const readStream = createReadStream(file); + for await (const chunk of readStream) { + yield chunk; + } } } diff --git a/src/zip/decompressDir.js b/src/zip/decompressDir.js index d6e770f6..8a883457 100644 --- a/src/zip/decompressDir.js +++ b/src/zip/decompressDir.js @@ -1,8 +1,33 @@ +import { createReadStream, createWriteStream } from 'node:fs'; +import fs, { constants } from 'node:fs/promises'; +import path, { join } from 'node:path'; +import { pipeline } from 'node:stream'; +import { createBrotliDecompress } from 'node:zlib'; +import { isFileOrDirectoryExist } from '../utils/util.js'; + const decompressDir = async () => { - // Write your code here - // Read archive.br from workspace/compressed/ - // Decompress and extract to workspace/decompressed/ - // Use Streams API + const sourcePath = './workspace/compressed'; + const fromFile = join(sourcePath, 'archive.br'); + const toFolder = './workspace/decompressed'; + isFileOrDirectoryExist(sourcePath); + isFileOrDirectoryExist(fromFile); + + try { + await fs.access(toFolder, constants.F_OK); + } catch (error) { + if (error.code === 'ENOENT') { + await fs.mkdir(toFolder, { recursive: true }); + } + } + + const readStream = createReadStream(fromFile); + const transformStream = createBrotliDecompress(); + + try { + await pipeline(source, brotli, parseArchive); + } catch (error) { + console.error(Error(error)); + } }; await decompressDir(); From fec41e58c09d79c20edd36781bdcb430c931f926 Mon Sep 17 00:00:00 2001 From: Akhmad Badalov Date: Mon, 9 Mar 2026 11:19:40 +0500 Subject: [PATCH 22/22] fix: module name --- src/streams/lineNumberer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/streams/lineNumberer.js b/src/streams/lineNumberer.js index 74c47309..73162ce8 100644 --- a/src/streams/lineNumberer.js +++ b/src/streams/lineNumberer.js @@ -1,4 +1,4 @@ -import { StartLineWithNumberTranform } from './customized/transformStream.js'; +import { StartLineWithNumberTranform } from './customized/numberTransformStream.js'; const lineNumberer = () => { const customLine = new StartLineWithNumberTranform();