Overview
The VFS (Virtual File System) class provides a complete in-memory Unix-like filesystem with support for mounting external providers, content chunking for large files, and filesystem watching.
Creating a VFS
import { VFS } from '@lifo-sh/core' ;
const vfs = new VFS ();
Constructor
constructor ( contentStore ?: ContentStore )
Content store for chunked large files. Defaults to new ContentStore().
File Operations
readFile()
Read file contents as binary data.
readFile ( path : string ): Uint8Array
File contents as binary data
Example:
const vfs = new VFS ();
vfs . writeFile ( '/test.txt' , 'Hello' );
const data = vfs . readFile ( '/test.txt' );
console . log ( data ); // Uint8Array [72, 101, 108, 108, 111]
Throws: VFSError with code:
ENOENT - File or directory does not exist
EISDIR - Path is a directory, not a file
readFileString()
Read file contents as a UTF-8 string.
readFileString ( path : string ): string
File contents as UTF-8 string
Example:
vfs . writeFile ( '/config.json' , '{"key": "value"}' );
const content = vfs . readFileString ( '/config.json' );
console . log ( JSON . parse ( content )); // { key: 'value' }
writeFile()
Write file contents. Creates or overwrites the file.
writeFile ( path : string , content : string | Uint8Array ): void
content
string | Uint8Array
required
File content (string or binary data)
Example:
// Write text
vfs . writeFile ( '/hello.txt' , 'Hello, World!' );
// Write binary
const binary = new Uint8Array ([ 0x89 , 0x50 , 0x4E , 0x47 ]);
vfs . writeFile ( '/image.png' , binary );
// Overwrite existing file
vfs . writeFile ( '/hello.txt' , 'New content' );
Throws: VFSError with code:
ENOENT - Parent directory does not exist
EISDIR - Path is a directory
EINVAL - Path is on a read-only mounted filesystem
Note: Files >= 1MB are automatically chunked into the ContentStore.
appendFile()
Append content to a file. Creates the file if it doesn’t exist.
appendFile ( path : string , content : string | Uint8Array ): void
content
string | Uint8Array
required
Content to append
Example:
vfs . writeFile ( '/log.txt' , 'Line 1 \n ' );
vfs . appendFile ( '/log.txt' , 'Line 2 \n ' );
vfs . appendFile ( '/log.txt' , 'Line 3 \n ' );
console . log ( vfs . readFileString ( '/log.txt' ));
// Output:
// Line 1
// Line 2
// Line 3
exists()
Check if a path exists.
exists ( path : string ): boolean
True if the path exists (file or directory)
Example:
vfs . writeFile ( '/test.txt' , 'data' );
console . log ( vfs . exists ( '/test.txt' )); // true
console . log ( vfs . exists ( '/missing.txt' )); // false
stat()
Get file or directory metadata.
File or directory metadata Size in bytes (for files) or number of children (for directories)
Creation time (Unix timestamp in milliseconds)
Modification time (Unix timestamp in milliseconds)
Unix file mode (e.g., 0o644 for files, 0o755 for directories)
MIME type (files only, auto-detected from extension)
Example:
vfs . writeFile ( '/doc.pdf' , 'PDF data' );
const stat = vfs . stat ( '/doc.pdf' );
console . log ( stat . type ); // 'file'
console . log ( stat . size ); // 8
console . log ( stat . mime ); // 'application/pdf'
console . log ( new Date ( stat . mtime )); // 2026-03-01T...
Throws: VFSError with code ENOENT if path does not exist.
unlink()
Delete a file.
unlink ( path : string ): void
Example:
vfs . writeFile ( '/temp.txt' , 'data' );
vfs . unlink ( '/temp.txt' );
console . log ( vfs . exists ( '/temp.txt' )); // false
Throws: VFSError with code:
ENOENT - File does not exist
EISDIR - Path is a directory (use rmdir instead)
EINVAL - Path is on a read-only filesystem
rename()
Rename or move a file or directory.
rename ( oldPath : string , newPath : string ): void
Example:
vfs . writeFile ( '/old.txt' , 'data' );
vfs . rename ( '/old.txt' , '/new.txt' );
console . log ( vfs . exists ( '/old.txt' )); // false
console . log ( vfs . exists ( '/new.txt' )); // true
// Move between directories
vfs . mkdir ( '/src' );
vfs . mkdir ( '/dst' );
vfs . writeFile ( '/src/file.txt' , 'data' );
vfs . rename ( '/src/file.txt' , '/dst/file.txt' );
Throws: VFSError with code:
ENOENT - Source path does not exist
EINVAL - Cannot rename across mount boundaries
copyFile()
Copy a file.
copyFile ( src : string , dest : string ): void
Example:
vfs . writeFile ( '/original.txt' , 'data' );
vfs . copyFile ( '/original.txt' , '/copy.txt' );
console . log ( vfs . readFileString ( '/copy.txt' )); // 'data'
Throws: VFSError with code:
ENOENT - Source file does not exist
EISDIR - Source is a directory
touch()
Create an empty file or update the modification time of an existing file.
touch ( path : string ): void
Example:
vfs . touch ( '/new.txt' );
console . log ( vfs . exists ( '/new.txt' )); // true
console . log ( vfs . readFileString ( '/new.txt' )); // ''
vfs . writeFile ( '/existing.txt' , 'data' );
const before = vfs . stat ( '/existing.txt' ). mtime ;
vfs . touch ( '/existing.txt' );
const after = vfs . stat ( '/existing.txt' ). mtime ;
console . log ( after > before ); // true
Directory Operations
mkdir()
Create a directory.
mkdir ( path : string , options ?: { recursive? : boolean }): void
Create parent directories if they don’t exist
Example:
// Simple mkdir
vfs . mkdir ( '/test' );
// Recursive mkdir
vfs . mkdir ( '/a/b/c/d' , { recursive: true });
console . log ( vfs . exists ( '/a/b/c/d' )); // true
Throws: VFSError with code:
ENOENT - Parent directory does not exist (without recursive)
EEXIST - Directory already exists
ENOTDIR - Parent path is not a directory
EINVAL - Path is on a read-only filesystem
rmdir()
Remove an empty directory.
rmdir ( path : string ): void
Example:
vfs . mkdir ( '/empty' );
vfs . rmdir ( '/empty' );
console . log ( vfs . exists ( '/empty' )); // false
Throws: VFSError with code:
ENOENT - Directory does not exist
ENOTDIR - Path is not a directory
ENOTEMPTY - Directory is not empty
EINVAL - Path is on a read-only filesystem
rmdirRecursive()
Recursively remove a directory and all its contents.
rmdirRecursive ( path : string ): void
Directory path to remove recursively
Example:
vfs . mkdir ( '/project/src' , { recursive: true });
vfs . writeFile ( '/project/src/index.js' , 'code' );
vfs . writeFile ( '/project/README.md' , 'docs' );
vfs . rmdirRecursive ( '/project' );
console . log ( vfs . exists ( '/project' )); // false
Throws: VFSError with code:
ENOENT - Directory does not exist
ENOTDIR - Path is not a directory
readdir()
List directory contents.
readdir ( path : string ): Dirent []
Array of directory entries
Example:
vfs . mkdir ( '/dir' );
vfs . writeFile ( '/dir/a.txt' , 'A' );
vfs . writeFile ( '/dir/b.txt' , 'B' );
vfs . mkdir ( '/dir/subdir' );
const entries = vfs . readdir ( '/dir' );
for ( const entry of entries ) {
console . log ( entry . name , entry . type );
}
// Output:
// a.txt file
// b.txt file
// subdir directory
Throws: VFSError with code:
ENOENT - Directory does not exist
ENOTDIR - Path is not a directory
readdirStat()
List directory contents with full stat information.
readdirStat ( path : string ): Array < Dirent & Stat >
Array of directory entries with stat info
Example:
vfs . mkdir ( '/dir' );
vfs . writeFile ( '/dir/small.txt' , 'Hi' );
vfs . writeFile ( '/dir/large.txt' , 'x' . repeat ( 1000 ));
const entries = vfs . readdirStat ( '/dir' );
for ( const entry of entries ) {
console . log ( ` ${ entry . name } : ${ entry . size } bytes, ${ entry . type } ` );
}
// Output:
// small.txt: 2 bytes, file
// large.txt: 1000 bytes, file
Mount System
mount()
Mount a provider at an arbitrary path.
mount ( path : string , provider : VirtualProvider | MountProvider ): void
Virtual path where the provider will be mounted (e.g., “/proc”, “/mnt/project”)
provider
VirtualProvider | MountProvider
required
Provider implementing the VirtualProvider or MountProvider interface
Example:
import { VFS , NativeFsProvider } from '@lifo-sh/core' ;
import fs from 'node:fs' ;
const vfs = new VFS ();
// Mount a native filesystem directory (Node.js only)
const provider = new NativeFsProvider ( '/home/user/project' , fs );
vfs . mount ( '/mnt/project' , provider );
// Now VFS operations on /mnt/project/* delegate to the native filesystem
const files = vfs . readdir ( '/mnt/project' );
unmount()
Unmount a previously mounted provider.
unmount ( path : string ): void
Virtual path that was passed to mount()
Example:
vfs . unmount ( '/mnt/project' );
Throws: VFSError with code EINVAL if path is not mounted.
registerProvider()
Backward-compatible alias for mount().
registerProvider ( prefix : string , provider : VirtualProvider ): void
Filesystem Watching
watch()
Watch for filesystem changes.
// Global watch (all changes)
watch ( listener : VFSWatchListener ): () => void
// Scoped watch (changes under a specific path)
watch ( path : string , listener : VFSWatchListener ): () => void
Path to watch (if omitted, watches all changes)
Callback function invoked on filesystem changes
Function to stop watching
Example:
// Watch all changes
const unwatch = vfs . watch (( event ) => {
console . log ( event . type , event . path );
});
vfs . writeFile ( '/test.txt' , 'data' ); // Logs: create /test.txt
vfs . writeFile ( '/test.txt' , 'new' ); // Logs: modify /test.txt
vfs . unlink ( '/test.txt' ); // Logs: delete /test.txt
unwatch (); // Stop watching
// Watch specific directory
const unwatchHome = vfs . watch ( '/home/user' , ( event ) => {
console . log ( 'Home changed:' , event . type , event . path );
});
vfs . writeFile ( '/home/user/file.txt' , 'data' ); // Triggers callback
vfs . writeFile ( '/tmp/other.txt' , 'data' ); // Does not trigger
Event types:
interface VFSWatchEvent {
type : 'create' | 'modify' | 'delete' | 'rename' ;
path : string ; // Affected path
oldPath ?: string ; // Previous path (rename events only)
fileType : 'file' | 'directory' ;
}
Serialization
getRoot()
Get the root INode for serialization.
Example:
const root = vfs . getRoot ();
const serialized = JSON . stringify ( root );
loadFromSerialized()
Load filesystem from a serialized root node.
loadFromSerialized ( root : INode ): void
Example:
const root = JSON . parse ( serialized );
const vfs = new VFS ();
vfs . loadFromSerialized ( root );
Type Definitions
interface Stat {
type : 'file' | 'directory' ;
size : number ;
ctime : number ; // Creation time (ms since epoch)
mtime : number ; // Modification time (ms since epoch)
mode : number ; // Unix file mode (e.g., 0o644)
mime ?: string ; // MIME type (files only)
}
interface Dirent {
name : string ;
type : 'file' | 'directory' ;
}
interface VFSWatchEvent {
type : 'create' | 'modify' | 'delete' | 'rename' ;
path : string ;
oldPath ?: string ; // Only for 'rename' events
fileType : 'file' | 'directory' ;
}
type VFSWatchListener = ( event : VFSWatchEvent ) => void ;
class VFSError extends Error {
code : 'ENOENT' | 'EEXIST' | 'ENOTDIR' | 'EISDIR' | 'ENOTEMPTY' | 'EINVAL' ;
}
Content Chunking
Files >= 1MB are automatically chunked into the ContentStore:
Small files (< 1MB): Stored inline in the INode
Large files (>= 1MB): Split into 256KB chunks, stored in ContentStore
This optimization reduces memory usage for large files while keeping small files fast.
Example:
const vfs = new VFS ();
// Small file - stored inline
vfs . writeFile ( '/small.txt' , 'x' . repeat ( 100_000 )); // 100 KB
// Large file - automatically chunked
vfs . writeFile ( '/large.txt' , 'x' . repeat ( 2_000_000 )); // 2 MB
const stat = vfs . stat ( '/large.txt' );
console . log ( stat . size ); // 2000000 (size is still accurate)
// Reading works transparently
const content = vfs . readFileString ( '/large.txt' );
console . log ( content . length ); // 2000000
Source Location
// src/kernel/vfs/VFS.ts (604 lines)
export class VFS {
constructor ( contentStore ?: ContentStore );
// File operations
readFile ( path : string ) : Uint8Array ;
readFileString ( path : string ) : string ;
writeFile ( path : string , content : string | Uint8Array ) : void ;
appendFile ( path : string , content : string | Uint8Array ) : void ;
exists ( path : string ) : boolean ;
stat ( path : string ) : Stat ;
unlink ( path : string ) : void ;
rename ( oldPath : string , newPath : string ) : void ;
copyFile ( src : string , dest : string ) : void ;
touch ( path : string ) : void ;
// Directory operations
mkdir ( path : string , options ?: { recursive ?: boolean }) : void ;
rmdir ( path : string ) : void ;
rmdirRecursive ( path : string ) : void ;
readdir ( path : string ) : Dirent [];
readdirStat ( path : string ) : Array < Dirent & Stat >;
// Mount system
mount ( path : string , provider : VirtualProvider | MountProvider ) : void ;
unmount ( path : string ) : void ;
registerProvider ( prefix : string , provider : VirtualProvider ) : void ;
// Watching
watch ( listener : VFSWatchListener ) : () => void ;
watch ( path : string , listener : VFSWatchListener ) : () => void ;
// Serialization
getRoot () : INode ;
loadFromSerialized ( root : INode ) : void ;
}