# `installer-fs` ## Purpose Re-exports the read-only `InstallerFs` interface from the types module and declares its mutating extension `MutableInstallerFs`. Plugins receive a read-only `InstallerFs` through the context — they can describe writes via `FileOp[]` but cannot perform them directly. The host uses `MutableInstallerFs` internally to materialise ops with rollback. Keeping the read/write split at the interface level is what makes `--dry-run` trivially correct: a dry-run never resolves the mutating subset. ## Features - Single source of truth for filesystem capability boundaries inside the installer. - `MutableInstallerFs` extends `InstallerFs` with `writeFile`, `deleteFile`, `mkdir`, `rename`, and `writeJson`. - Both `MemoryFs` and `DiskFs` implement `MutableInstallerFs`, but only the read-only view is ever handed to plugin code. ## Basic Example ```ts import { MemoryFs, type InstallerFs } from "@suaveplan/installer"; async function describe(fs: InstallerFs): Promise { const entries = await fs.listDir("/"); return entries.length; } const fs = new MemoryFs({ seed: { "/a.txt": "hi" } }); await describe(fs); ``` ## Advanced Example ```ts import { type MutableInstallerFs, MemoryFs, } from "@suaveplan/installer"; async function backupThenWrite( fs: MutableInstallerFs, path: string, next: Uint8Array ): Promise { if (await fs.exists(path)) { const prior = await fs.readFile(path); await fs.writeFile(`${path}.bak`, prior); } await fs.writeFile(path, next); } await backupThenWrite(new MemoryFs(), "/cfg.json", new Uint8Array([123, 125])); ``` ## API Reference - `interface InstallerFs` (re-export from `types/installer.types.ts`): `exists`, `readFile`, `readJson`, `stat`, `listDir`, `pathOf`. - `interface MutableInstallerFs extends InstallerFs`: `writeFile(path, contents, mode?)`, `deleteFile(path)`, `mkdir(path)`, `rename(from, to)`, `writeJson(path, value)`. ## Implementation Notes `MutableInstallerFs` is exposed publicly through `./fs` so authors who want to write a custom backing store (e.g. an S3-backed installer fs) have a single contract to satisfy. Implementations must honour any `AbortSignal` they were constructed with — both in-tree implementations call a shared `ensureNotAborted(signal)` helper at the start of every async operation. The interface deliberately has no `chmod` or `chown` operations: per-file mode is carried on the `WriteOp` itself, and ownership is left to the host filesystem.