cms
import { defineCollection, defineCmsConfig, createLocalApi, renderRichText, rule,} from "louisecms/cms";The cms subpath is the structured content engine: define collections, generate
Drizzle schema from them, and read/write documents through an access-controlled,
validated Local API. Peer dependency: drizzle-orm.
Defining collections
Section titled “Defining collections”import { defineCollection, defineCmsConfig } from "louisecms/cms";
const artworks = defineCollection({ slug: "artworks", fields: { title: { type: "text", validation: (r) => r.required().min(2) }, slug: { type: "text", validation: (r) => r.slug().unique() }, year: { type: "number", validation: (r) => r.integer().positive() }, body: { type: "richtext" }, },});
export const cms = defineCmsConfig({ collections: { artworks } });defineCollection / defineCmsConfig are identity helpers (Sanity’s
defineType analogue) — they return the config unchanged but give you
autocomplete and a single greppable call site.
Codegen — schema from config
Section titled “Codegen — schema from config”import { cmsConfigToSchema, generateSchemaSource } from "louisecms/cms";cmsConfigToSchema(config)builds Drizzle table objects from aCmsConfigat runtime.generateSchemaSource(config)emits.tssource for a committed schema file (import-sorted so your formatter never flags it).
Related builders: collectionToTable, collectionVersionsTable,
relationshipJoinTables, and full-text search helpers
(collectionSearchTableSQL, extractSearchText).
The Local API
Section titled “The Local API”import { createLocalApi, createVersionedLocalApi } from "louisecms/cms";
const api = createLocalApi(collectionConfig, table, { registry });await api.create(doc, context); // runs access + validation, then insertsawait api.find(query, context);await api.update(id, patch, context);Every method takes a context and runs the matching access function
(read for find/findByID, create for create, …) before touching the
database, and validates writes with the collection’s
rules — throwing LouiseAccessDeniedError (→ 403) or
LouiseValidationError (→ 422) so a routing layer can branch by instanceof.
createVersionedLocalApi adds draft/version history for collections that opt in
with versions. can(...) evaluates access without performing the operation.
Validation
Section titled “Validation”A chainable, immutable, Sanity-style rule builder:
import { rule } from "louisecms/cms";
rule().required().min(2).max(80);rule().slug().unique();rule().email().warning("Double-check this address");rule().custom((value, ctx) => value !== "forbidden" || "Not allowed");Pure checks (min/max/length/regex/email/slug/integer/positive/
custom) run anywhere; unique and reference are DB-backed and skipped in a
pure client pass. validateDocument(...) returns all violations;
assertValid(...) throws LouiseValidationError on any "error"-severity one
while returning warnings.
Patches & rich text
Section titled “Patches & rich text”diffDocuments/computePatch/applyPatch— structural document diffs for optimistic updates and version history.renderRichText(content)— render stored rich-text content to HTML (the server never runs ProseMirror; see Rich text).
Webhooks, migrations, visual editing
Section titled “Webhooks, migrations, visual editing”createWebhookHook/deliverWebhookMessage— afterChange-style outbound webhooks.defineMigration/runMigration— content migrations over collections.buildStudioStructure,getCollectionsMeta— drive a studio UI.mountVisualEditing/mountPreviewSync/editAttr— live preview and click-to-edit references.