Files
2026-05-27 23:47:53 +01:00

96 lines
8.1 KiB
Markdown

# @suaveplan/storage-browser-opfs - Browser OPFS AbstractStorage adapter for @suaveplan/storage: origin-scoped persistent file storage via navigator.storage.getDirectory(), AbortSignal everywhere, strict key sanitisation, native Web Streams, native partial reads + partial writes, DOMException → typed OpfsError mapping, full OTel surface.
Browser-tier `AbstractStorage` adapter backed by the Origin Private File System (OPFS). Provides persistent, origin-scoped key-value storage for browser consumers (vault snapshots, drive chunk caches, in-app i18n catalogues, SQLite-WASM backing files), with full `AbstractStorage` conformance, native Web Streams, native partial reads + partial writes, AbortSignal threading, Zod-validated options, structured `OpfsError` boundary, and a complete OpenTelemetry surface.
<!-- begin:auto-docs -->
## Documentation tree
- [filename-codec](./src/filename-codec/filename-codec.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [filename-codec](./src/filename-codec/filename-codec.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [in-memory-opfs](./src/in-memory-opfs/in-memory-opfs.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [in-memory-opfs](./src/in-memory-opfs/in-memory-opfs.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [key-sanitizer](./src/key-sanitizer/key-sanitizer.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [key-sanitizer](./src/key-sanitizer/key-sanitizer.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [opfs-adapter](./src/opfs-adapter/opfs-adapter.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [opfs-adapter](./src/opfs-adapter/opfs-adapter.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [opfs-storage](./src/opfs-storage/opfs-storage.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - opfs-storage.ops — Hosts pure / nearly-pure helpers…
- [opfs-storage.errors](./src/opfs-storage/opfs-storage.errors.md) — Encapsulates the mapping between WHATWG `DOMException` shapes thrown by the OPFS API and the typed `OpfsError` codes th…
- [opfs-storage](./src/opfs-storage/opfs-storage.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - opfs-storage.ops — Hosts pure / nearly-pure helpers…
- [opfs-storage.ops](./src/opfs-storage/opfs-storage.ops.md) — Hosts pure / nearly-pure helpers that `OpfsStorage` calls but that would push the class file over the 300-line hard bud…
- [opfs-storage.streams](./src/opfs-storage/opfs-storage.streams.md) — Factors the `createReadStream` / `createWriteStream` builders out of the main `OpfsStorage` class so the class stays un…
- [otel](./src/otel/otel.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [otel](./src/otel/otel.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [registry](./src/registry/registry.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [registry](./src/registry/registry.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [error-codes](./src/error-codes.md) — Single source of truth for every error code the `@suaveplan/storage-browser-opfs` package can throw. The registry uses…
<!-- end:auto-docs -->
- [filename-codec](./src/filename-codec/filename-codec.md) — <!-- begin:auto-docs --> _(no submodules)_- [in-memory-opfs](./src/in-memory-opfs/in-memory-opfs.md) — <!-- begin:auto-docs --> _(no submodules)_ - [in-memory-opfs](./src/in-memory-opfs/in-memory-opfs.md) — <!-- begin:auto-docs --> _(no submodules)_- [key-sanitizer](./src/key-sanitizer/key-sanitizer.md) — <!-- begin:auto-docs --> _(no submodules)_ - [key-sanitizer](./src/key-sanitizer/key-sanitizer.md) — <!-- begin:auto-docs --> _(no submodules)_- [opfs-adapter](./src/opfs-adapter/opfs-adapter.md) — <!-- begin:auto-docs --> _(no submodules)_ - [opfs-adapter](./src/opfs-adapter/opfs-adapter.md) — <!-- begin:auto-docs --> _(no submodules)_- [opfs-storage](./src/opfs-storage/opfs-storage.md) — <!-- begin:auto-docs --> _(no submodules)_ - [opfs-storage.errors](./src/opfs-storage/opfs-storage.errors.md) — Encapsulates the mapping between WHATWG `DOMException` shapes thrown by the OPFS API and the typed `OpfsError` codes th…
- [opfs-storage](./src/opfs-storage/opfs-storage.md) — <!-- begin:auto-docs --> _(no submodules)_ - [opfs-storage.ops](./src/opfs-storage/opfs-storage.ops.md) — Hosts pure / nearly-pure helpers that `OpfsStorage` calls but that would push the class file over the 300-line hard bud…
- [opfs-storage.streams](./src/opfs-storage/opfs-storage.streams.md) — Factors the `createReadStream` / `createWriteStream` builders out of the main `OpfsStorage` class so the class stays un…
- [otel](./src/otel/otel.md) — <!-- begin:auto-docs --> _(no submodules)_ - [otel](./src/otel/otel.md) — <!-- begin:auto-docs --> _(no submodules)_- [registry](./src/registry/registry.md) — <!-- begin:auto-docs --> _(no submodules)_ - [registry](./src/registry/registry.md) — <!-- begin:auto-docs --> _(no submodules)_- [error-codes](./src/error-codes.md) — Single source of truth for every error code the `@suaveplan/storage-browser-opfs` package can throw. The registry uses…
## Install
```bash
bun add @suaveplan/storage-browser-opfs
```
## Quick start
```ts
import {
createOpfsStorage,
isOpfsAvailable,
} from "@suaveplan/storage-browser-opfs";
if (!isOpfsAvailable()) {
throw new Error("This browser does not support OPFS");
}
const storage = createOpfsStorage({ directory: "myapp" });
await storage.set("user/42", new TextEncoder().encode("hello"));
const back = await storage.get("user/42");
```
## Factory integration
```ts
import { registerOpfsProvider } from "@suaveplan/storage-browser-opfs/registry";
import { createStorage } from "@suaveplan/storage/factory";
registerOpfsProvider();
const storage = createStorage({ type: "opfs", config: { directory: "myapp" } });
```
The package does NOT self-register at module-evaluation time — call `registerOpfsProvider()` explicitly so `sideEffects: false` bundlers can tree-shake the factory path when not used.
## Capabilities
| Capability | Value | Notes |
|---|---|---|
| `partialReads` | `true` | Via `File.slice(...).arrayBuffer()`. |
| `partialWrites` | `true` | Via `FileSystemWritableFileStream.seek()`. |
| `nativeStreams` | `true` | `File.stream()` for read; buffered close-on-commit for write. |
| `atomicWrites` | `false` | Close-as-commit semantics within a single realm; no cross-realm locking. |
| `contentAddressable` | `false` | Keys are caller-supplied strings. |
## Modules
- [`src/error-codes.ts`](./src/error-codes.md) — `OPFS_CODES` registry + `OpfsError`.
- [`src/key-sanitizer/key-sanitizer.ts`](./src/key-sanitizer/key-sanitizer.md) — strict key boundary.
- [`src/filename-codec/filename-codec.ts`](./src/filename-codec/filename-codec.md) — caller-key ↔ OPFS-filename round-trip.
- [`src/opfs-adapter/opfs-adapter.ts`](./src/opfs-adapter/opfs-adapter.md) — `defaultOpfs()` + `isOpfsAvailable()`.
- [`src/opfs-storage/opfs-storage.ts`](./src/opfs-storage/opfs-storage.md) — the `OpfsStorage` class.
- [`src/opfs-storage/opfs-storage.errors.ts`](./src/opfs-storage/opfs-storage.errors.md) — DOMException mapping.
- [`src/opfs-storage/opfs-storage.ops.ts`](./src/opfs-storage/opfs-storage.ops.md) — internal helpers.
- [`src/opfs-storage/opfs-storage.streams.ts`](./src/opfs-storage/opfs-storage.streams.md) — `ReadableStream`/`WritableStream` builders.
- [`src/otel/otel.ts`](./src/otel/otel.md) — OpenTelemetry instrument set.
- [`src/registry/registry.ts`](./src/registry/registry.md) — `registerOpfsProvider()`.
- [`src/in-memory-opfs/in-memory-opfs.ts`](./src/in-memory-opfs/in-memory-opfs.md) — real-implementation test double, exposed under `./in-memory-opfs` for downstream test packages.
- [`src/types/opfs-like.types.ts`](./src/types/opfs-like.types.md) — structural OPFS surface.
- [`src/types/opfs-storage.types.ts`](./src/types/opfs-storage.types.md) — option boundary.
## License
MIT.