---
title: "@unikvs/opfs"
description: "Learn how to use the storage plugin that saves byte sequences to the browser's OPFS."
---

## Overview [#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).

:::warning[About the runtime environment]
OPFS availability depends on the browser type, version, and settings. It is commonly used with Secure Contexts and Workers; check your browser docs for requirements.
:::

Install it as follows.

```package-install
npm install @unikvs/opfs
```

## Usage [#usage]

The class is named `Opfs`, available as both a default and a named export.

```ts
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()`.

```ts
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.

```ts
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 [#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 [#streams]

`Opfs` supports both writable and readable streams.

- `getWritable({ key })` returns a `WritableStream<Uint8Array<ArrayBuffer>>`. It returns the stream from `createWritable()` as is.
- `getReadable({ key })` returns a `ReadableStream<Uint8Array<ArrayBuffer>>`. It fetches the file and returns `File.stream()`.

```ts
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 [#notes]

- Operating before `open()` causes a runtime error. Always open first.
- On `write` failure, changes are discarded via `abort`. Partial writes never corrupt existing data.
- `exists` returns `false` for missing keys by catching a `NotFoundError` `DOMException`. Other errors are rethrown.
- Keys and root names follow file-name constraints. Invalid names raise a validation error.

:::note[About persistence and origins]
- OPFS is a private file system isolated per origin. Users cannot see it via normal file operations.
- Persistence is subject to browser storage management (e.g. eviction under pressure). Back up important data separately.
- Behavior may vary between browsers. Test in your target browsers.
:::

## Examples [#examples]

A minimal example using `Opfs` directly in the browser.

```ts
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 });
```
