# @suaveplan/api-plugin-cms - Headless CMS primitives for @suaveplan/api: in-memory CmsEngine over @suaveplan/events with content models, entries, status lifecycle, version history, slug uniqueness, Zod-validated plugin options, RFC 7807 problem+json renderer, and an OTel instrument bundle. Server-tier — Bun + Node. Headless content-management primitives for `@suaveplan/api`. Ships an in-memory `CmsEngine` that manages flexible content models, content entries with a `draft → review → published → archived` status lifecycle, slug uniqueness within a model, and a per-entry version trail. The package factory wraps the engine in a typed `PluginDescriptor` consumable by `@suaveplan/api-plugin`'s `PluginRegistry`; the engine is exposed under the `cms` slot of the host `ServiceRegistry`. ## Documentation tree - [engine](./src/engine/cms-engine.md) — `CmsEngine` is the headless content-management core for `@suaveplan/api-plugin-cms`. It manages flexible content models… - [cms-engine](./src/engine/cms-engine.md) — `CmsEngine` is the headless content-management core for `@suaveplan/api-plugin-cms`. It manages flexible content models… - [error-codes](./src/error-codes/error-codes.md) — This module owns the stable error-code namespace `CMS.*` for the package and the concrete `CmsError` class that consume… - [error-codes](./src/error-codes/error-codes.md) — This module owns the stable error-code namespace `CMS.*` for the package and the concrete `CmsError` class that consume… - [otel](./src/otel/instruments.md) — The package emits seven OpenTelemetry counters on every production mutation so operators can dashboard CMS activity and… - [instruments](./src/otel/instruments.md) — The package emits seven OpenTelemetry counters on every production mutation so operators can dashboard CMS activity and… - [plugin](./src/plugin/plugin.md) — Wraps `CmsEngine` in a typed `PluginDescriptor` consumable by `@suaveplan/api-plugin`'s `PluginRegistry`. The factory m… - [plugin](./src/plugin/plugin.md) — Wraps `CmsEngine` in a typed `PluginDescriptor` consumable by `@suaveplan/api-plugin`'s `PluginRegistry`. The factory m… - [routes](./src/routes/problem-json.md) — Render `CmsError` (and arbitrary throwables) as RFC 7807 problem+json HTTP responses without requiring `@suaveplan/api-… - [problem-json](./src/routes/problem-json.md) — Render `CmsError` (and arbitrary throwables) as RFC 7807 problem+json HTTP responses without requiring `@suaveplan/api-… - [schemas](./src/schemas/options.md) — Zod schema validating the `CmsPluginOptions` bundle accepted by `createCmsPlugin(...)`. Boundary validation at the plug… - [options](./src/schemas/options.md) — Zod schema validating the `CmsPluginOptions` bundle accepted by `createCmsPlugin(...)`. Boundary validation at the plug… - [services](./src/services/registry-augmentation.md) — Declares the `cms` slot on the `@suaveplan/api-plugin/service-registry`'s `ServiceRegistry` interface via TypeScript mo… - [registry-augmentation](./src/services/registry-augmentation.md) — Declares the `cms` slot on the `@suaveplan/api-plugin/service-registry`'s `ServiceRegistry` interface via TypeScript mo… ## Install ```bash bun add @suaveplan/api-plugin-cms ``` ## Quick start ```ts import { createCmsPlugin } from "@suaveplan/api-plugin-cms"; import { createPluginRegistry } from "@suaveplan/api-plugin"; const registry = createPluginRegistry(); registry.register(createCmsPlugin({})); await registry.initAll(); const cms = registry.context("cms").getService("cms"); const model = cms?.createModel({ name: "Blog post", slug: "post", contentType: "post", fields: [ { name: "title", type: "text", label: "Title", required: true }, { name: "body", type: "richtext", label: "Body" }, ], }); const entry = cms?.createContent({ modelId: model!.id, title: "Hello world", data: { title: "Hello world", body: "Welcome." }, }); cms?.transitionStatus(entry!.id, "published"); ``` ## Rendering errors as RFC 7807 problem+json Use `respondProblemJson(res, err)` from your route's error handler. The renderer maps every `CMS.*` code to the documented HTTP status (404 / 400 / 409 / 500). ```ts import { respondProblemJson } from "@suaveplan/api-plugin-cms"; app.onError((err, c) => respondProblemJson(c, err)); ``` ## Modules - [`engine/cms-engine`](./src/engine/cms-engine.md) — `CmsEngine` core (model + content CRUD, status FSM, versions). - [`error-codes/error-codes`](./src/error-codes/error-codes.md) — `CMS_CODES`, `CmsError`, factory helpers. - [`otel/instruments`](./src/otel/instruments.md) — OpenTelemetry counters. - [`plugin/plugin`](./src/plugin/plugin.md) — `createCmsPlugin` factory. - [`routes/problem-json`](./src/routes/problem-json.md) — RFC 7807 renderer. - [`schemas/options`](./src/schemas/options.md) — `CmsPluginOptionsSchema`. - [`services/registry-augmentation`](./src/services/registry-augmentation.md) — `ServiceRegistry` augmentation for the `cms` slot. ## License MIT — © Suaveplan Team