Files
skills/plugins/document-navigator/docs/api-stack/api/api-plugin-cms
admin 9bf56a806b Move docs/ package mirror into document-navigator plugin
Relocate the ~6k-file docs tree from the repo root into
plugins/document-navigator/docs/ so the plugin is self-contained and
portable. docs/superpowers/ (specs and plans) stays at the repo root.

Moved via cp+rm rather than git mv because directory rename() fails with
EPERM on this WSL /mnt DrvFs mount; git rename-detection pairs the
identical-content files at commit time.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 01:10:00 +01:00
..

@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 — CmsEngine is the headless content-management core for @suaveplan/api-plugin-cms. It manages flexible content models…
    • cms-engine — CmsEngine is the headless content-management core for @suaveplan/api-plugin-cms. It manages flexible content models…
  • error-codes — This module owns the stable error-code namespace CMS.* for the package and the concrete CmsError class that consume…
    • error-codes — This module owns the stable error-code namespace CMS.* for the package and the concrete CmsError class that consume…
  • otel — The package emits seven OpenTelemetry counters on every production mutation so operators can dashboard CMS activity and…
    • instruments — The package emits seven OpenTelemetry counters on every production mutation so operators can dashboard CMS activity and…
  • plugin — Wraps CmsEngine in a typed PluginDescriptor consumable by @suaveplan/api-plugin's PluginRegistry. The factory m…
    • plugin — Wraps CmsEngine in a typed PluginDescriptor consumable by @suaveplan/api-plugin's PluginRegistry. The factory m…
  • routes — Render CmsError (and arbitrary throwables) as RFC 7807 problem+json HTTP responses without requiring `@suaveplan/api-…
    • problem-json — Render CmsError (and arbitrary throwables) as RFC 7807 problem+json HTTP responses without requiring `@suaveplan/api-…
  • schemas — Zod schema validating the CmsPluginOptions bundle accepted by createCmsPlugin(...). Boundary validation at the plug…
    • options — Zod schema validating the CmsPluginOptions bundle accepted by createCmsPlugin(...). Boundary validation at the plug…
  • services — Declares the cms slot on the @suaveplan/api-plugin/service-registry's ServiceRegistry interface via TypeScript mo…
    • registry-augmentation — Declares the cms slot on the @suaveplan/api-plugin/service-registry's ServiceRegistry interface via TypeScript mo…

Install

bun add @suaveplan/api-plugin-cms

Quick start

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).

import { respondProblemJson } from "@suaveplan/api-plugin-cms";

app.onError((err, c) => respondProblemJson(c, err));

Modules

License

MIT — © Suaveplan Team