Unified filesystem abstraction for browser storage (OPFS, IndexedDB)
npm install @siglum/filesystemUnified filesystem abstraction for browser storage with automatic fallback.
``bash`
npm install @siglum/filesystem
`typescript
import { 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:
`typescript
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'`
`typescript
// 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' })
`
`typescript
import { isOPFSAvailable } from '@siglum/filesystem'
if (isOPFSAvailable()) {
console.log('OPFS is supported!')
}
`
`typescript`
// Get the backend type for a mounted path
const type = fileSystem.getBackendType('/documents') // 'opfs', 'indexeddb', or null
For direct control, import and mount backends explicitly:
`typescript
import { fileSystem, opfsBackend, indexedDBBackend } from '@siglum/filesystem'
fileSystem.mount('/documents', opfsBackend)
fileSystem.mount('/legacy', indexedDBBackend)
`
For reading multiple files efficiently:
`typescript`
const paths = ['/documents/a.txt', '/documents/b.txt', '/documents/c.txt']
const results = await fileSystem.readBinaryBatch(paths)
// Returns Map
For writing multiple files with progress tracking:
`typescript
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):
`typescript
// 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):
`typescript`
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
#### Mounting
- 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)
#### File Operations
- 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 (returns Map)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
#### Directory Operations
- mkdir(path) - Create directory (and parents)rmdir(path, options?)
- - Remove directory ({ recursive?: boolean })readdir(path)
- - List directory contents (returns FileEntry[])
#### Query Operations
- exists(path) - Check if path existsstat(path)
- - Get file/directory stats (returns FileStats)
#### Events
- subscribe(handler) - Subscribe to filesystem events, returns unsubscribe function
`typescript
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