2.7 KiB
dependsOnSchema
Purpose
Map of plugin-name → semver range used by the dep-graph resolver. Names follow the same npm-package convention as identity.name; ranges follow the same syntax the in-house satisfies() accepts (pinned, caret, tilde, wildcard, */latest).
Features
- Free-form record keys (plugin names) — keyspace is checked downstream by the dep-graph against the actual install set
- Range values are non-empty strings
- Readonly map output discourages mutation
- Empty map (
{}) parses successfully — a plugin with no dependencies is a leaf
Basic Example
import { dependsOnSchema } from "@suaveplan/api-manifest/schema";
dependsOnSchema.parse({ "@suaveplan/api-plugin-users": "^1" });
Advanced Example
import { resolveInstallOrder } from "@suaveplan/api-manifest/dep-graph";
import { dependsOnSchema } from "@suaveplan/api-manifest/schema";
const deps = dependsOnSchema.parse(raw);
const order = resolveInstallOrder([
{ ...userManifest, dependsOn: deps },
...others,
]);
API Reference
dependsOnSchema: z.ZodReadonly<z.ZodRecord<z.ZodString, z.ZodString>>DependsOn = z.infer<typeof dependsOnSchema>
Implementation Notes
The schema deliberately doesn't pre-validate range syntax — resolveInstallOrder round-trips every range through satisfies() and surfaces MANIFEST.DEPENDENCY_VERSION_MISMATCH on anything it can't parse, so the error envelope is identical for invalid ranges and unsatisfied ranges. This keeps the schema decoupled from the matcher's exact range subset. Duplicate keys in JSON resolve to last-wins per JSON.parse, and the dep-graph treats each entry independently.
Cross-package Notes
This module is part of the @suaveplan/api-manifest Stage-1 contract package. It sits beneath every Stage-2 consumer (api-host, api-loader, the admin CLI, the IDE plugin) and above the foundation packages (@suaveplan/error, @suaveplan/types). The strict-mode top-level rejection of unknown keys means upgrading from manifest v1 to v2 is an OpenSpec-driven event: a new key landing in a consumer manifest is a parse failure rather than a silent drift, which is exactly the contract this stage exists to guarantee.
When the host reloads a plugin without a full restart, every manifest field this module validates is compared against the install-time manifestHash. A mismatch triggers a hard rollback via the RollbackJournal because the host's invariants (route mounts, registered capabilities, declared scopes) are derived from these exact bytes. Treat the schemas and validators here as the contract surface, not just an input filter — downstream code reads manifest.foo.bar and relies on those keys being present, well-typed, and non-undefined in the documented optionality regions.