Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions input.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
a
b
c
17 changes: 17 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

39 changes: 39 additions & 0 deletions src/cli/interactive.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,47 @@
import readline from "node: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 rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});

rl.on("line", (input) => {
const command = input.trim();

switch (command) {
case "uptime":
console.log(process.uptime());
break;

case "cwd":
console.log(process.cwd());
break;

case "date":
console.log(new Date().toString());
break;

case "exit":
rl.close();
return;

default:
console.log("Unknown command");
}
});

rl.on("close", () => {
process.exit(0);
});

process.on("SIGINT", () => {
rl.close();
});
};

interactive();
20 changes: 20 additions & 0 deletions src/cli/progress.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,26 @@ const progress = () => {
// Simulate progress bar from 0% to 100% over ~5 seconds
// Update in place using \r every 100ms
// Format: [████████████████████ ] 67%

let percent = 0;
const totalSteps = 50;
const barLength = 30;

const interval = setInterval(() => {
percent += 100 / totalSteps;

const filled = Math.round((percent / 100) * barLength);
const empty = barLength - filled;

const bar = "█".repeat(filled) + " ".repeat(empty);

process.stdout.write(`\r[${bar}] ${Math.round(percent)}%`);

if (percent >= 100) {
clearInterval(interval);
process.stdout.write("\n");
}
}, 100);
};

progress();
16 changes: 16 additions & 0 deletions src/cp/execCommand.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,26 @@
import { spawn } from "node: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 command = process.argv[2];

const child = spawn(command, {
shell: true,
env: process.env,
});

child.stdout.pipe(process.stdout);
child.stderr.pipe(process.stderr);

child.on("close", (code) => {
process.exit(code);
});
};

execCommand();
26 changes: 26 additions & 0 deletions src/fs/findByExt.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,33 @@
import fs from "node:fs/promises";
import path from "node:path";

const findByExt = async () => {
// Write your code here
// Recursively find all files with specific extension
// Parse --ext CLI argument (default: .txt)

/* console.log(process.argv); */

const extIndex = process.argv.indexOf("--ext");
const extension = extIndex !== -1 ? "." + process.argv[extIndex + 1] : ".txt";

const startDir = process.cwd();

const search = async (dir) => {
const entries = await fs.readdir(dir, { withFileTypes: true });

for (const entry of entries) {
const fullPath = path.join(dir, entry.name);

if (entry.isDirectory()) {
await search(fullPath);
} else if (entry.isFile() && entry.name.endsWith(extension)) {
console.log(fullPath);
}
}
};

await search(startDir);
};

await findByExt();
28 changes: 28 additions & 0 deletions src/fs/merge.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,36 @@
import fs from "node:fs/promises";
import path from "node:path";

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 workspaceDir = path.join(process.cwd(), "workspace");
const partsDir = path.join(workspaceDir, "parts");
const outputFile = path.join(workspaceDir, "merged.txt");

const filesIndex = process.argv.indexOf("--files");

let files = [];

if (filesIndex !== -1) {
files = process.argv[filesIndex + 1].split(",");
} else {
const entries = await fs.readdir(partsDir);
files = entries.filter((file) => file.endsWith(".txt")).sort();
}

let mergedContent = "";

for (const file of files) {
const filePath = path.join(partsDir, file);
const content = await fs.readFile(filePath, "utf8");
mergedContent += content;
}

await fs.writeFile(outputFile, mergedContent);
};

await merge();
31 changes: 31 additions & 0 deletions src/fs/restore.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,39 @@
import fs from "node:fs/promises";
import path from "node:path";

const restore = async () => {
// Write your code here
// Read snapshot.json
// Treat snapshot.rootPath as metadata only
// Recreate directory/file structure in workspace_restored
try {
const snapshotRaw = await fs.readFile("snapshot.json", "utf8");
const snapshot = JSON.parse(snapshotRaw);

const destDir = path.join(process.cwd(), "workspace_restored");

try {
await fs.access(destDir);
throw new Error();
} catch {}

for (const entry of snapshot.entries) {
const entryPath = path.join(destDir, entry.path);

if (entry.type === "directory") {
await fs.mkdir(entryPath, { recursive: true });
} else if (entry.type === "file") {
await fs.mkdir(path.dirname(entryPath), { recursive: true });
const contentBuffer = Buffer.from(entry.content, "base64");
await fs.writeFile(entryPath, contentBuffer);
}
}

console.log("workspace_restored created successfully");
} catch (err) {
console.error("FS operation failed");
process.exit(1);
}
};

