Skip to content

Contentrain/ai

Developer Productivity

Local-first MCP server for AI content governance — 13 tools for model/content CRUD, validation, normalization, and i18n across any framework.

TypeScript Latest [email protected] · 9d ago Security brief →

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 →
Security behavior changed
@contentrain/[email protected] Breaking risk
Auth RBAC

Field constraint enforcement

Security behavior changed
@contentrain/[email protected] Breaking risk
Auth Breaking upgrade

Enforced field validation

@contentrain/[email protected] Breaking risk
⚠ Upgrade required
  • 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.
Breaking changes
  • `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 .body no longer compiles

    On 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(). But all() was typed Promise<T[]>, and the generated document type
    declares body: string. So entry.body type-checked, returned undefined at
    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 returns DocumentIndexEntry<T>[]
    (Omit<T, 'body'>), and first() returns DocumentIndexEntry<T> | undefined.
    Reading .body off 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.ts via vitest --typecheck,
    since tsconfig.json only covers src and 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.

About

Stars
4
Forks
0
Languages
TypeScript JavaScript Vue
Downloads/week
134 ↑9%
NPM Maintainers
1 Single npm maintainer
Contributors
5
TypeScript
Types included ✓

Install & Platforms

Install via
npm

Community & Support

Beta — feedback welcome: [email protected]