Initial Commit
This commit is contained in:
72
packages/devtools/tooling/installer/src/host/plan.md
Normal file
72
packages/devtools/tooling/installer/src/host/plan.md
Normal file
@@ -0,0 +1,72 @@
|
||||
# `plan`
|
||||
|
||||
## Purpose
|
||||
|
||||
Invokes `plugin.plan(answers, ctx)` and validates the returned `FileOp[]`
|
||||
against `FileOpsSchema` before letting them flow into apply. This is the
|
||||
quality gate that prevents a buggy plugin from feeding malformed ops into
|
||||
the host's transactional apply path. Both plugin-thrown errors and
|
||||
schema-validation failures surface as `INSTALLER.PLAN_FAILED` so callers
|
||||
have a single code to switch on.
|
||||
|
||||
## Features
|
||||
|
||||
- Schema validation via `FileOpsSchema` — every op kind, path, contents
|
||||
type, and conflict policy is checked at the boundary.
|
||||
- Wrapped in OTel span `installer.plan` carrying `plugin.id`,
|
||||
`plugin.version`, and `dry_run`.
|
||||
- Distinguishes `plan` failures (plugin threw) from invalid-shape failures
|
||||
(plan returned bad ops) only via `messageKey` — both surface as the
|
||||
same code, which keeps consumer error handlers simple.
|
||||
- Original error chained via `cause` so the underlying Zod error or
|
||||
plugin exception is recoverable for logging.
|
||||
|
||||
## Basic Example
|
||||
|
||||
```ts
|
||||
import { runPlan } from "@suaveplan/installer";
|
||||
|
||||
const ops = await runPlan(myPlugin, answers, ctx);
|
||||
console.log(`plan returned ${ops.length} ops`);
|
||||
```
|
||||
|
||||
## Advanced Example
|
||||
|
||||
```ts
|
||||
import { InstallerError, runPlan } from "@suaveplan/installer";
|
||||
|
||||
try {
|
||||
const ops = await runPlan(myPlugin, answers, ctx);
|
||||
return ops;
|
||||
} catch (err) {
|
||||
if (
|
||||
err instanceof InstallerError &&
|
||||
err.code === "INSTALLER.PLAN_FAILED"
|
||||
) {
|
||||
const issues = err.context?.["issues"];
|
||||
if (issues) {
|
||||
console.error("Plugin returned invalid ops:", issues);
|
||||
} else {
|
||||
console.error("Plugin plan threw:", err.cause);
|
||||
}
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `runPlan<TAnswers, THandle>(plugin, answers, ctx): Promise<ReadonlyArray<FileOp>>`
|
||||
- Errors: `INSTALLER.PLAN_FAILED` with either:
|
||||
- `installer.plan.invalid_ops` and `context.issues` for schema failures
|
||||
- `installer.plan.threw` and `cause` set to the plugin's exception
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
Validation runs *after* the plugin returns, so a plugin can build ops
|
||||
incrementally without seeing intermediate validation errors. The schema
|
||||
also fixes the common mistake of returning a single op instead of an
|
||||
array — `FileOpsSchema` requires `Array<FileOp>` so that gets caught at
|
||||
the boundary instead of crashing later in apply. If the plugin already
|
||||
threw an `InstallerError`, we re-throw it directly rather than wrapping
|
||||
twice — this preserves `cause` chains all the way to the top.
|
||||
Reference in New Issue
Block a user