# RFC 7807 problem+json renderer _(no submodules)_ ## Purpose Render `CmsError` (and arbitrary throwables) as RFC 7807 problem+json HTTP responses without requiring `@suaveplan/api-plugin-cms` to depend on any HTTP framework. The renderer accepts a small structural `HttpResponseLike` interface that is compatible with Hono, Fastify, Bun's stdlib `Response`, and any framework whose response object exposes `header(name, value)` plus `body(data, status)`. Status mapping is encoded inline as a `Record` lookup table that the package-level spec-conformance test asserts is exhaustive over `CMS_CODES`. ## Features - Pure `formatProblemJson(err)` returns the JSON body for logging or side-channel use. - `respondProblemJson(res, err)` writes header + body and returns the framework's `Response` for `return res.body(...)` style handlers. - `PROBLEM_JSON_CONTENT_TYPE` constant — `application/problem+json` per RFC 7807. - Local `ProblemDetails` interface matching the IETF spec, with an extension-index signature so callers can add fields per RFC §3.2. - Status table: - `CMS.CONTENT_NOT_FOUND` / `CMS.MODEL_NOT_FOUND` → 404 - `CMS.INVALID_TRANSITION` / `CMS.DUPLICATE_SLUG` / `CMS.MODEL_IN_USE` → 409 - `CMS.VALIDATION_FAILED` → 400 - `CMS.INVALID_INPUT` → 500 - Generic `SuaveplanError` instances render with HTTP 500 but with the original code so log scrapers still see structure. - Non-error throwables render `{ type: "about:blank", status: 500 }`. ## Usage: Basic ```ts import { Hono } from "hono"; import { respondProblemJson } from "@suaveplan/api-plugin-cms"; const app = new Hono(); app.onError((err, c) => respondProblemJson(c, err)); ``` ## Usage: Advanced Side-channel logging without writing to a response (e.g. forwarding the body to an audit pipeline): ```ts import { formatProblemJson } from "@suaveplan/api-plugin-cms"; try { engine.transitionStatus(id, target); } catch (err) { const body = formatProblemJson(err); await audit.log({ type: "cms.transition.failed", severity: body.status >= 500 ? "error" : "warning", ...body, }); throw err; } ``` ## API Reference | Symbol | Description | |---|---| | `PROBLEM_JSON_CONTENT_TYPE` | `"application/problem+json"`. | | `interface ProblemDetails` | RFC 7807 body shape with an extension index. | | `interface HttpResponseLike` | `{ header(n, v); body(d, s): Response }`. | | `respondProblemJson(res, err)` | Write header + body; return `Response`. | | `formatProblemJson(err)` | Pure variant. | ## Implementation Notes The status lookup is a `Readonly>` keyed by the fully-qualified `CMS.*` code. If a `CmsError` is constructed with a code not in the table (unreachable in normal use; possible only in tests that bypass `defineCodes`), the renderer falls back to HTTP 500. Generic `SuaveplanError` instances render with HTTP 500 + the original `code` field so the response is still grep-friendly. The `HttpResponseLike` shape was chosen over `Context` (Hono) to keep this package free of framework dependencies — see `@suaveplan/api-plugin-database`'s `routes/problem-json.ts` for the same pattern.