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

2.8 KiB

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

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

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.