2.9 KiB
filename-codec — caller key ↔ OPFS filename
(no submodules)
Purpose
OPFS filenames inherit filesystem-level constraints that caller keys must not (case-insensitivity on some platforms, no embedded / separator within a single entry name, byte-encoding ambiguity across browsers). This module wraps each key in RFC 4648 base64url over its UTF-8 byte representation, producing OPFS-safe filenames that round-trip every caller key — including unicode, mixed scripts, slashes, and any byte the caller threw at us. Storing the codec as a separate module keeps the algorithm independently testable (property-based round-trip suite) and replaceable (a future hash-addressed mode can plug in without touching OpfsStorage).
Features
- RFC 4648 base64url with
+→-,/→_, padding stripped. - UTF-8 fatal decode rejects malformed byte sequences with a typed
OpfsError. - Round-trip invariant proven by a
fast-checkproperty test (200 random inputs). - Output regex:
^[A-Za-z0-9_-]*$— accepted by every OPFS implementation. - Pure / synchronous / zero workspace dependencies.
Usage: Basic Example
import {
decodeFilename,
encodeFilename,
} from "@suaveplan/storage-browser-opfs/filename-codec";
const filename = encodeFilename("orders/2026/05.json"); // "b3JkZXJzLzIwMjYvMDUuanNvbg"
const original = decodeFilename(filename); // "orders/2026/05.json"
Usage: Advanced Example
import {
decodeFilename,
encodeFilename,
OPFS_CODES,
OpfsError,
} from "@suaveplan/storage-browser-opfs";
function listKeysSafely(entries: { name: string }[]): string[] {
const out: string[] = [];
for (const entry of entries) {
try {
out.push(decodeFilename(entry.name));
} catch (err) {
// Skip entries that aren't ours — a foreign tool may have written
// something to the OPFS directory we share.
if (
err instanceof OpfsError &&
err.code === OPFS_CODES.INVALID_KEY
) {
continue;
}
throw err;
}
}
return out;
}
API Reference
encodeFilename(key: string): string— converts to base64url-without-padding.decodeFilename(filename: string): string— throwsOpfsError(OPFS.INVALID_KEY)for malformed base64 or invalid UTF-8.
Implementation Notes
The codec is deliberately byte-identical to the WHATWG spec for base64url to keep filenames stable across runtimes — the same OPFS directory written from Chrome must round-trip from Safari and Firefox. btoa/atob are universally available in browsers and in Node.js 20+, so no polyfill is needed at runtime. The fatal TextDecoder makes the codec self-healing: a foreign tool that writes non-base64 entries to the OPFS directory will be skipped by decodeFilename-using callers rather than corrupting their key view.