await restore();
44 changes: 44 additions & 0 deletions src/fs/snapshot.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,53 @@
import fs from "node:fs/promises";
import path from "node:path";

const scanDir = async (dir, baseDir) => {
const entries = [];
const items = await fs.readdir(dir, { withFileTypes: true });

for (const item of items) {
const fullPath = path.join(dir, item.name);
const relativePath = path.relative(baseDir, fullPath);

if (item.isDirectory()) {
entries.push({ path: relativePath, type: "directory" });
const subEntries = await scanDir(fullPath, baseDir);
entries.push(...subEntries);
} else if (item.isFile()) {
const contentBuffer = await fs.readFile(fullPath);
entries.push({
path: relativePath,
type: "file",
size: contentBuffer.length,
content: contentBuffer.toString("base64"),
});
}
}

return entries;
};

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

const workspaceDir = path.join(process.cwd(), "workspace");

try {
await fs.access(workspaceDir);
const entries = await scanDir(workspaceDir, workspaceDir);

const snapshotData = { rootPath: workspaceDir, entries };
await fs.writeFile("snapshot.json", JSON.stringify(snapshotData, null, 2));

console.log("snapshot.json created successfully");
} catch (err) {
console.error("FS operation failed");
process.exit(1);
}
};

await snapshot();
26 changes: 26 additions & 0 deletions src/hash/verify.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,34 @@
import fs from "node:fs";
import path from "node:path";
import crypto from "node:crypto";

const verify = async () => {
// Write your code here
// Read checksums.json
// Calculate SHA256 hash using Streams API
// Print result: filename — OK/FAIL
const checksums = JSON.parse(
await fs.promises.readFile("checksums.json", "utf8"),
);

for (const item of checksums) {
const filePath = path.join("workspace", item.file);
const hash = crypto.createHash("sha256");
const stream = fs.createReadStream(filePath);

await new Promise((resolve, reject) => {
stream.on("data", (chunk) => hash.update(chunk));
stream.on("end", () => resolve());
stream.on("error", reject);
});

const digest = hash.digest("hex").toLowerCase();
if (digest === item.hash.toLowerCase()) {
console.log(`${item.file} — OK`);
} else {
console.log(`${item.file} — FAIL`);
}
}
};

await verify();
15 changes: 15 additions & 0 deletions src/modules/dynamic.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,21 @@ const dynamic = async () => {
// Dynamically import plugin from plugins/ directory
// Call run() function and print result
// Handle missing plugin case

const pluginName = process.argv[2];
if (!pluginName) {
console.log("Plugin name not provided");
process.exit(1);
}

try {
const plugin = await import(`./plugins/${pluginName}.js`);
const result = await plugin.run();
console.log(result);
} catch {
console.log("Plugin not found");
process.exit(1);
}
};

await dynamic();
17 changes: 17 additions & 0 deletions src/streams/filter.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,26 @@
import { Transform } from "node: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 patternArg = process.argv.find((arg) => arg.startsWith("--pattern="));
const pattern = patternArg ? patternArg.split("=")[1] : "";

const transform = new Transform({
transform(chunk, encoding, callback) {
const lines = chunk.toString().split("\n");
const filtered = lines
.filter((line) => line.includes(pattern))
.join("\n");
this.push(filtered ? filtered + "\n" : "");
callback();
},
});

process.stdin.pipe(transform).pipe(process.stdout);
};

filter();
17 changes: 17 additions & 0 deletions src/streams/lineNumberer.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,25 @@
import { Transform } from "node:stream";

const lineNumberer = () => {
// Write your code here
// Read from process.stdin
// Use Transform Stream to prepend line numbers
// Write to process.stdout

let lineCount = 0;

const transform = new Transform({
transform(chunk, encoding, callback) {
const lines = chunk.toString().split("\n");
const numbered = lines
.map((line) => (line ? `${++lineCount}: ${line}` : ""))
.join("\n");
this.push(numbered + "\n");
callback();
},
});

process.stdin.pipe(transform).pipe(process.stdout);
};

lineNumberer();
Loading