# `memory-fs` ## Purpose In-memory implementation of `MutableInstallerFs`. Used by the testing harness (`runPlugin` constructs one if the caller doesn't supply one) and by anything that needs deterministic, side-effect-free filesystem behaviour: integration tests, dry-run inspection of large plugins, fixture seeding for replays. POSIX-style paths only; Windows-style backslashes are normalised to forward slashes on entry. ## Features - Seedable from a `Record` map at construction time — strings are UTF-8 encoded automatically. - Tracks directories as first-class entries, so `listDir` returns deterministic, sorted results without sniffing files for shared prefixes. - `writeFile`/`mkdir` create intermediate directories implicitly. - `deleteFile` refuses to remove a non-empty directory (`ENOTEMPTY`); empty dirs delete cleanly. - `snapshot()` returns a `{ path: Uint8Array }` map of every file — handy for end-of-test assertions. - Honours an optional `AbortSignal` — every async call short-circuits with an `AbortError` once the signal aborts. ## Basic Example ```ts import { MemoryFs } from "@suaveplan/installer"; const fs = new MemoryFs({ seed: { "/README.md": "# hi\n" } }); const bytes = await fs.readFile("/README.md"); console.log(new TextDecoder().decode(bytes)); ``` ## Advanced Example ```ts import { MemoryFs } from "@suaveplan/installer"; const ac = new AbortController(); const fs = new MemoryFs({ signal: ac.signal }); await fs.mkdir("/.suaveplan/skills"); await fs.writeJson("/.suaveplan/skills/index.json", { skills: [] }); await fs.writeFile( "/.suaveplan/skills/echo/SKILL.md", new TextEncoder().encode("# echo\n") ); const list = await fs.listDir("/.suaveplan/skills"); console.log(list); // ["echo", "index.json"] ac.abort(); await fs.writeFile("/late.txt", new Uint8Array()); // throws AbortError ``` ## API Reference - `new MemoryFs(options?: { seed?, signal? })` - `pathOf(...segments): string` - `exists(path): Promise` - `stat(path): Promise<{ size, isFile, isDir }>` - `readFile(path): Promise` - `readJson(path): Promise` - `listDir(path): Promise` - `writeFile(path, contents, mode?): Promise` - `deleteFile(path): Promise` - `mkdir(path): Promise` - `rename(from, to): Promise` - `writeJson(path, value): Promise` - `snapshot(): Record` ## Implementation Notes Path normalisation collapses repeated slashes, strips trailing slashes (except at the root), and canonicalises backslashes. Seeding paths whose parents collide with an already-seeded *file* throws `ENOTDIR` — this is intentional and mirrors POSIX behaviour. The internal `entries` map is ordered by insertion; `listDir` re-sorts on every call so results are stable regardless of write order.