Skip to content

Types

npm version npm downloads

@contentrain/types is the shared type contract for the Contentrain ecosystem. Every package — MCP, CLI, SDK, Rules — imports its domain types from here instead of redefining them. If you are building tooling on top of Contentrain or authoring a framework integration, this is the package you depend on.

Why a Shared Types Package?

Without a single source of truth, each package would define its own ModelDefinition, FieldDef, or ContentrainConfig — and they would inevitably drift. @contentrain/types ensures:

  • One vocabulary — every package speaks the same domain language
  • Breaking changes are visible — a type change here is an ecosystem-level change
  • Zero runtime cost — most exports are type-only, tree-shaken away in production

Ecosystem Role

  • MCP validates and writes ModelDefinition
  • CLI reads ContentrainConfig and ContextJson
  • SDK codegen consumes ModelDefinition and FieldDef
  • Rules align with the same model and workflow vocabulary
  • Contentrain Studio operates on the same type contract — schemas defined locally work identically in team workflows

Install

bash
pnpm add @contentrain/types

For type-only usage (no runtime exports needed):

bash
pnpm add -D @contentrain/types

Requirements:

  • Node.js 22+
  • TypeScript 5.0+

Quick Example

ts
import type {
  ContentrainConfig,
  FieldDef,
  ModelDefinition,
  ValidationResult,
} from '@contentrain/types'

const fields: Record<string, FieldDef> = {
  title: { type: 'string', required: true },
  slug: { type: 'slug', required: true, unique: true },
}

const model: ModelDefinition = {
  id: 'blog-post',
  name: 'Blog Post',
  kind: 'collection',
  domain: 'blog',
  i18n: true,
  fields,
}

const config: ContentrainConfig = {
  version: 1,
  stack: 'next',
  workflow: 'review',
  locales: { default: 'en', supported: ['en', 'tr'] },
  domains: ['blog'],
}

const result: ValidationResult = {
  valid: true,
  errors: [],
}

Export Catalog

Core Unions

TypeValuesReference
FieldType27 field types (string, number, boolean, relation, ...)Field Types
ModelKindsingleton, collection, document, dictionaryModel Kinds
ContentStatusdraft, in_review, published, rejected, archived
ContentSourceagent, human, import
WorkflowModeauto-merge, reviewConfiguration
StackTypenuxt, next, astro, sveltekit, remix, + 25 moreConfiguration
Platformweb, mobile, api, desktop, static, other
ContextSourcemcp-local, mcp-studio, studio-ui
CollectionRuntimeFormatmap, array
LocaleStrategyfile, suffix, directory, none
FileFrameworkvue, svelte, jsx, astro, script

Core Interfaces

InterfacePurpose
FieldDefField schema definition (type, required, unique, constraints)
ModelDefinitionFull model schema (id, kind, domain, fields, i18n, locale strategy)
ContentrainConfigProject configuration (stack, workflow, locales, domains)
VocabularyShared terms for content consistency
EntryMetaPer-entry metadata (status, source, timestamps)
AssetEntryAsset registry entry (path, type, size, alt)
ValidationErrorStructured validation issue — severity is error, warning, or notice (notices flag drift like drafts beside published entries)
ValidationResultValidation outcome (valid flag + error list)
ContextJsonLast operation context written by MCP
ModelSummaryLightweight model info for listing operations

Provider Contract Types

Third-party developers can implement custom providers by implementing these interfaces:

Interface / TypePurpose
RepoProviderFull provider contract: read, write, branch, merge, diff operations, plus an optional media?: MediaProvider facet
RepoReaderRead-only interface (readFile, listDirectory, fileExists)
RepoWriterWrite interface (applyPlan for atomic commits)
ProviderCapabilitiesCapability flags (localWorktree, sourceRead, sourceWrite, pushRemote, branchProtection, pullRequestFallback, astScan)
FileChangeA single file addition, modification, or deletion ({ path, content: string | null })
ApplyPlanInputInput for a single atomic commit (branch, changes, message, author, optional base)
CommitResult of a commit operation (sha, message, author, timestamp)
BranchGit branch metadata (name, sha, protected)
FileDiffFile change within a plan (path, status, before, after)
MergeResultMerge outcome (merged flag, sha, pullRequestUrl, optional sync?: SyncResult for LocalProvider, optional remote? source-branch cleanup outcome)
SyncResultSelective file sync result (synced, skipped, optional warning)
CommitAuthorCommit author metadata (name, email)

Media facet types (implemented by providers exposing a media stack — drives the contentrain_media_* tools):

Interface / TypePurpose
MediaProviderOptional RepoProvider.media facet: list / get / ingest / update / delete
MediaAssetOne asset — id, path (media/...), optional url, mime, size, alt, tags, createdAt, meta
MediaListOptionsList filters (search, tag, limit, cursor)
MediaListResultList page (assets, optional nextCursor, total)
MediaIngestInputURL-based ingest input (url, optional filename, alt, tags)
MediaUpdateInputMetadata patch (alt, tags, filename)

Pre-built capability set:

  • LOCAL_CAPABILITIES — Capability set for LocalProvider: localWorktree, sourceRead, sourceWrite, pushRemote, and astScan enabled; branchProtection and pullRequestFallback are false (a local worktree has no remote protection or PR flow). Exported from @contentrain/types for custom providers that back onto the local filesystem.

See RepoProvider Reference for the complete interface definitions and a minimum-viable provider recipe.

Storage Types

These types define the canonical JSON structure for each model kind on disk:

