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: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ npm-debug.log*
workspace/
workspace_restored/
snapshot.json
data.json
# data.json
checksums.json
source.txt
chunk_*.txt
package-lock.json
1 change: 1 addition & 0 deletions data.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[42, 17, 89, 5, 73, 31, 56, 92, 11, 68, 48, 76, 9, 61, 38, 81, 22, 57, 94, 14]
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
"cli:interactive": "node src/cli/interactive.js",
"cli:progress": "node src/cli/progress.js",
"modules:dynamic": "node src/modules/dynamic.js uppercase",
"modules:dynamic:uppercase": "node src/modules/dynamic.js uppercase",
"modules:dynamic:reverse": "node src/modules/dynamic.js reverse",
"modules:dynamic:repeat": "node src/modules/dynamic.js repeat",
"hash:verify": "node src/hash/verify.js",
"streams:lineNumberer": "echo 'hello\nworld' | node src/streams/lineNumberer.js",
"streams:filter": "echo 'hello\nworld\ntest' | node src/streams/filter.js --pattern test",
Expand Down
45 changes: 45 additions & 0 deletions src/cli/interactive.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,53 @@
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,
prompt: '> ',
});

const formatUptime = (secondsTotal) => {
const hours = Math.floor(secondsTotal / 3600);
const minutes = Math.floor((secondsTotal % 3600) / 60);
const seconds = secondsTotal % 60;

return `${hours}h ${minutes}m ${seconds}s`;
};

rl.prompt();

rl.on('line', (line) => {
const command = line.trim().toLowerCase();

if (command === 'uptime') {
const uptimeInSeconds = Math.floor(process.uptime());
console.log(formatUptime(uptimeInSeconds));
} else if (command === 'cwd') {
console.log(process.cwd());
} else if (command === 'date') {
console.log(new Date().toString());
} else if (command === 'exit') {
rl.close();
return;
} else if (command !== '') {
console.log('Unknown command');
}

rl.prompt();
});

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

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

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

let step = 0;

const render = () => {
const percent = Math.round((step / totalSteps) * 100);
const filled = Math.round((percent / 100) * barWidth);
const empty = barWidth - filled;
const bar = `${'█'.repeat(filled)}${' '.repeat(empty)}`;

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

render();

const timer = setInterval(() => {
step += 1;
render();

if (step >= totalSteps) {
clearInterval(timer);
process.stdout.write('\n');
}
}, intervalMs);
};

progress();
29 changes: 28 additions & 1 deletion src/cp/execCommand.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,37 @@
import { spawn } from 'node:child_process';

const execCommand = () => {
// Write your code here
// 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.slice(2).join(' ').trim();

if (!command) {
process.exit(1);
}

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

child.on('error', () => {
process.exit(1);
});

child.on('exit', (code, signal) => {
if (signal) {
process.kill(process.pid, signal);
return;
}

process.exit(code ?? 1);
});
};

execCommand();
32 changes: 32 additions & 0 deletions src/fs/findByExt.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,39 @@
import { readdir } 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)
const extFlagIndex = process.argv.indexOf('--ext');
const rawExt = extFlagIndex >= 0 ? process.argv[extFlagIndex + 1] : '.txt';
const extension = rawExt ? (rawExt.startsWith('.') ? rawExt : `.${rawExt}`) : '.txt';
const rootDir = process.cwd();
const matchedFiles = [];

const scanDirectory = async (currentDir) => {
const entries = await readdir(currentDir, { withFileTypes: true });

for (const entry of entries) {
const entryPath = path.join(currentDir, entry.name);

if (entry.isDirectory()) {
await scanDirectory(entryPath);
} else if (entry.isFile() && path.extname(entry.name).toLowerCase() === extension.toLowerCase()) {
matchedFiles.push(path.relative(rootDir, entryPath).split(path.sep).join('/'));
}
}
};

try {
await scanDirectory(rootDir);
matchedFiles.sort((a, b) => a.localeCompare(b));
matchedFiles.forEach((filePath) => {
console.log(filePath);
});
} catch {
throw new Error('FS operation failed');
}
};

await findByExt();
44 changes: 44 additions & 0 deletions src/fs/merge.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,52 @@
import { debug } from 'node:console';
import { readdir, readFile, writeFile } from 'node:fs/promises';
import path from 'node:path';
import { fileURLToPath } from 'node:url';

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 currentFilePath = fileURLToPath(import.meta.url);
const currentDir = path.dirname(currentFilePath);
const partsDir = path.resolve(currentDir, '../../workspace/parts');
const targetFile = path.resolve(currentDir, '../../workspace/merged.txt');

