@unikvs/opfs
Learn how to use the storage plugin that saves byte sequences to the browser's OPFS.
Overview
@unikvs/opfs is a storage plugin that uses the browser’s OPFS (Origin Private File System) as its persistence layer. It handles byte sequences (Uint8Array<ArrayBuffer>) only.Browser only
It assumes a browser environment (main thread or Web Worker).
Install it as follows.
npm install @unikvs/opfspnpm add @unikvs/opfsyarn add @unikvs/opfsbun add @unikvs/opfsnub add @unikvs/opfsaube add @unikvs/opfsUsage
The class is named Opfs, available as both a default and a named export.
import { Opfs } from "@unikvs/opfs";
const storage = new Opfs(".unikvs");
The constructor argument root is typed string | FileSystemDirectoryHandle and defaults to ".unikvs". A string is the root name inside OPFS; a handle itself becomes the working directory. Passing a handle counts as already open. The second argument options is optional, and options.allowRepair (boolean, defaults to false) controls whether repair writes are allowed. When false, write and getWritable with a repair marker (vars["unikvs:repair"] set to true) throw an error.
Call open() before use. With UniKvs, register with appendStorage then call kvs.open(). It internally calls Opfs.open().
import { UniKvs } from "unikvs";
import { Opfs } from "@unikvs/opfs";
const storage = new Opfs(".unikvs");
const kvs = UniKvs.config().appendStorage(storage).create();
await kvs.open();
// Read and write byte sequences.
await kvs.close();
When operating Opfs directly, use write, read, exists, delete, and clear. All require open() first.
import { Opfs } from "@unikvs/opfs";
const storage = new Opfs(".unikvs");
await storage.open();
await storage.write({ key: "data.bin", data: new Uint8Array([1, 2, 3]) });
const data = await storage.read({ key: "data.bin" });
const found = await storage.exists({ key: "data.bin" });
await storage.delete({ key: "data.bin" });
File Layout
Keys are treated as file names and stored one-to-one directly under the root. No subdirectories are created. Keys are validated with assertValidFilename.
When a string is passed, the root name is normalized as follows.
"",".", or"/"targets the OPFS root directly.- Consecutive
/collapse into one; leading/trailing/are removed. - Each segment is validated with
assertValidDirname.
open() obtains the origin via navigator.storage.getDirectory() and creates or retrieves each level with create: true. clear() deletes entries individually at the root, or removes and recreates a subdirectory.
Streams
Opfs supports both writable and readable streams.
getWritable({ key })returns aWritableStream<Uint8Array<ArrayBuffer>>. It returns the stream fromcreateWritable()as is.getReadable({ key })returns aReadableStream<Uint8Array<ArrayBuffer>>. It fetches the file and returnsFile.stream().
import { Opfs } from "@unikvs/opfs";
const storage = new Opfs(".unikvs");
await storage.open();
const writable = await storage.getWritable({ key: "large.bin" });
const writer = writable.getWriter();
await writer.write(new Uint8Array([1, 2, 3]));
await writer.close();
const readable = await storage.getReadable({ key: "large.bin" });
await readable.cancel();
Notes
- Operating before
open()causes a runtime error. Always open first. - On
writefailure, changes are discarded viaabort. Partial writes never corrupt existing data. existsreturnsfalsefor missing keys by catching aNotFoundErrorDOMException. Other errors are rethrown.- Keys and root names follow file-name constraints. Invalid names raise a validation error.
Examples
A minimal example using Opfs directly in the browser.
import { Opfs } from "@unikvs/opfs";
const storage = new Opfs(".unikvs");
await storage.open();
const key = "hello.bin";
await storage.write({ key, data: new TextEncoder().encode("hello") });
const loaded = await storage.read({ key });
console.log(new TextDecoder().decode(loaded));
console.log(await storage.exists({ key }));
await storage.delete({ key });