first commit
This commit is contained in:
83
docs/devtools/tooling/installer/src/fs/disk-fs.md
Normal file
83
docs/devtools/tooling/installer/src/fs/disk-fs.md
Normal 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.
|
||||
Reference in New Issue
Block a user