Collections
A collection is the combination of three things, all derived from one source of truth:
- Field descriptors (
src/descriptors/builders, used by each manifest) — define the content shape. - An editor manifest (
src/editor/manifests/<slug>.ts) — wires the fields, routes, and access predicates into the admin's/cmssurface. - A Convex table (
packages/convex/convex/tables/cms.ts) — generated from the descriptors bypnpm cms:gen; every content table keys on the shop id with aby_shopindex.
| Collection | Manifest | Notes |
|---|---|---|
pages | src/editor/manifests/pages.ts | Slug + blocks. Drafts + autosave. |
articles | src/editor/manifests/articles.ts | Slug + rich-text body. Drafts. |
productMetadata | src/editor/manifests/product-metadata.ts | Per-shopifyHandle SEO/marketing overrides. |
collectionMetadata | src/editor/manifests/collection-metadata.ts | Per-shopifyHandle overrides for Shopify collections. |
header | src/editor/manifests/header.ts | Singleton — one row per tenant. |
footer | src/editor/manifests/footer.ts | Singleton — one row per tenant. |
businessData | src/editor/manifests/business-data.ts | Singleton — addresses, phone, support email, etc. |
media | src/editor/manifests/media.tsx | Uploads land in Convex file storage; legacy assets resolve via the S3_*/R2 fallback URLs. |
users | src/editor/manifests/users.ts | Editor accounts. |
shops | src/editor/manifests/shop.ts | Edit surface over the Convex shops table (name, domain, design); provider secrets never reach the editor. |
reviews | src/editor/manifests/review.ts | Tenant-scoped product reviews. |
feature-flags | src/editor/manifests/feature-flag.ts | Admin-only; keyed by key. |
allManifests (src/editor/manifests/index.ts) is the registry the admin app
mounts. The legacy tenants slug survives only as an editor-route alias rendering
an empty surface (legacy-tenants-slug.ts) — shop == tenant since the Convex
migration, so there is no tenants collection.
Access predicates
Editor access is declared per manifest with the predicates from
src/editor/access.ts:
import { adminOnly, editorOrAdmin, tenantMember } from '@nordcom/commerce-cms/editor';| Predicate | Grants |
|---|---|
adminOnly | Platform admins only (destructive operations, feature flags). |
editorOrAdmin | Any signed-in editor or admin (cross-tenant list surfaces). |
tenantMember | Admins, or editors whose tenants include the route's tenant id. |
These gate the editor routes; the Convex functions enforce the same boundary server-side via their tenant-aware constructors, so a forged route never reads another tenant's rows.
Revalidation
There are no collection hooks to wire — publishing a document runs the Convex
revalidation pipeline (packages/convex/convex/revalidate/), which fans out the
three CMS cache tags (cms.<shopId>.<collection>.<key> /
cms.<shopId>.<collection> / cms.<shopId>) and delivers them to the
storefront's /api/revalidate/convex route as signed events.
Adding a collection
See Adding a collection: declare the descriptors,
register a manifest, run pnpm cms:gen, and commit the regenerated artifacts.