From b9bf5f4f05d8c51fb76262aeaecc69be993a6272 Mon Sep 17 00:00:00 2001 From: Ihor Stanovyi Date: Mon, 9 Mar 2026 01:47:08 +0200 Subject: [PATCH 1/9] feat: implement cli functions --- src/cli/interactive.js | 37 +++++++++++++++++++++++++++---- src/cli/progress.js | 50 ++++++++++++++++++++++++++++++++++++++---- 2 files changed, 79 insertions(+), 8 deletions(-) diff --git a/src/cli/interactive.js b/src/cli/interactive.js index d0e3e0d9..7b799cb6 100644 --- a/src/cli/interactive.js +++ b/src/cli/interactive.js @@ -1,8 +1,37 @@ +import readline from "readline"; + 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 currentReadLine = readline.createInterface({ + input: process.stdin, + output: process.stdout, + }); + + currentReadLine.setPrompt("> "); + currentReadLine.prompt(); + + currentReadLine.on("line", (line) => { + const cmd = line.trim(); + + if (cmd === "uptime") { + console.log(`Uptime: ${process.uptime().toFixed(2)}s`); + } else if (cmd === "cwd") { + console.log(process.cwd()); + } else if (cmd === "date") { + console.log(new Date().toISOString()); + } else if (cmd === "exit") { + console.log("Goodbye!"); + process.exit(0); + } else { + console.log("Unknown command"); + } + + currentReadLine.prompt(); + }); + + currentReadLine.on("close", () => { + console.log("Goodbye!"); + process.exit(0); + }); }; interactive(); diff --git a/src/cli/progress.js b/src/cli/progress.js index 3e060763..3c4d92bb 100644 --- a/src/cli/progress.js +++ b/src/cli/progress.js @@ -1,8 +1,50 @@ 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 currentArgs = process.argv; + + const getArg = (name, def) => { + const i = currentArgs.indexOf(name); + return i !== -1 ? Number(currentArgs[i + 1]) : def; + }; + + const duration = getArg("--duration", 5000); + const interval = getArg("--interval", 100); + const length = getArg("--length", 30); + + const colorIndex = currentArgs.indexOf("--color"); + let colorCode = ""; + + if (colorIndex !== -1) { + const hex = currentArgs[colorIndex + 1]; + if (/^#[0-9a-fA-F]{6}$/.test(hex)) { + const r = parseInt(hex.slice(1, 3), 16); + const g = parseInt(hex.slice(3, 5), 16); + const b = parseInt(hex.slice(5, 7), 16); + + colorCode = `\x1b[38;2;${r};${g};${b}m`; + process.stdout.write(`\x1b[38;2;${r};${g};${b}mTEST COLOR\x1b[0m\n`); + } + } + + const steps = Math.floor(duration / interval); + let current = 0; + + const timer = setInterval(() => { + current++; + + const percent = Math.round((current / steps) * 100); + + const filled = Math.round((current / steps) * length); + const empty = length - filled; + + const bar = colorCode + "█".repeat(filled) + "\x1b[0m" + " ".repeat(empty); + + process.stdout.write(`\r[${bar}] ${percent}%`); + + if (current >= steps) { + clearInterval(timer); + process.stdout.write("\nDone!\n"); + } + }, interval); }; progress(); From 4dd9669d1df5616eeb74765c171b864cac8fc435 Mon Sep 17 00:00:00 2001 From: Ihor Stanovyi Date: Mon, 9 Mar 2026 01:47:37 +0200 Subject: [PATCH 2/9] feat: implement cp functions --- src/cp/execCommand.js | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/cp/execCommand.js b/src/cp/execCommand.js index 34a89c8d..3dd7d9e9 100644 --- a/src/cp/execCommand.js +++ b/src/cp/execCommand.js @@ -1,10 +1,20 @@ +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 input = process.argv[2]; + + const [cmd, ...args] = input.split(" "); + + const child = spawn(cmd, args, { + env: process.env, + }); + + child.stdout.pipe(process.stdout); + child.stderr.pipe(process.stderr); + + child.on("close", (code) => { + process.exit(code); + }); }; execCommand(); From adde1474731543fae5d0d78f4808c9ac80b5ce04 Mon Sep 17 00:00:00 2001 From: Ihor Stanovyi Date: Mon, 9 Mar 2026 01:47:51 +0200 Subject: [PATCH 3/9] feat: implement fs functions --- src/fs/findByExt.js | 47 ++++++++++++++++++++++++++++++++++++---- src/fs/merge.js | 53 ++++++++++++++++++++++++++++++++++++++++----- src/fs/restore.js | 44 ++++++++++++++++++++++++++++++++----- src/fs/snapshot.js | 47 ++++++++++++++++++++++++++++++++++------ 4 files changed, 170 insertions(+), 21 deletions(-) diff --git a/src/fs/findByExt.js b/src/fs/findByExt.js index 24f06cb8..58ffde31 100644 --- a/src/fs/findByExt.js +++ b/src/fs/findByExt.js @@ -1,7 +1,46 @@ -const findByExt = async () => { - // Write your code here - // Recursively find all files with specific extension - // Parse --ext CLI argument (default: .txt) +import { readdir, stat } from "fs/promises"; +import { join, relative, extname } from "path"; +import { fileURLToPath } from "url"; +import { dirname } from "path"; + +const directoryName = dirname(fileURLToPath(import.meta.url)); + +export const findByExt = async () => { + const args = process.argv; + const extIndex = args.indexOf("--ext"); + + const ext = extIndex !== -1 ? args[extIndex + 1] : "txt"; + + const dotExt = ext.startsWith(".") ? ext : `.${ext}`; + + const workspacePath = join(directoryName, "workspace"); + + await stat(workspacePath).catch(() => { + throw new Error("FS operation failed"); + }); + + const found = []; + + async function scan(dir) { + const items = await readdir(dir); + + for (const item of items) { + const fullPath = join(dir, item); + const info = await stat(fullPath); + + if (info.isDirectory()) { + await scan(fullPath); + } else { + if (extname(item) === dotExt) { + found.push(relative(workspacePath, fullPath)); + } + } + } + } + + await scan(workspacePath); + + found.sort().forEach((p) => console.log(p)); }; await findByExt(); diff --git a/src/fs/merge.js b/src/fs/merge.js index cb8e0d8f..64efef63 100644 --- a/src/fs/merge.js +++ b/src/fs/merge.js @@ -1,8 +1,51 @@ -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 +import { readdir, readFile, writeFile, stat } from "fs/promises"; +import { join, dirname } from "path"; +import { fileURLToPath } from "url"; + +const directoryName = dirname(fileURLToPath(import.meta.url)); + +export const merge = async () => { + const workspacePath = join(directoryName, "workspace"); + const partsPath = join(workspacePath, "parts"); + + await stat(partsPath).catch(() => { + throw new Error("FS operation failed"); + }); + + const args = process.argv; + const filesIndex = args.indexOf("--files"); + + let filesToMerge = []; + + if (filesIndex !== -1) { + const fileNames = args[filesIndex + 1].split(","); + + for (const name of fileNames) { + const fullPath = join(partsPath, name); + await stat(fullPath).catch(() => { + throw new Error("FS operation failed"); + }); + filesToMerge.push(fullPath); + } + } else { + const items = await readdir(partsPath); + filesToMerge = items + .filter((item) => item.endsWith(".txt")) + .sort() + .map((item) => join(partsPath, item)); + + if (filesToMerge.length === 0) { + throw new Error("FS operation failed"); + } + } + + const currentChunks = []; + for (const filePath of filesToMerge) { + const text = await readFile(filePath, "utf-8"); + currentChunks.push(text); + } + + await writeFile(join(workspacePath, "merged.txt"), currentChunks.join("")); }; await merge(); diff --git a/src/fs/restore.js b/src/fs/restore.js index 96ae1ffb..538ee843 100644 --- a/src/fs/restore.js +++ b/src/fs/restore.js @@ -1,8 +1,42 @@ -const restore = async () => { - // Write your code here - // Read snapshot.json - // Treat snapshot.rootPath as metadata only - // Recreate directory/file structure in workspace_restored +import { mkdir, readFile, writeFile, access } from "fs/promises"; +import { join, dirname } from "path"; +import { fileURLToPath } from "url"; + +const directoryName = dirname(fileURLToPath(import.meta.url)); + +export const restore = async () => { + const snapshotPath = join(directoryName, "snapshot.json"); + const restorePath = join(directoryName, "workspace_restored"); + + await access(snapshotPath).catch(() => { + throw new Error("FS operation failed"); + }); + + await access(restorePath) + .then(() => { + throw new Error("FS operation failed"); + }) + .catch((err) => { + if (err.message === "FS operation failed") { + throw err; + } + }); + + const raw = await readFile(snapshotPath, "utf-8"); + const snapshot = JSON.parse(raw); + + await mkdir(restorePath); + + for (const entry of snapshot.entries) { + const fullPath = join(restorePath, entry.path); + + if (entry.type === "directory") { + await mkdir(fullPath, { recursive: true }); + } else { + const fileBytes = Buffer.from(entry.content, "base64"); + await writeFile(fullPath, fileBytes); + } + } }; await restore(); diff --git a/src/fs/snapshot.js b/src/fs/snapshot.js index 050103d3..5cd2d4e1 100644 --- a/src/fs/snapshot.js +++ b/src/fs/snapshot.js @@ -1,9 +1,42 @@ -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 { readdir, readFile, stat, writeFile } from "fs/promises"; +import { join, relative } from "path"; + +export const snapshot = async (workspacePath) => { + await stat(workspacePath).catch(() => { + throw new Error("FS operation failed"); + }); + + const entries = []; + + async function scan(dir) { + const items = await readdir(dir); + + for (const item of items) { + const fullPath = join(dir, item); + const relPath = relative(workspacePath, fullPath); + const info = await stat(fullPath); + + if (info.isDirectory()) { + entries.push({ path: relPath, type: "directory" }); + await scan(fullPath); + } else { + const fileBytes = await readFile(fullPath); + entries.push({ + path: relPath, + type: "file", + size: info.size, + content: fileBytes.toString("base64"), + }); + } + } + } + + await scan(workspacePath); + + const result = { rootPath: workspacePath, entries }; + const saveTo = join(workspacePath, "..", "snapshot.json"); + + await writeFile(saveTo, JSON.stringify(result, null, 2)); }; -await snapshot(); +await snapshot(new URL("workspace", import.meta.url).pathname); From d36b9b5eefdadb306465dfba951e01493ff3eba3 Mon Sep 17 00:00:00 2001 From: Ihor Stanovyi Date: Mon, 9 Mar 2026 01:48:04 +0200 Subject: [PATCH 4/9] feat: implement hash functions --- src/hash/file1.txt | 1 + src/hash/verify.js | 36 ++++++++++++++++++++++++++++++++---- 2 files changed, 33 insertions(+), 4 deletions(-) create mode 100644 src/hash/file1.txt diff --git a/src/hash/file1.txt b/src/hash/file1.txt new file mode 100644 index 00000000..ce013625 --- /dev/null +++ b/src/hash/file1.txt @@ -0,0 +1 @@ +hello diff --git a/src/hash/verify.js b/src/hash/verify.js index 7f1e8961..346f873d 100644 --- a/src/hash/verify.js +++ b/src/hash/verify.js @@ -1,8 +1,36 @@ +import { createReadStream, createWriteStream } from "fs"; +import { readFile, access } from "fs/promises"; +import { createHash } from "crypto"; +import { join, dirname } from "path"; +import { fileURLToPath } from "url"; + +const directoryName = dirname(fileURLToPath(import.meta.url)); + const verify = async () => { - // Write your code here - // Read checksums.json - // Calculate SHA256 hash using Streams API - // Print result: filename — OK/FAIL + const checksumsPath = join(directoryName, "checksums.json"); + + await access(checksumsPath).catch(() => { + throw new Error("FS operation failed"); + }); + + const raw = await readFile(checksumsPath, "utf-8"); + const checksums = JSON.parse(raw); + + for (const [filename, expectedHash] of Object.entries(checksums)) { + const filePath = join(directoryName, filename); + + const actualHash = await new Promise((resolve, reject) => { + const hash = createHash("sha256"); + const stream = createReadStream(filePath); + + stream.on("data", (chunk) => hash.update(chunk)); + stream.on("end", () => resolve(hash.digest("hex"))); + stream.on("error", reject); + }); + + const status = actualHash === expectedHash ? "OK" : "FAIL"; + console.log(`${filename} — ${status}`); + } }; await verify(); From f24e187d3e190e63817e188c23443e062510503a Mon Sep 17 00:00:00 2001 From: Ihor Stanovyi Date: Mon, 9 Mar 2026 01:48:38 +0200 Subject: [PATCH 5/9] feat: implement dunamic function --- src/modules/dynamic.js | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/modules/dynamic.js b/src/modules/dynamic.js index 008ca387..20dbb7e1 100644 --- a/src/modules/dynamic.js +++ b/src/modules/dynamic.js @@ -1,9 +1,20 @@ +import { join, dirname } from "path"; +import { fileURLToPath } from "url"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + 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 pluginName = process.argv[2]; + + const pluginPath = join(__dirname, "plugins", `${pluginName}.js`); + + try { + const plugin = await import(pluginPath); + console.log(plugin.run()); + } catch { + console.log("Plugin not found"); + process.exit(1); + } }; await dynamic(); From 1ffe3d0784ac9836c4e5e29393ead6976e994c87 Mon Sep 17 00:00:00 2001 From: Ihor Stanovyi Date: Mon, 9 Mar 2026 01:49:01 +0200 Subject: [PATCH 6/9] feat: implement stream functions --- src/streams/filter.js | 37 +++++++++++++++++++---- src/streams/lineNumberer.js | 33 +++++++++++++++++--- src/streams/split.js | 60 +++++++++++++++++++++++++++++++++---- 3 files changed, 116 insertions(+), 14 deletions(-) diff --git a/src/streams/filter.js b/src/streams/filter.js index 3868ab46..ebcebaef 100644 --- a/src/streams/filter.js +++ b/src/streams/filter.js @@ -1,9 +1,36 @@ +import { Transform } from "stream"; + const filter = () => { - // Write your code here - // Read from process.stdin - // Filter lines by --pattern CLI argument - // Use Transform Stream - // Write to process.stdout + const patternIndex = process.argv.indexOf("--pattern"); + const pattern = patternIndex !== -1 ? process.argv[patternIndex + 1] : ""; + + let leftover = ""; + + const filterer = new Transform({ + transform(chunk, encoding, done) { + const text = leftover + chunk.toString(); + const lines = text.split("\n"); + + leftover = lines.pop(); + + for (const line of lines) { + if (line.includes(pattern)) { + this.push(`${line}\n`); + } + } + + done(); + }, + + flush(done) { + if (leftover && leftover.includes(pattern)) { + this.push(leftover); + } + done(); + }, + }); + + process.stdin.pipe(filterer).pipe(process.stdout); }; filter(); diff --git a/src/streams/lineNumberer.js b/src/streams/lineNumberer.js index 579d662e..f13ced5f 100644 --- a/src/streams/lineNumberer.js +++ b/src/streams/lineNumberer.js @@ -1,8 +1,33 @@ +import { Transform } from "stream"; + const lineNumberer = () => { - // Write your code here - // Read from process.stdin - // Use Transform Stream to prepend line numbers - // Write to process.stdout + let lineNum = 1; + let leftover = ""; + + const numberer = new Transform({ + transform(chunk, encoding, done) { + const text = leftover + chunk.toString(); + const lines = text.split("\n"); + + leftover = lines.pop(); + + for (const line of lines) { + this.push(`${lineNum} | ${line}\n`); + lineNum++; + } + + done(); + }, + + flush(done) { + if (leftover) { + this.push(`${lineNum} | ${leftover}`); + } + done(); + }, + }); + + process.stdin.pipe(numberer).pipe(process.stdout); }; lineNumberer(); diff --git a/src/streams/split.js b/src/streams/split.js index f8f814fa..edfa0546 100644 --- a/src/streams/split.js +++ b/src/streams/split.js @@ -1,8 +1,58 @@ +import { createReadStream, createWriteStream } from "fs"; +import { Transform } from "stream"; +import { join, dirname } from "path"; +import { fileURLToPath } from "url"; + +const directoryName = dirname(fileURLToPath(import.meta.url)); + 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 linesIndex = process.argv.indexOf("--lines"); + const maxLines = linesIndex !== -1 ? Number(process.argv[linesIndex + 1]) : 10; + + const sourcePath = join(directoryName, "source.txt"); + + let leftover = ""; + let lineBuffer = []; + let chunkNum = 1; + + + const writeChunk = (lines) => { + const outPath = join(directoryName, `chunk_${chunkNum}.txt`); + const writer = createWriteStream(outPath); + writer.write(lines.join("\n") + "\n"); + writer.end(); + chunkNum++; + }; + + await new Promise((resolve, reject) => { + const reader = createReadStream(sourcePath); + + reader.on("data", (chunk) => { + const text = leftover + chunk.toString(); + const lines = text.split("\n"); + leftover = lines.pop(); + + for (const line of lines) { + lineBuffer.push(line); + + if (lineBuffer.length === maxLines) { + writeChunk(lineBuffer); + lineBuffer = []; + } + } + }); + + reader.on("end", () => { + + if (leftover) lineBuffer.push(leftover); + + if (lineBuffer.length > 0) writeChunk(lineBuffer); + resolve(); + }); + + reader.on("error", reject); + }); }; -await split(); +await split(); \ No newline at end of file From 657cd828e4beaaac870f089f243500bd15167cdd Mon Sep 17 00:00:00 2001 From: Ihor Stanovyi Date: Mon, 9 Mar 2026 01:49:19 +0200 Subject: [PATCH 7/9] feat: implement wt functions --- src/wt/main.js | 65 ++++++++++++++++++++++++++++++++++++++++++------ src/wt/worker.js | 11 +++----- 2 files changed, 62 insertions(+), 14 deletions(-) diff --git a/src/wt/main.js b/src/wt/main.js index d7d21f0c..ed73e317 100644 --- a/src/wt/main.js +++ b/src/wt/main.js @@ -1,11 +1,62 @@ +import { readFile } from "fs/promises"; +import { Worker } from "worker_threads"; +import { cpus } from "os"; +import { join, dirname } from "path"; +import { fileURLToPath } from "url"; + +const directoryName = dirname(fileURLToPath(import.meta.url)); + 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 raw = await readFile(join(directoryName, "data.json"), "utf-8"); + const numbers = JSON.parse(raw); + const coreCount = cpus().length; + + const chunkSize = Math.ceil(numbers.length / coreCount); + const chunks = []; + for (let i = 0; i < coreCount; i++) { + chunks.push(numbers.slice(i * chunkSize, (i + 1) * chunkSize)); + } + + const sortedChunks = await Promise.all( + chunks.map( + (chunk, i) => + new Promise((resolve, reject) => { + const worker = new Worker(join(directoryName, "worker.js")); + + worker.postMessage(chunk); + + worker.on("message", resolve); + worker.on("error", reject); + }), + ), + ); + + const result = []; + + const pointers = new Array(sortedChunks.length).fill(0); + + while (true) { + let minVal = Infinity; + let minIdx = -1; + + for (let i = 0; i < sortedChunks.length; i++) { + if (pointers[i] < sortedChunks[i].length) { + if (sortedChunks[i][pointers[i]] < minVal) { + minVal = sortedChunks[i][pointers[i]]; + minIdx = i; + } + } + } + + if (minIdx === -1) { + break; + } + + result.push(minVal); + pointers[minIdx]++; + } + + console.log(result); }; await main(); diff --git a/src/wt/worker.js b/src/wt/worker.js index 15f42fc8..4ce57141 100644 --- a/src/wt/worker.js +++ b/src/wt/worker.js @@ -1,9 +1,6 @@ -import { parentPort } from 'worker_threads'; +import { parentPort } from "worker_threads"; -// Receive array from main thread -// Sort in ascending order -// Send back to main thread - -parentPort.on('message', (data) => { - // Write your code here +parentPort.on("message", (data) => { + const sortedData = [...data].sort((a, b) => a - b); + parentPort.postMessage(sortedData); }); From c0ba8c53ae1cd60d589b8f9acbb0f6985bcb4312 Mon Sep 17 00:00:00 2001 From: Ihor Stanovyi Date: Mon, 9 Mar 2026 01:49:33 +0200 Subject: [PATCH 8/9] feat: implement zip functions --- src/zip/compressDir.js | 67 +++++++++++++++++++++++++++++++++++++--- src/zip/decompressDir.js | 54 +++++++++++++++++++++++++++++--- 2 files changed, 112 insertions(+), 9 deletions(-) diff --git a/src/zip/compressDir.js b/src/zip/compressDir.js index 3a3c5089..ceec4292 100644 --- a/src/zip/compressDir.js +++ b/src/zip/compressDir.js @@ -1,9 +1,66 @@ +import { createReadStream, createWriteStream } from "fs"; +import { readdir, stat, mkdir, access } from "fs/promises"; +import { createBrotliCompress } from "zlib"; +import { join, relative, dirname } from "path"; +import { fileURLToPath } from "url"; +import { pipeline } from "stream/promises"; + +const directoryName = dirname(fileURLToPath(import.meta.url)); + const compressDir = async () => { - // Write your code here - // Read all files from workspace/toCompress/ - // Compress entire directory structure into archive.br - // Save to workspace/compressed/ - // Use Streams API + const toCompressPath = join(directoryName, "workspace", "toCompress"); + const compressedPath = join(directoryName, "workspace", "compressed"); + const archivePath = join(compressedPath, "archive.br"); + + + await access(toCompressPath).catch(() => { + throw new Error("FS operation failed"); + }); + + + await mkdir(compressedPath, { recursive: true }); + + + const allFiles = []; + async function scan(dir) { + const items = await readdir(dir); + for (const item of items) { + const full = join(dir, item); + const info = await stat(full); + if (info.isDirectory()) { + await scan(full); + } else { + allFiles.push(full); + } + } + } + await scan(toCompressPath); + + + const writer = createWriteStream(archivePath); + const brotli = createBrotliCompress(); + brotli.pipe(writer); + + for (const filePath of allFiles) { + const relPath = relative(toCompressPath, filePath); + const info = await stat(filePath); + const fileData = await new Promise((resolve, reject) => { + const chunks = []; + const reader = createReadStream(filePath); + reader.on("data", (c) => chunks.push(c)); + reader.on("end", () => resolve(Buffer.concat(chunks))); + reader.on("error", reject); + }); + + + const header = JSON.stringify({ path: relPath, size: info.size }) + "\n"; + brotli.write(header); + brotli.write(fileData); + brotli.write("\n---END---\n"); + } + + brotli.end(); + await new Promise((resolve) => writer.on("finish", resolve)); }; await compressDir(); diff --git a/src/zip/decompressDir.js b/src/zip/decompressDir.js index d6e770f6..80130bd6 100644 --- a/src/zip/decompressDir.js +++ b/src/zip/decompressDir.js @@ -1,8 +1,54 @@ +import { createReadStream, createWriteStream } from "fs"; +import { mkdir, access } from "fs/promises"; +import { createBrotliDecompress } from "zlib"; +import { join, dirname } from "path"; +import { fileURLToPath } from "url"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + const decompressDir = async () => { - // Write your code here - // Read archive.br from workspace/compressed/ - // Decompress and extract to workspace/decompressed/ - // Use Streams API + const compressedPath = join(__dirname, "workspace", "compressed"); + const archivePath = join(compressedPath, "archive.br"); + const decompressedPath = join(__dirname, "workspace", "decompressed"); + + await access(compressedPath).catch(() => { + throw new Error("FS operation failed"); + }); + await access(archivePath).catch(() => { + throw new Error("FS operation failed"); + }); + + await mkdir(decompressedPath, { recursive: true }); + + const chunks = []; + await new Promise((resolve, reject) => { + const reader = createReadStream(archivePath); + const brotli = createBrotliDecompress(); + reader.pipe(brotli); + brotli.on("data", (c) => chunks.push(c)); + brotli.on("end", resolve); + brotli.on("error", reject); + }); + + const content = Buffer.concat(chunks).toString(); + const parts = content.split("\n---END---\n").filter(Boolean); + + for (const part of parts) { + const newlineIdx = part.indexOf("\n"); + const header = JSON.parse(part.slice(0, newlineIdx)); + const fileData = part.slice(newlineIdx + 1); + + const outPath = join(decompressedPath, header.path); + + await mkdir(dirname(outPath), { recursive: true }); + await new Promise((resolve, reject) => { + const writer = createWriteStream(outPath); + writer.write(fileData); + writer.end(); + writer.on("finish", resolve); + writer.on("error", reject); + }); + } }; await decompressDir(); From 5b708b8ce566833727159ac950950183f4a77a10 Mon Sep 17 00:00:00 2001 From: Ihor Stanovyi Date: Mon, 9 Mar 2026 01:59:06 +0200 Subject: [PATCH 9/9] feat: add new branch --- src/cli/progress.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/cli/progress.js b/src/cli/progress.js index 3c4d92bb..cba0c9ea 100644 --- a/src/cli/progress.js +++ b/src/cli/progress.js @@ -48,3 +48,5 @@ const progress = () => { }; progress(); + +