Skip to content

feat: add HttpFs, mount() helper, and graceful redirection error handling#127

Open
jcourson-bg wants to merge 4 commits intovercel-labs:mainfrom
jcourson-bg:cursor/bash-in-memory-filesystem-d243
Open

feat: add HttpFs, mount() helper, and graceful redirection error handling#127
jcourson-bg wants to merge 4 commits intovercel-labs:mainfrom
jcourson-bg:cursor/bash-in-memory-filesystem-d243

Conversation

@jcourson-bg
Copy link
Copy Markdown

@jcourson-bg jcourson-bg commented Mar 4, 2026

Example

import { Bash, HttpFs, mount } from "just-bash";

const fs = mount({
  "/data": new HttpFs("https://cdn.example.com/dataset", [
    "train.csv",
    "test.csv",
    "metadata.json",
  ]),
});

const bash = new Bash({ fs });

await bash.exec("wc -l /data/train.csv");       // fetches once, then cached
await bash.exec("cat /data/metadata.json | jq .name"); // reads from cache
await bash.exec("ls /data");                     // from manifest, no network
await bash.exec("echo x > /data/new.txt");       // bash: /data/new.txt: Read-only file system

What's in here

  • HttpFs — read-only filesystem backed by HTTP. You declare the file paths up front (a manifest), and they're fetched lazily on first read, then cached. Supports custom headers and a custom fetch function for auth, proxies, etc.
  • mount() helper — shorthand for composing filesystems. Creates an initialised InMemoryFs base (with /dev, /proc, /bin) automatically when you don't provide "/".
  • Graceful redirection errors — redirections (>, >>, &>, etc.) now catch FS write errors and produce bash-style messages (bash: file: Read-only file system) instead of propagating raw JS errors. Without this, echo x > /mounted/file on a read-only mount throws an unhandled error instead of behaving like real bash.
  • MountableFs sync helpers — added mkdirSync, writeFileSync, and writeFileLazy delegation so initFilesystem() and registerCommand() work when the top-level FS is a MountableFs.

Design decisions

  • Manifest-based — HTTP has no standard directory listing, so HttpFs requires declaring file paths up front. This means readdir/ls/exists work without network calls, and commands like grep -r won't trigger surprise fetches.
  • Read-only — all write operations throw EROFS. For read-write over HTTP, compose with MountableFs (writable InMemoryFs base + read-only HttpFs mount).
  • Object.create(null)FS_ERROR_LABELS and reqHeaders use null-prototype objects per the project's prototype pollution guidelines.

Known follow-up

Command-level error messages (mkdir, rm, etc.) still surface raw JS errors like EROFS: read-only file system, mkdir '/path' rather than bash-style messages. Separate concern from redirection handling — can be addressed in a follow-up.

Made with Cursor

cursoragent and others added 4 commits March 4, 2026 15:54
HttpFs is a read-only IFileSystem backed by HTTP fetch. Files are declared
up front as a manifest and fetched lazily on first read, then cached in
memory. Directory structure is derived from file paths -- no server-side
directory listing required. Zero dependencies (uses globalThis.fetch).

mount() is a one-liner to compose multiple IFileSystem instances into a
unified namespace via MountableFs. Automatically initializes the base
InMemoryFs with /dev, /proc, /bin when no custom base is provided.

Also adds mkdirSync/writeFileSync/writeFileLazy delegation to MountableFs
so that initFilesystem() and registerCommand() work transparently when
Bash receives a MountableFs.

Usage:
  const fs = mount({
    '/data': new HttpFs('https://cdn.example.com/dataset', [
      'train.csv', 'test.csv', 'metadata.json',
    ]),
  });
  const bash = new Bash({ fs });
  await bash.exec('cat /data/train.csv | wc -l');

Co-authored-by: James Courson <jcourson8@users.noreply.github.com>
Every writeFile/appendFile call in redirections.ts now goes through
redirectWrite() or redirectAppend() -- two helpers that catch FS errors
and return bash-style error strings. No raw FS writes, no catch-all
try-catches.

When a redirect target is on a read-only filesystem (or any FS that
rejects writes), the shell reports it the same way real bash does:

  $ echo hello > /readonly/file
  bash: /readonly/file: Read-only file system

Covers all redirect operators: >, >>, >|, >&, &>, &>>, and persistent
FD redirections from exec.

Co-authored-by: James Courson <jcourson8@users.noreply.github.com>
- README.md: Add HttpFs and mount() sections to Filesystem Options
- CLAUDE.md: Update Filesystem module listing, add composition guide,
  add redirect error handling guidance for contributors

Co-authored-by: James Courson <jcourson8@users.noreply.github.com>
…assertion

- FS_ERROR_LABELS in redirections.ts now uses Object.create(null) to
  comply with prototype pollution lint rule
- CLI test updated to assert "Read-only file system" (bash-style message)
  instead of raw "EROFS" for redirection write errors

Made-with: Cursor
@vercel
Copy link
Copy Markdown

vercel bot commented Mar 4, 2026

@jcourson-bg is attempting to deploy a commit to the Vercel Labs Team on Vercel.

A member of the Team first needs to authorize it.

@cramforce
Copy link
Copy Markdown
Contributor

I think this would be better as a sibling package

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants