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
41 changes: 41 additions & 0 deletions src/cli/interactive.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,49 @@
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 rl = readline.createInterface(
process.stdin, process.stdout);
const startTime = performance.now();
rl.setPrompt(`>`);
rl.prompt();
rl.on('line', (cmd) => {
switch (cmd) {
case 'pwd':
console.log(process.cwd())
break;
case 'uptime':
upTime(startTime)
break;
case 'date':
printDate()
break;
case 'exit':
process.exit(0)
default:
console.log(`Unknown command`)
}
});
};

function printDate() {
const now = new Date();
console.log(now.toISOString())
}

function upTime (startTime) {
const endTime = performance.now();
const elapsedTime = ((endTime - startTime) / 1000).toFixed(2);
console.log(`Uptime: ${elapsedTime}`)
}

interactive();

process.on('exit', (code) => {
console.log(`Goodbye!`)
});
60 changes: 60 additions & 0 deletions src/cli/progress.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,68 @@

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%
let duration = 5000
let interval = 100
let length = 30

let st = 0;
let color = '\x1b[37m'

const colorIdx = process.argv.indexOf("--color")
if (colorIdx > -1 && colorIdx < process.argv.length - 1) {
const rgb = hexToRgb(process.argv[colorIdx + 1])
if (rgb != null) color = rgb
}
const durationIdx = process.argv.indexOf("--duration")
if (durationIdx > -1 && durationIdx < process.argv.length - 1) {
duration = parseInt(process.argv[durationIdx + 1])
}

const intervalIdx = process.argv.indexOf("--interval")
if (intervalIdx > -1 && intervalIdx < process.argv.length - 1) {
interval = parseInt(process.argv[intervalIdx + 1])
}

const lengthIdx = process.argv.indexOf("--length")
if (lengthIdx > -1 && lengthIdx < process.argv.length - 1) {
length = parseInt(process.argv[lengthIdx + 1])
if (length < 1) {
length = 30
}
}

setInterval(() => {
updateProgress(st, duration, length, color);
st += interval;
}, interval);

setTimeout(() => {
console.log(`\nDone!`);
process.exit(0);
}, duration + interval)
};
const hexToRgb = (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);
if (isNaN(r) || isNaN(g) || isNaN(b)) {
return null
}
return `\x1b[38;2;${r};${g};${b}m`;
};

function updateProgress(progress, total, length, color) {
const percentage = Math.floor((progress / total) * 100);

const filledBar = `${color}${('█'.repeat((progress / total) * length))}`;
const emptyBar = ' '.repeat(length - (progress / total) * length);

process.stdout.clearLine();
process.stdout.cursorTo(0);
process.stdout.write("\x1b[37m[" + filledBar + emptyBar +"\x1b[37m] "+ percentage+ "%");
}

progress();
27 changes: 27 additions & 0 deletions src/cp/execCommand.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,37 @@
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

if (process.argv.length != 3) {
console.log("command not passed")
process.exit(1)
}

const [command, ...args] = process.argv[2].split(' ');

const child = spawn(command, args, {
env: process.env,
stdio: ['inherit', 'pipe', 'pipe'],
});

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

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

child.on('error', (err) => {
console.error(`Failed to start child process: ${err.message}`);
process.exit(1);
});
};


execCommand();
27 changes: 27 additions & 0 deletions src/fs/findByExt.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,34 @@
import os from 'os';
import { readdir } from 'node:fs/promises';

const findByExt = async () => {
// Write your code here
// Recursively find all files with specific extension
// Parse --ext CLI argument (default: .txt)
let ext = ".txt"
if (process.argv.length > 1) {
const idx = process.argv.indexOf("--ext")
if (idx > -1 && idx < process.argv.length-1) {
ext = process.argv[idx + 1]
}
}

const sep = (os.platform() == 'win32') ? '\\' : '/';
let files

try {
files = await readdir('workspace', { withFileTypes: true, recursive: true });
} catch (error) {
console.log('FS operation failed')
process.exit(1)
}

let filteredFiles = files.filter(f => f.isFile() && f.name.endsWith(ext)).map(f => f.parentPath + sep + f.name)
filteredFiles.sort()
for (let fileName of filteredFiles) {
console.log(fileName)
}
};


