@bandf/fs
v1.2.0
Published
Named-mount virtual filesystem for local, S3, and mirror storage.
Readme
@bandf/fs
Named-mount virtual filesystem for local, S3, and mirror storage.
import { homedir } from 'node:os';
import { join } from 'node:path';
import { openFs } from '@bandf/fs';
const fs = await openFs({
s3: { region: `us-east-1` }
});
await fs.mount({ name: `docs`, type: `local`, path: join(homedir(), `Documents`), mode: `rw` });
await fs.mount({ name: `archive`, type: `s3`, bucket: `my-bucket`, prefix: `docs/`, mode: `rw` });
const listing = await fs.ls({ address: `docs:**/*.md` });
const file = await fs.read({ address: `docs:notes/plan.md` });
// file is a VFile: file.value, file.path, file.data.fs = { mount, address, mode, ... }What it is
- Mounts: lowercase names mapped to
local,s3, ormirrorstorage, each read-only (ro) or read-write (rw). The durable mount table lives at~/.bandf/fs/mounts.json(BANDF_HOMEaware). Session mounts can also be supplied programmatically. - Addresses:
mount:relative/path. The relative part is normalized and jailed: no..segments, no absolute injection, no backslashes. An empty relative part is the mount root. URL schemes (http,https,file, ...) are reserved mount names, so an address can never be confused with a URL. - Records:
read()returns a vfile with the mount provenance stamped ondata.fs({ mount, address, relative, mode, bytes, mtimeMs, encoding }). Writes accept a VFile,{ value }, string, or Buffer. - S3: pass AWS S3 client config as
s3when callingopenFs()orcreateFs(). Mounts supply the bucket and prefix. - Mirror: mirror mounts commit after local and S3 both succeed. Reads, listing, and stat use local.
Mount Types
const fs = await openFs({
s3: { region: `us-east-1` }
});
await fs.mount({
name: `docs`,
type: `local`,
path: join(homedir(), `Documents`),
mode: `rw`
});
await fs.mount({
name: `archive`,
type: `s3`,
bucket: `my-bucket`,
prefix: `docs/`,
mode: `rw`
});
await fs.mount({
name: `docsMirror`,
type: `mirror`,
mode: `rw`,
local: { path: join(homedir(), `Documents`) },
s3: { bucket: `my-bucket`, prefix: `docs/` }
});API
| Call | Returns |
| --- | --- |
| openFs({ configPath?, issues?, s3? }) | instance with every persisted mount attached; bad entries land in issues |
| createFs({ mounts?, configPath?, issues?, s3? }) | instance from a programmatic mount table |
| mounts() / getMount({ name }) | mount records with type, mode, persisted, and backend fields |
| mount({ name, type, ...options, mode, persist }) | attach storage; persist writes the config |
| unmount({ name, persist }) | detach; persist removes it from the config |
| resolve({ address }) | local mounts return path; S3 and mirror mounts return bucket and key |
| exists({ address }) / stat({ address }) | storage metadata; local final symlinks can be reported |
| ls({ address, glob?, limit? }) | { mount, entries, truncated }; entries carry addresses |
| read({ address, encoding? }) | VFile (encoding: null for a Buffer value) |
| write({ address, file\|value, encoding?, makeParents? }) | { address, bytes, created, path?, bucket?, key? }; rw mounts only |
| remove({ address }) | rw only; refuses the mount root |
| copy({ from, to, overwrite? }) | source any mode, destination rw |
| move({ from, to, overwrite? }) | both ends rw |
Security posture
The jail rejects .. and absolute path escapes. Local mounts reject symlinks for file
operations. Enumeration never follows symbolic links and skips dotfiles.
Config
The s3 option is passed to new S3Client(s3). Use it for AWS client values such
as region, credentials, endpoint, and forcePathStyle.
~/.bandf/fs/mounts.json, created when missing:
{
"version": 1,
"mounts": {
"bandf": { "type": "local", "path": "/Users/you/.bandf", "mode": "ro" }
}
}Writes are atomic (tmp + rename, mode 600) and preserve unknown keys. A corrupt file throws and is never overwritten.
