Files
skills/docs/api-stack/api/api-lifecycle/src/reinstall-criteria/reinstall-criteria.md
2026-05-29 08:20:56 +01:00

73 lines
2.6 KiB
Markdown

# `reinstall-criteria`
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
Decides whether a manifest update from `prev` to `next` requires a full
reinstall path (`uninstalled → installing → installed → starting → started`)
versus a hot-reload (`stopped → starting`). The criteria mirror design §4
exactly: a `schemaVersion` bump, new scopes/events/jobs/secrets, or any
`dependsOn` diff force the full path; a strict version downgrade throws
`LIFECYCLE.REINSTALL_REQUIRED_VERSION_DOWNGRADE` because the ledger would
then point at a higher schemaVersion than the running code can satisfy.
## Features
- Pure `requiresReinstall(prev, next): boolean` — no I/O, no global state,
no clock dependency.
- Per-field detection of added items only (removals are intentionally not a
reinstall trigger — the host can drop them in hot-reload).
- Embedded `compareSemver(a, b)` implementing loose semver compare
(`X.Y.Z` form, prerelease/build metadata ignored). The manifest
validator in `api-manifest` already rejects non-semver values, so this
is the only consumer.
- Throws `LIFECYCLE.REINSTALL_REQUIRED_VERSION_DOWNGRADE` with `prev` /
`next` versions in `error.context` for downgrade attempts.
## Usage: Basic
```ts
import { requiresReinstall } from "@suaveplan/api-lifecycle/reinstall-criteria";
if (requiresReinstall(prev, next)) {
// walk the full install path
} else {
// hot reload via stopped → starting
}
```
## Usage: Advanced
Wrapping the call in admin tooling that wants to surface the specific
trigger reason:
```ts
import { compareSemver, requiresReinstall } from "@suaveplan/api-lifecycle/reinstall-criteria";
function explain(prev: PluginManifest, next: PluginManifest): string {
if (compareSemver(next.version, prev.version) < 0) return "version downgrade";
if ((next.database?.schemaVersion ?? 0) > (prev.database?.schemaVersion ?? 0)) {
return "schemaVersion bumped";
}
return requiresReinstall(prev, next) ? "manifest changed materially" : "hot reload OK";
}
```
## API Reference
- `requiresReinstall(prev: PluginManifest, next: PluginManifest): boolean`
- `compareSemver(a: string, b: string): -1 | 0 | 1`
## Implementation Notes
The "added-items" detection uses `Set`s built from `prev` so the comparison
is O(n) per field. `dependsOn` is compared via a key-union walk so a
removed-but-then-readded dep with a different version is still caught.
Removing items intentionally does not trigger a reinstall — design §4
defines this as a hot-reload-safe operation; the host driver drops the
corresponding capability registrations in the `installed → starting`
side-effect chain.