await findByExt();
51 changes: 51 additions & 0 deletions src/fs/merge.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,59 @@
import os from 'os';
import { readdir, readFile, writeFile } from 'node:fs/promises';

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 sep = (os.platform() == 'win32') ? '\\' : '/';
const workspace = 'workspace' + sep + "parts";
let files

try {
files = await readdir(workspace, { withFileTypes: true, recursive: true });
} catch (error) {
console.log('FS operation failed')
process.exit(1)
}

let filenames = null
if (process.argv.length > 1) {
const idx = process.argv.indexOf("--files")
if (idx > -1 && idx < process.argv.length-1) {
filenames = process.argv[idx + 1].split(",")
}
}

const contents = []
const promises = files.filter(f => f.name.endsWith(".txt") &&
(filenames == null || filenames != null && filenames.indexOf(f.name.replace(".txt", "")) > -1))
.map( f => readFile(f.parentPath + sep + f.name, {encoding: "utf-8"}).then(d => contents.push([f.name, d])));

await Promise.all(promises);

// check all found
if (filenames != null) {
const filteredNames = contents.map(c => c[0])
const missedCount = filenames.filter(f => filteredNames.indexOf(f + '.txt') == -1).length
if (missedCount > 0) {
console.log('FS operation failed')
process.exit(1)
}
}

// merge
let concate = "";
if (contents.length > 0) {
contents.sort()
for (let [_, content] of contents) {
concate += content
}
console.log(concate)
}
const mergedFileName = 'workspace' + sep + 'merged.txt'
await writeFile(mergedFileName, concate, 'utf8')
};

await merge();
58 changes: 54 additions & 4 deletions src/fs/restore.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,58 @@
import { readFile, writeFile, mkdir } from 'node:fs/promises';
import os from 'os';

const restore = async () => {
// Write your code here
// Read snapshot.json
// Treat snapshot.rootPath as metadata only
// Recreate directory/file structure in workspace_restored
let sep = (os.platform() == 'win32') ? '\\' : '/';
let fileName = 'snapshot.json';
// read content
let snapshot;
try {
snapshot = await readFile(fileName, { encoding: 'utf8' })
} catch (err) {
console.log('FS operation failed')
process.exit(1)
}

try {
snapshot = JSON.parse(snapshot)
} catch (err) {
console.log('Json content expected')
process.exit(1)
}

// validate structure
if (snapshot.rootPath == undefined) {
console.log('wrong json structure: rootPath is not declared')
process.exit(1)
}

if (snapshot.entries == undefined) {
console.log('wrong json structure: entries is not declared')
process.exit(1)
}

if (snapshot.entries == undefined || snapshot.entries.length == 0) {
return
}

const basePath = 'workspace_restored'
try {
await mkdir(basePath)
} catch (err) {
console.log('FS operation failed')
process.exit(1)
}

const promises = snapshot.entries
.filter(f => f.type == 'directory')
.map(d => mkdir(basePath + sep + d.path, {recursive: true}))
await Promise.all(promises);


const filePromises = snapshot.entries
.filter(f => f.type == 'file')
.map(f => writeFile(basePath + sep + f.path, atob(f.content), 'utf8'))
await Promise.all(filePromises);
};

await restore();
39 changes: 39 additions & 0 deletions src/fs/snapshot.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,48 @@
import { readdir, readFile, writeFile } from 'node:fs/promises';
import os from 'os';

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
// Check if the file is readable.

const userHomeDir = os.homedir();
let sep = (os.platform() == 'win32') ? '\\' : '/';
let workspace = 'workspace';

let files
try {
files = await readdir(workspace, { withFileTypes: true, recursive: true });
} catch (error) {
console.log('FS operation failed')
process.exit(1)
}

var fileContents = []
var promises = []

for (const file of files) {

let relativePath = file.parentPath + sep + file.name
const relativeFilePath = relativePath.replace(workspace + sep, '')
// console.log(file);
if (file.isDirectory()) {
fileContents.push({ path: relativeFilePath, type: 'directory' })
} else {
const promise = readFile(relativePath)
.then(data => {
fileContents.push({path: relativeFilePath, type: 'file', size: data.length, content: Buffer.from(data).toString('base64')})
})
promises.push(promise)
}
}
await Promise.all(promises);
// prepare final object
let shapshotContent = JSON.stringify({ rootPath: process.cwd() + sep + workspace, entries: fileContents })
await writeFile('snapshot.json', shapshotContent, 'utf8');
};

await snapshot();
Loading