2.9 KiB
disk-fs
Purpose
Disk-backed implementation of MutableInstallerFs used by the
suaveplan-install bin and any host-side code that wants to write to a
real project directory. All reads and writes resolve relative to a single
configurable cwd, so an installer run cannot accidentally escape the
target project's root via absolute paths supplied by a third-party plugin.
Features
- Constructor accepts an explicit
cwd(defaults toprocess.cwd()) and an optionalAbortSignal— every async method short-circuits with anAbortErroronce the signal aborts. writeFileandrenameautomatically create intermediate directories viamkdir({ recursive: true }).- Absolute paths supplied by callers (e.g.
/foo) are resolved as relative to the configuredcwd, never escaping it. This is the same policyMemoryFsuses, keeping the two backends interchangeable for tests vs. production. readFilereturns a trueUint8Arrayview (not a NodeBuffer), so consumers that target browsers and servers behave identically.
Basic Example
import { DiskFs } from "@suaveplan/installer";
const fs = new DiskFs({ cwd: process.cwd() });
const bytes = await fs.readFile("package.json");
const pkg = JSON.parse(new TextDecoder().decode(bytes)) as {
name: string;
};
console.log(pkg.name);
Advanced Example
import { DiskFs } from "@suaveplan/installer";
const ac = new AbortController();
process.once("SIGINT", () => ac.abort());
const fs = new DiskFs({ cwd: process.cwd(), signal: ac.signal });
await fs.mkdir(".suaveplan/skills");
await fs.writeJson(".suaveplan/skills/index.json", {
schemaVersion: 1,
skills: [],
});
await fs.writeFile(
".suaveplan/skills/echo/SKILL.md",
new TextEncoder().encode("# echo\n"),
0o644
);
API Reference
new DiskFs(options?: { cwd?, signal? })pathOf(...segments): string— resolves relative tocwd.exists(path): Promise<boolean>— never throws; missing path returnsfalse.stat(path): Promise<{ size, isFile, isDir }>readFile(path): Promise<Uint8Array>readJson<T>(path): Promise<T>listDir(path): Promise<string[]>— sorted ascending.writeFile(path, contents, mode?): Promise<void>deleteFile(path): Promise<void>— no-op when absent (force: true).mkdir(path): Promise<void>— recursive.rename(from, to): Promise<void>writeJson(path, value): Promise<void>
Implementation Notes
Internally, paths starting with / are stripped of the leading slash and
re-resolved against cwd so they cannot escape. This is critical: a third
party plugin that planned { kind: "write", path: "/etc/passwd" } would
otherwise overwrite a system file. With the strip-and-resolve policy, the
write lands at <cwd>/etc/passwd instead. Consumers who really want to
write outside cwd must use node:fs/promises directly — the installer
does not provide an escape hatch on purpose.