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
65 changes: 65 additions & 0 deletions examples/virtual-fs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Virtual Filesystem Example

Demonstrates how to create synthetic filesystems whose content is generated at
runtime by async hooks. The shell never knows the content is virtual — it just
runs `ls`, `cat`, `grep`, `wc` as usual.

## Sources Included

- **reportDbSource** — simulates a project report database. Each report is
generated from in-memory records, as if fetched from a real DB.
- **metricsApiSource** — simulates a monitoring API. The directory tree
(`/cpu/<node>.txt`, `/memory/<node>.txt`, `/status.json`) is computed from
live node metrics.

## Running the Example

```bash
# Install dependencies
pnpm install

# Run the demo
pnpm start
```

## Creating Your Own Source

Use `defineVirtualFs` to create a typed factory:

```typescript
import { defineVirtualFs, VirtualFs, MountableFs, Bash } from "just-bash";

const mySource = defineVirtualFs((opts: { userId: string }) => ({
async readFile(path) {
// Return file content or null when not found
return path === "/hello.txt" ? `Hello, ${opts.userId}!` : null;
},
async readdir(path) {
// Return entries or null when not a directory
if (path === "/") {
return [{ name: "hello.txt", isFile: true, isDirectory: false }];
}
return null;
},
}));

const bash = new Bash({
fs: new MountableFs({
mounts: [
{ mountPoint: "/data", filesystem: new VirtualFs(mySource({ userId: "alice" })) },
],
}),
});

await bash.exec("cat /data/hello.txt"); // → Hello, alice!
```

## VirtualFsSource Hooks

| Hook | Required | Description |
|------|----------|-------------|
| `readFile(path)` | Yes | Return content (`string` or `Uint8Array`) or `null` |
| `readdir(path)` | Yes | Return `{ name, isFile, isDirectory }[]` or `null` |
| `stat(path)` | No | Return `FsStat` or `null` — derived from readdir/readFile when absent |
| `exists(path)` | No | Return `boolean` — derived from stat when absent |
| `dispose()` | No | Called by `VirtualFs.dispose()` to release resources |
108 changes: 108 additions & 0 deletions examples/virtual-fs/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/**
* Virtual Filesystem Example
*
* Demonstrates how to create synthetic filesystems whose content is
* generated at runtime by async hooks — the shell never knows the
* content is virtual.
* Run with: npx tsx main.ts
*/

import { Bash, MountableFs, VirtualFs } from "just-bash";
import { metricsApiSource, reportDbSource } from "./sources.js";

const bash = new Bash({
fs: new MountableFs({
mounts: [
{
mountPoint: "/reports",
filesystem: new VirtualFs(
reportDbSource({ userId: "alice" }),
),
},
{
mountPoint: "/metrics",
filesystem: new VirtualFs(
metricsApiSource({ cluster: "production" }),
),
},
],
}),
});

async function run(cmd: string) {
const result = await bash.exec(cmd);
console.log(`$ ${cmd}`);
if (result.stdout) process.stdout.write(result.stdout);
if (result.stderr) process.stderr.write(result.stderr);
console.log();
return result;
}

// ── demos ──────────────────────────────────────────────────

async function demoReports() {
console.log("=== 1. List sprint reports ===\n");
await run("ls /reports");

console.log("=== 2. Read a single report ===\n");
await run("cat /reports/sprint-24");

console.log("=== 3. Search for errors across all reports ===\n");
await run("grep ERROR /reports/sprint-23 /reports/sprint-24 /reports/sprint-25");

console.log("=== 4. Count lines per report ===\n");
await run("wc -l /reports/sprint-23 /reports/sprint-24 /reports/sprint-25");
}

async function demoMetrics() {
console.log("=== 5. Browse the metrics tree ===\n");
await run("ls /metrics");
await run("ls /metrics/cpu");

console.log("=== 6. Read cluster status ===\n");
await run("cat /metrics/status.json");

console.log("=== 7. Read individual node metrics ===\n");
await run("cat /metrics/cpu/node-2.txt");
await run("cat /metrics/memory/node-2.txt");

console.log("=== 8. Find critical nodes ===\n");
await run("grep critical /metrics/cpu/node-1.txt /metrics/cpu/node-2.txt /metrics/cpu/node-3.txt");
}

async function demoPipelines() {
console.log("=== 9. Pipeline: extract latency values ===\n");
await run("cat /reports/sprint-23 /reports/sprint-24 /reports/sprint-25 | grep latency");

console.log("=== 10. Pipeline: count warnings across all reports ===\n");
await run("cat /reports/sprint-23 /reports/sprint-24 /reports/sprint-25 | grep WARN | wc -l");
}

async function demoWriteHooks() {
console.log("=== 11. Write a new report (writeFile hook) ===\n");
await run("echo '# Sprint 26 — New Features' > /reports/sprint-26");
await run("ls /reports");
await run("cat /reports/sprint-26");

console.log("=== 12. Append to an existing report (appendFile hook) ===\n");
await run("echo '- P95 latency: 80ms' >> /reports/sprint-26");
await run("cat /reports/sprint-26");

console.log("=== 13. Delete a report (rm hook) ===\n");
await run("rm /reports/sprint-26");
await run("ls /reports");

console.log("=== 14. Write to metrics fails (no write hooks) ===\n");
const result = await bash.exec("echo test > /metrics/cpu/fake.txt").catch((e) => e);
console.log(`$ echo test > /metrics/cpu/fake.txt`);
console.log(`→ ${result instanceof Error ? result.message : "unexpected success"}\n`);
}

async function main() {
await demoReports();
await demoMetrics();
await demoPipelines();
await demoWriteHooks();
}

main().catch(console.error);
13 changes: 13 additions & 0 deletions examples/virtual-fs/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"name": "virtual-fs-example",
"version": "1.0.0",
"description": "Example of virtual filesystems backed by async hooks in just-bash",
"type": "module",
"scripts": {
"start": "npx tsx main.ts",
"typecheck": "npx tsc --noEmit"
},
"dependencies": {
"just-bash": "link:../.."
}
}
13 changes: 13 additions & 0 deletions examples/virtual-fs/pnpm-lock.yaml

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

Loading