first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,83 @@
# `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 to `process.cwd()`) and
an optional `AbortSignal` — every async method short-circuits with an
`AbortError` once the signal aborts.
- `writeFile` and `rename` automatically create intermediate directories
via `mkdir({ recursive: true })`.
- Absolute paths supplied by callers (e.g. `/foo`) are resolved as
*relative* to the configured `cwd`, never escaping it. This is the same
policy `MemoryFs` uses, keeping the two backends interchangeable for
tests vs. production.
- `readFile` returns a true `Uint8Array` view (not a Node `Buffer`), so
consumers that target browsers and servers behave identically.
## Basic Example
```ts
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
```ts
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 to `cwd`.
- `exists(path): Promise<boolean>` — never throws; missing path returns
`false`.
- `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.