TypeModel KindShape
SingletonContentFileSingletonRecord<string, unknown>
CollectionContentFileCollectionRecord<string, Record<string, unknown>> (object-map by entry ID)
DictionaryContentFileDictionaryRecord<string, string> (flat key-value, all strings)

Output Types

How MCP and SDK return content to consumers (different from storage format):

TypeDescription
CollectionEntry{ id: string } & Record<string, unknown>
CollectionContentOutputCollectionEntry[] (array format)
DocumentEntry{ slug, frontmatter, body } — parsed markdown
DocumentContentOutputDocumentEntry[]
PolymorphicRelationRef{ model, ref } — cross-model relation storage

Metadata Types

TypeDescription
SingletonMetaAlias for EntryMeta
CollectionMetaRecord<string, EntryMeta> — per-entry metadata map
DocumentMetaAlias for EntryMeta
DictionaryMetaAlias for EntryMeta

Scan & Graph Types

Used by the normalize flow (scan, extract, reuse):

TypePurpose
ScanCandidateHardcoded string candidate with file, line, column, context
DuplicateGroupGroup of repeated strings with occurrence locations
GraphNodeFile node in the project graph (category, imports, strings)
ProjectGraphFull project structure graph (pages, components, layouts)
ScanCandidatesResultScan output with candidates, duplicates, and stats
ScanSummaryResultHigh-level scan summary (directory breakdown, top repeated)
StringContextWhere a string appears (jsx_text, template_attribute, ...)
FileCategoryFile classification (page, component, layout, other)
NormalizePlanNormalize plan exchanged between scan and apply
NormalizePlanModelModel proposal inside a normalize plan
NormalizePlanExtractionOne extraction target (content entry to create)
NormalizePlanPatchOne source patch inside a normalize plan

Runtime Constants

Beyond types, the package ships a small runtime surface: constants plus pure, dependency-free validate/serialize functions (browser-compatible — Studio shares the same validation contract through them). Constants first:

ts
import {
  CONTENTRAIN_DIR,       // '.contentrain'
  CONTENTRAIN_BRANCH,    // 'contentrain'
  PATH_PATTERNS,         // Canonical file path patterns
  SLUG_PATTERN,          // /^[a-z0-9]+(?:-[a-z0-9]+)*$/
  ENTRY_ID_PATTERN,      // /^[a-zA-Z0-9][a-zA-Z0-9_-]{0,39}$/
  LOCALE_PATTERN,        // /^[a-z]{2}(?:-[A-Z]{2})?$/
  CANONICAL_JSON,        // { indent: 2, encoding: 'utf-8', ... }
} from '@contentrain/types'
ConstantValuePurpose
CONTENTRAIN_DIR'.contentrain'Root directory name
CONTENTRAIN_BRANCH'contentrain'Dedicated content branch name
PATH_PATTERNSObjectCanonical paths for config, models, content, meta
SLUG_PATTERNRegExpValidates slug format
ENTRY_ID_PATTERNRegExpValidates entry IDs
LOCALE_PATTERNRegExpValidates ISO locale codes
CANONICAL_JSONObjectDeterministic serialization rules
SECRET_PATTERNSReadonlyArray<RegExp>Patterns behind detectSecrets — extend for custom secret detection

Runtime Functions

Validate functions (pure, dependency-free):

FunctionPurpose
validateSlug(slug)Kebab-case slug validation
validateEntryId(id)Entry ID format validation
validateLocale(locale, config)Locale format + config support check
detectSecrets(value)Detect potential secrets in field values
validateFieldValue(value, fieldDef)Full field schema validation (type, required, min/max, pattern, select)
validateSemanticType(value, type)Semantic checks for typed values (integer, date, email, url, ...)
validateAccept(value, accept)Extension-based accept constraint check for media paths
isMediaType(type)Whether a field type is media-backed

Serialize functions (pure, dependency-free):

FunctionPurpose
sortKeys(obj, fieldOrder?)Recursive key sorting for canonical output
canonicalStringify(data, fieldOrder?)Deterministic JSON serialization
generateEntryId()12-char hex entry ID generation
parseMarkdownFrontmatter(content)Parse YAML frontmatter + body from markdown
serializeMarkdownFrontmatter(data, body)Serialize data + body into markdown frontmatter

Unique constraints and relation references need external state (all entries / target existence), so they stay in MCP's validator — validateFieldValue covers everything schema-level.

Git Transaction Types

TypePurpose
SyncResultResult of selective file sync (synced files, skipped files, warning)
ContentrainErrorStructured error with code, message, agent hint, and developer action
ScaffoldTemplateTemplate definition for project scaffolding

Import Style

Type-only imports (recommended for application code):

ts
import type { ModelDefinition, ContentrainConfig, FieldDef } from '@contentrain/types'

Runtime imports (when you need constants):

ts
import { PATH_PATTERNS, CANONICAL_JSON, CONTENTRAIN_DIR } from '@contentrain/types'

Stability

This package is the shared public contract across the ecosystem:

  • Types exported from the package root are the public surface
  • Packages depend on these shared definitions instead of redefining domain types
  • Breaking changes here are ecosystem-level breaking changes
  • The package should stay small, dependency-light, and stable

Development

From the monorepo root:

bash
pnpm --filter @contentrain/types build
pnpm --filter @contentrain/types test
pnpm --filter @contentrain/types typecheck
  • MCP Tools — Validates and writes models using these types
  • CLI — Reads config and context using these types
  • Query SDK — Codegen consumes model definitions and field types
  • Rules & Skills — Aligns with the same vocabulary
  • Model Kinds — Detailed specification of the four model kinds
  • Field Types — Comprehensive field type reference
  • Configuration — Config file schemas and directory layout