js/file/memory
js/file/memory.ts
fino:file/memory — an in-memory filesystem provider.
MemoryFileSystem implements the same FileSystem contract as
DiskFileSystem, so anything written against fino:file works against it
unchanged: the same Stat objects, the same File handles, the same
Entry/FileEntry/DirEntry listings, and the same errno-carrying errors.
Nothing it stores ever reaches a disk.
That makes it useful wherever a real filesystem is inconvenient — tests that
would otherwise need a temp directory, staging a tree before committing it,
or any caller written against FileSystem that needs an isolated instance.
import { MemoryFileSystem } from 'fino:file/memory';
const fs = new MemoryFileSystem({ '/etc/app.conf': 'debug=true' });
console.log(new TextDecoder().decode(await fs.readFile('/etc/app.conf')));
Parent directories of seeded files are created implicitly, which is what makes a one-line constructor a usable tree. Directories created later follow POSIX and require their parent to exist.
Timestamps come from Date.now() by default. Pass a now function to make
them something else — a counter, for instance, so that two runs of the same
sequence of operations produce identical metadata.
Interfaces
interface MemoryFileSystemOptions {
Settings for a MemoryFileSystem.
Properties
files?: Record<string, string | Uint8Array>
Files to seed the tree with, by absolute path. Parent directories are created as needed.
now?: () => number
Source of timestamps, in milliseconds. Defaults to Date.now.
A monotonic counter here makes metadata reproducible across repeated runs of the same operation sequence.
fileMode?: number
Permission bits reported for seeded and created files. Defaults to 0o644.
dirMode?: number
Permission bits reported for directories. Defaults to 0o755.
Classes
class MemoryFileSystem extends FileSystem {
A filesystem whose entire contents live in this isolate's heap.
Construction seeds the tree; everything after that goes through the ordinary
provider methods. Instances are independent — two MemoryFileSystems share
nothing, which is what makes one per test safe to use in parallel.
import { MemoryFileSystem } from 'fino:file/memory';
const fs = new MemoryFileSystem();
await fs.mkdir('/work');
await fs.writeFile('/work/notes.txt', new TextEncoder().encode('hello'));
const dir = await fs.dir('/work');
for (const entry of await dir.entries()) console.log(entry.name);Constructors
constructor(
files: Record<string, string | Uint8Array> | MemoryFileSystemOptions = {},
options: MemoryFileSystemOptions = {},
)
Methods
async stat(path: Path | string): Promise<Stat>
Stat a path, following symlinks.
import { MemoryFileSystem } from 'fino:file/memory';
const fs = new MemoryFileSystem({ '/a.txt': 'hi' });
console.log((await fs.stat('/a.txt')).size); // 2statSync(path: Path | string): Stat
Synchronous stat. Always available: there is no I/O to await.
async lstat(path: Path | string): Promise<Stat>
Stat a path without following symlinks.
import { MemoryFileSystem } from 'fino:file/memory';
const fs = new MemoryFileSystem({ '/a.txt': 'hi' });
console.log((await fs.lstat('/a.txt')).isSymlink()); // falseasync open(path: Path | string, mode: string = 'r'): Promise<FileHandle>
Open a file and return a handle.
Supports the same modes as DiskFileSystem: r, r+, w, w+, a,
a+, the internal c+, and an x suffix for exclusive creation.
import { MemoryFileSystem } from 'fino:file/memory';
const fs = new MemoryFileSystem();
const file = await fs.open('/log.txt', 'w');
await file.pwrite(0, new TextEncoder().encode('ready\n'));
await file.close();openSync(path: Path | string, mode: string = 'r'): FileHandle
Synchronous open, for callers such as the SQLite VFS that cannot await.
async dir(path: Path | string): Promise<DirEntry>
Open a directory handle.
import { MemoryFileSystem } from 'fino:file/memory';
const fs = new MemoryFileSystem({ '/work/a.txt': '' });
const dir = await fs.dir('/work');
console.log((await dir.entries()).length); // 1async entry(path: Path | string): Promise<Entry>
Build an Entry for any path, without following a trailing symlink.
import { MemoryFileSystem } from 'fino:file/memory';
const fs = new MemoryFileSystem({ '/a.txt': 'hi' });
console.log((await fs.entry('/a.txt')).isFile()); // trueasync readdir(path: Path | string): Promise<Entry[]>
List a directory's immediate children.
DirEntry iteration goes through this, which is how listings work without
a real directory stream underneath.
import { MemoryFileSystem } from 'fino:file/memory';
const fs = new MemoryFileSystem({ '/work/a.txt': '' });
console.log((await fs.readdir('/work')).map((e) => e.name)); // ['a.txt']async mkdir(path: Path | string, mode: number = 0o755): Promise<void>
Create a directory. The parent must already exist.
import { MemoryFileSystem } from 'fino:file/memory';
const fs = new MemoryFileSystem();
await fs.mkdir('/work', 0o700);async rmdir(path: Path | string): Promise<void>
Remove an empty directory.
import { MemoryFileSystem } from 'fino:file/memory';
const fs = new MemoryFileSystem();
await fs.mkdir('/scratch');
await fs.rmdir('/scratch');async unlink(path: Path | string): Promise<void>
Delete a file or symlink.
import { MemoryFileSystem } from 'fino:file/memory';
const fs = new MemoryFileSystem({ '/a.txt': 'hi' });
await fs.unlink('/a.txt');unlinkSync(path: Path | string): void
Synchronous unlink.
async rename(oldPath: Path | string, newPath: Path | string): Promise<void>
Rename a file or directory, moving any subtree with it.
import { MemoryFileSystem } from 'fino:file/memory';
const fs = new MemoryFileSystem({ '/a.txt': 'hi' });
await fs.rename('/a.txt', '/b.txt');async readlink(path: Path | string): Promise<string>
Read a symlink's target.
import { MemoryFileSystem } from 'fino:file/memory';
const fs = new MemoryFileSystem({ '/a.txt': 'hi' });
await fs.symlink('/a.txt', '/link');
console.log(await fs.readlink('/link')); // '/a.txt'async symlink(target: Path | string, linkpath: Path | string): Promise<void>
Create a symlink at linkpath pointing at target.
The target is stored verbatim and resolved on use, so a link may be created before the thing it names, exactly as on disk.
import { MemoryFileSystem } from 'fino:file/memory';
const fs = new MemoryFileSystem();
await fs.symlink('/not-yet', '/link');async link(existingPath: Path | string, newPath: Path | string): Promise<void>
Create a hard link. The two paths then hold the same node.
import { MemoryFileSystem } from 'fino:file/memory';
const fs = new MemoryFileSystem({ '/a.txt': 'hi' });
await fs.link('/a.txt', '/b.txt');async realpath(path: Path | string): Promise<string>
Resolve a path to its canonical form, expanding every symlink.
import { MemoryFileSystem } from 'fino:file/memory';
const fs = new MemoryFileSystem({ '/a/b.txt': 'hi' });
console.log(await fs.realpath('/a/./b.txt')); // '/a/b.txt'async access(path: Path | string, mode: number = F_OK): Promise<void>
Check a path exists, and optionally that its permission bits allow a mode.
import { MemoryFileSystem, F_OK } from 'fino:file/memory';
const fs = new MemoryFileSystem({ '/a.txt': 'hi' });
await fs.access('/a.txt', F_OK);async chmod(path: Path | string, mode: number): Promise<void>
Change a path's permission bits.
import { MemoryFileSystem } from 'fino:file/memory';
const fs = new MemoryFileSystem({ '/a.txt': 'hi' });
await fs.chmod('/a.txt', 0o600);async chown(path: Path | string, uid: number, gid: number): Promise<void>
Change a path's owner and group.
import { MemoryFileSystem } from 'fino:file/memory';
const fs = new MemoryFileSystem({ '/a.txt': 'hi' });
await fs.chown('/a.txt', 501, 20);async utimes(path: Path | string, atime: Date | number, mtime: Date | number): Promise<void>
Set a path's access and modification times.
import { MemoryFileSystem } from 'fino:file/memory';
const fs = new MemoryFileSystem({ '/a.txt': 'hi' });
await fs.utimes('/a.txt', 0, 0);async truncate(path: Path | string, size = 0): Promise<void>
Set a file's size, zero-filling any extension.
import { MemoryFileSystem } from 'fino:file/memory';
const fs = new MemoryFileSystem({ '/a.txt': 'hello' });
await fs.truncate('/a.txt', 2);async copyFile(src: Path | string, dest: Path | string): Promise<void>
Copy a file's contents to another path.
import { MemoryFileSystem } from 'fino:file/memory';
const fs = new MemoryFileSystem({ '/a.txt': 'hi' });
await fs.copyFile('/a.txt', '/b.txt');snapshot(): Record<string, string>
Every file's contents as text, keyed by path.
Handy for asserting on what a run wrote without walking the tree.
import { MemoryFileSystem } from 'fino:file/memory';
const fs = new MemoryFileSystem({ '/a.txt': 'hi' });
console.log(fs.snapshot()); // { '/a.txt': 'hi' }