const filesFlagIndex = process.argv.indexOf('--files');
//debugger;

try {
let filenames;

if (filesFlagIndex >= 0) {
const rawList = process.argv[filesFlagIndex + 1] ?? '';
filenames = rawList
.split(',')
.map((name) => name.trim())
.filter(Boolean);

if (filenames.length === 0) {
throw new Error('FS operation failed');
}
} else {
const entries = await readdir(partsDir, { withFileTypes: true });
filenames = entries
.filter((entry) => entry.isFile() && entry.name.toLowerCase().endsWith('.txt'))
.map((entry) => entry.name)
.sort((a, b) => a.localeCompare(b));
}

const chunks = [];
for (const filename of filenames) {
const filePath = path.join(partsDir, filename);
chunks.push(await readFile(filePath, 'utf8'));
}
//debugger;
await writeFile(targetFile, chunks.join(''), 'utf8');
} catch {
throw new Error('FS operation failed');
}
};

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

const restore = async () => {
// Write your code here
// Read snapshot.json
// Treat snapshot.rootPath as metadata only
// Recreate directory/file structure in workspace_restored
const currentFilePath = fileURLToPath(import.meta.url);
const currentDir = path.dirname(currentFilePath);
const snapshotPath = path.resolve(currentDir, '../../snapshot.json');
const restoreRoot = path.resolve(currentDir, '../../workspace_restored');

try {
const rawSnapshot = await readFile(snapshotPath, 'utf8');
const snapshot = JSON.parse(rawSnapshot);

if (!snapshot || !Array.isArray(snapshot.entries)) {
throw new Error('Invalid snapshot format');
}

await rm(restoreRoot, { recursive: true, force: true });
await mkdir(restoreRoot, { recursive: true });

for (const entry of snapshot.entries) {
if (!entry || typeof entry.path !== 'string' || typeof entry.type !== 'string') {
throw new Error('Invalid snapshot entry');
}

const normalizedRelativePath = path.normalize(entry.path);
const targetPath = path.resolve(restoreRoot, normalizedRelativePath);

if (targetPath !== restoreRoot && !targetPath.startsWith(`${restoreRoot}${path.sep}`)) {
throw new Error('Invalid snapshot entry path');
}

if (entry.type === 'directory') {
await mkdir(targetPath, { recursive: true });
} else if (entry.type === 'file') {
const parentDir = path.dirname(targetPath);
await mkdir(parentDir, { recursive: true });
const content = typeof entry.content === 'string' ? Buffer.from(entry.content, 'base64') : Buffer.alloc(0);
await writeFile(targetPath, content);
} else {
throw new Error('Invalid snapshot entry type');
}
}
} catch {
throw new Error('FS operation failed');
}
};

await restore();
53 changes: 52 additions & 1 deletion src/fs/snapshot.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,60 @@
import { readdir, readFile, stat, writeFile } from 'node:fs/promises';
import path from 'node:path';
import { fileURLToPath } from 'node:url';

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 currentFilePath = fileURLToPath(import.meta.url);
const currentDir = path.dirname(currentFilePath);
const workspaceRoot = path.resolve(currentDir, '../../workspace');
const snapshotPath = path.resolve(currentDir, '../../snapshot.json');
debugger;
const entries = [];

const toSnapshotPath = (absolutePath) => path.relative(workspaceRoot, absolutePath).split(path.sep).join('/');

const scanDirectory = async (directory) => {
const items = await readdir(directory, { withFileTypes: true });
items.sort((firstItem, secondItem) => firstItem.name.localeCompare(secondItem.name));

for (const item of items) {
const itemPath = path.join(directory, item.name);
const relativePath = toSnapshotPath(itemPath);

if (item.isDirectory()) {
entries.push({
path: relativePath,
type: 'directory',
});
await scanDirectory(itemPath);
} else if (item.isFile()) {
const fileStats = await stat(itemPath);
const content = await readFile(itemPath);

entries.push({
path: relativePath,
type: 'file',
size: fileStats.size,
content: content.toString('base64'),
});
}
}
};

try {
await scanDirectory(workspaceRoot);
const snapshotData = {
rootPath: workspaceRoot,
entries,
};

await writeFile(snapshotPath, JSON.stringify(snapshotData, null, 2), 'utf8');
} catch {
throw new Error('FS operation failed');
}
};

await snapshot();
Loading