Contentrain/ai
Developer ProductivityLocal-first MCP server for AI content governance — 13 tools for model/content CRUD, validation, normalization, and i18n across any framework.
Features
- Extract hardcoded UI strings into structured content
- Create, translate, and update content via agents
- Enforce schema validation and Git review workflow
- Output plain JSON/Markdown consumable by any platform
Recent releases
View all 102 releases →Field constraint enforcement
Enforced field validation
- Update code that previously accessed `entry.body` from `all()` results to first retrieve the full document with `bySlug(slug)`, then read `body` from the returned object.
- `CdnDocumentQuery.all()` now returns `DocumentIndexEntry[]` (omitting `body`) and `first()` returns `DocumentIndexEntry | undefined`; reading `.body` is a compile error. Use `bySlug(slug)` to fetch the full document including `body`.
Full changelog
Major Changes
-
d617dab: feat(sdk)!: CDN
document().all()returns index entries, so reading.bodyno longer compilesOn the CDN,
document(model).all()reads the model's_index, which carries
frontmatter only — the body lives in the per-slug document and is reachable via
bySlug(). Butall()was typedPromise<T[]>, and the generated document type
declaresbody: string. Soentry.bodytype-checked, returnedundefinedat
runtime, and pages rendered their headings and dropped their prose — with no
error, no warning, and a passing typecheck.The trap is that the bundled runtime's
all()does carry bodies. Code written
and tested against bundled delivery compiled and passed, then quietly rendered
empty on CDN. The two modes genuinely differ in shape, so they no longer share
one return type.BREAKING:
CdnDocumentQuery.all()now returnsDocumentIndexEntry<T>[]
(Omit<T, 'body'>), andfirst()returnsDocumentIndexEntry<T> | undefined.
Reading.bodyoff either is now a compile error. Fetch the body with
bySlug(slug), which returns{ frontmatter, body, html }. Runtime behaviour is
unchanged — this only makes the types tell the truth about what was already
being returned.// before — compiled, rendered nothing const sections = await client.document<GuideSections>("guide-sections").all(); sections.map((s) => renderMarkdown(s.body)); // undefined at runtime // after — the first line is a compile error; fetch bodies explicitly const index = await client.document<GuideSections>("guide-sections").all(); const full = await Promise.all(index.map((s) => q.bySlug(s.slug))); full.map((d) => renderMarkdown(d!.body));Type-level contracts are now asserted in
*.test-d.tsviavitest --typecheck,
sincetsconfig.jsononly coverssrcand never type-checked the tests.
Weekly OSS security release digest.
The CVE patches and breaking changes that affected production tools this week. One email, every Sunday.
No spam, unsubscribe anytime.