Initial Commit

This commit is contained in:
2026-05-27 23:29:53 +01:00
commit fbc70f96e6
10000 changed files with 1102152 additions and 0 deletions

View File

@@ -0,0 +1,82 @@
# `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<string, string | Uint8Array>` 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<boolean>`
- `stat(path): Promise<{ size, isFile, isDir }>`
- `readFile(path): Promise<Uint8Array>`
- `readJson<T>(path): Promise<T>`
- `listDir(path): Promise<string[]>`
- `writeFile(path, contents, mode?): Promise<void>`
- `deleteFile(path): Promise<void>`
- `mkdir(path): Promise<void>`
- `rename(from, to): Promise<void>`
- `writeJson(path, value): Promise<void>`
- `snapshot(): Record<string, Uint8Array>`
## 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.