Unified filesystem abstraction for browser storage with automatic fallback.
npm install @siglum/filesystemimport { fileSystem } from '@siglum/filesystem'
// Auto-select best backend (OPFS if available, else IndexedDB)
await fileSystem.mountAuto('/documents')
// Use unified API
await fileSystem.writeFile('/documents/main.tex', '\\documentclass{article}...')
const content = await fileSystem.readFile('/documents/main.tex')The mountAuto method automatically selects the best available backend:
import { fileSystem } from '@siglum/filesystem'
// Uses OPFS if available, falls back to IndexedDB
const backend = await fileSystem.mountAuto('/documents')
console.log(`Using ${backend.name} backend`) // 'opfs' or 'indexeddb'// Force OPFS (throws if not available)
await fileSystem.mountAuto('/documents', { backend: 'opfs' })
// Force IndexedDB
await fileSystem.mountAuto('/legacy', { backend: 'indexeddb' })
// Explicit auto (same as default)
await fileSystem.mountAuto('/data', { backend: 'auto' })import { isOPFSAvailable } from '@siglum/filesystem'
if (isOPFSAvailable()) {
console.log('OPFS is supported!')
}// Get the backend type for a mounted path
const type = fileSystem.getBackendType('/documents') // 'opfs', 'indexeddb', or nullFor direct control, import and mount backends explicitly:
import { fileSystem, opfsBackend, indexedDBBackend } from '@siglum/filesystem'
fileSystem.mount('/documents', opfsBackend)
fileSystem.mount('/legacy', indexedDBBackend)For reading multiple files efficiently:
const paths = ['/documents/a.txt', '/documents/b.txt', '/documents/c.txt']
const results = await fileSystem.readBinaryBatch(paths)
// Returns Map<string, Uint8Array> - missing files are omittedFor writing multiple files with progress tracking:
const entries = [
{ path: '/documents/a.txt', content: new Uint8Array([1, 2, 3]) },
{ path: '/documents/b.txt', content: new Uint8Array([4, 5, 6]) },
]
await fileSystem.writeBinaryBatch(entries, {
createParents: true,
onProgress: (completed, total) => console.log(`${completed}/${total}`),
concurrency: 20, // parallel writes (default: 20)
})For accessing the filesystem from Web Workers (including classic workers that can't use ES modules):
// In your worker
import {
readBinaryIDB,
writeBinaryIDB,
readBinaryOPFS,
writeBinaryOPFS,
isOPFSAvailable
} from '@siglum/filesystem/worker'
// Read/write directly without the full service
const data = await readBinaryIDB('/path/to/file')
await writeBinaryOPFS('/path/to/file', new Uint8Array([1, 2, 3]))Access the underlying storage identifiers (useful for direct IndexedDB/OPFS access):
import {
IDB_NAME, // 'siglum_filesystem'
IDB_VERSION, // 1
IDB_FILES_STORE, // 'files'
IDB_DIRS_STORE, // 'directories'
OPFS_ROOT // ''
} from '@siglum/filesystem/constants'Fast, persistent storage using the browser's Origin Private File System API.
- Best performance for large files
- Streaming support
Fallback for browsers without OPFS support.
- Universal browser support
- Slightly slower for large files
mount(path, backend)- Mount a backend at a pathmountAuto(path, options?)- Mount with automatic backend selectionunmount(path)- Unmount a backendgetMounts()- List all mount pointsisMounted(path)- Check if a path has a mounted backendgetBackendType(path)- Get backend type ('opfs' | 'indexeddb' | null)
readFile(path)- Read text contentwriteFile(path, content, options?)- Write text contentreadBinary(path)- Read binary datawriteBinary(path, data, options?)- Write binary datareadBinaryBatch(paths)- Read multiple files efficiently (returnsMap<string, Uint8Array>)writeBinaryBatch(entries, options?)- Write multiple files with progress trackingdeleteFile(path)- Delete a filecopyFile(src, dest)- Copy a filerename(oldPath, newPath)- Rename or move a file
mkdir(path)- Create directory (and parents)rmdir(path, options?)- Remove directory ({ recursive?: boolean })readdir(path)- List directory contents (returnsFileEntry[])
exists(path)- Check if path existsstat(path)- Get file/directory stats (returnsFileStats)
subscribe(handler)- Subscribe to filesystem events, returns unsubscribe function
interface FileStats {
size: number
isDirectory: boolean
isFile: boolean
mtime: Date
}
interface FileEntry {
name: string
path: string
isDirectory: boolean
}
type FileSystemEvent =
| { type: 'file:created'; path: string }
| { type: 'file:modified'; path: string }
| { type: 'file:deleted'; path: string }
| { type: 'directory:created'; path: string }
| { type: 'directory:deleted'; path: string }MIT