Un archivo seed describe el modelo inicial y los datos de ejemplo opcionales de un sitio EmDash. Las plantillas actuales lo almacenan en seed/seed.json y apuntan a él con package.json#emdash.seed.
EmDash incrusta el seed en tiempo de compilación. Está pensado para la primera configuración y comandos seed explícitos, no como una migración que se ejecuta en cada despliegue.
Descubrimiento del archivo
La integración de Astro busca un seed en este orden:
.emdash/seed.json.- La ruta en
package.json#emdash.seed. seed/seed.json.- El seed predeterminado integrado cuando no existe un seed de usuario.
El siguiente campo del paquete selecciona la ruta convencional de la plantilla:
{
"emdash": {
"seed": "seed/seed.json"
}
}
Forma raíz
El siguiente ejemplo contiene cada propiedad raíz:
{
"$schema": "https://emdashcms.com/seed.schema.json",
"version": "1",
"defaultLocale": "en",
"meta": {
"name": "Publication",
"description": "A publication seed",
"author": "Example Studio"
},
"settings": {},
"blockTypes": [],
"collections": [],
"relations": [],
"taxonomies": [],
"bylines": [],
"content": {},
"menus": [],
"redirects": [],
"widgetAreas": [],
"sections": []
}
| Property | Required | Purpose |
|---|---|---|
$schema | No | URL del esquema del editor |
version | Sí | Formato seed; el único valor aceptado es "1" |
defaultLocale | No | Locale para filas con locale que omiten locale; por defecto la configuración en tiempo de ejecución, luego en |
meta | No | Nombre descriptivo, descripción y autor mostrados durante la configuración |
settings | No | Ajustes parciales del sitio |
blockTypes | No | Definiciones versionadas usadas por campos blocks |
collections | No | Definiciones de collection y campo |
taxonomies | No | Definiciones de taxonomía y términos opcionales |
bylines | No | Perfiles opcionales de créditos de presentación |
content | No | Entradas de ejemplo agrupadas por slug de collection |
menus | No | Menús e elementos anidados |
redirects | No | Reglas de redirección locales |
widgetAreas | No | Áreas de widgets y widgets |
sections | No | Sections reutilizables de Portable Text |
defaultLocale debe ser una cadena no vacía sin espacios iniciales ni finales.
Settings
settings es un objeto parcial de ajustes del sitio. Las propiedades habituales son title, tagline, logo, favicon, url, postsPerPage, dateFormat, timezone, social y seo.
El asistente de configuración permite al administrador reemplazar el título y el eslogan sembrados. El valor predeterminado onConflict: "skip" conserva esos valores cuando el seed se aplica de nuevo y completa cualquier ajuste suministrado que aún falte.
{
"version": "1",
"settings": {
"title": "Field Notes",
"tagline": "Reports from the team",
"postsPerPage": 12,
"dateFormat": "MMMM d, yyyy",
"timezone": "Europe/London"
}
}
Tipos de bloque
blockTypes define las formas versionadas usadas por los campos blocks de collection. EmDash aplica estas definiciones antes que las collections, de modo que un campo pueda nombrarlas en validation.allowedTypes.
El siguiente seed conserva la versión 1 para el contenido y las revisiones almacenados mientras hace activa la versión 2 para bloques nuevos:
{
"version": "1",
"blockTypes": [
{
"slug": "hero",
"label": "Hero",
"category": "Layout",
"currentVersion": 2,
"versions": [
{
"version": 1,
"fields": [
{ "slug": "heading", "label": "Heading", "type": "string", "required": true }
]
},
{
"version": 2,
"fields": [
{ "slug": "title", "label": "Title", "type": "string", "required": true },
{ "slug": "image", "label": "Image", "type": "image" }
]
}
]
}
],
"collections": [
{
"slug": "pages",
"label": "Pages",
"fields": [
{
"slug": "layout",
"label": "Layout",
"type": "blocks",
"validation": { "allowedTypes": ["hero"], "maxItems": 20 }
}
]
}
]
}
Los números de versión son enteros positivos contiguos que empiezan en 1. currentVersion debe nombrar una versión declarada. Exportar y volver a aplicar un seed conserva los números de versión exactos y el puntero activo; EmDash no los renumera.
Con onConflict: "update", un seed solo puede modificar una versión almacenada cuando la nueva definición es compatible. Reutilizar un número existente para una definición incompatible falla con BLOCK_TYPE_VERSION_CONFLICT. Añade un número de versión nuevo para una definición incompatible.
Los valores de bloque almacenados incluyen _type, _version y _key. Proporciona esas propiedades cuando el contenido del seed apunta a una versión retenida. El runtime asigna la versión activa y una clave cuando un bloque recién escrito las omite.
Collections
Una collection requiere slug, label y fields:
{
"version": "1",
"collections": [
{
"slug": "posts",
"label": "Posts",
"labelSingular": "Post",
"description": "Published articles",
"supports": ["drafts", "revisions", "scheduling", "search", "seo"],
"urlPattern": "/posts/{slug}",
"routable": true,
"commentsEnabled": true,
"editLocking": true,
"titleField": "title",
"dateField": "event_date",
"admin": {
"listColumns": ["event_date"]
},
"fields": [
{ "slug": "title", "label": "Title", "type": "string", "required": true },
{ "slug": "event_date", "label": "Event date", "type": "datetime", "indexed": true },
{ "slug": "content", "label": "Content", "type": "portableText" }
]
}
]
}
Propiedades de collection
| Property | Type | Behavior |
|---|---|---|
slug | string | Nombre requerido de base de datos y API; comienza con una letra minúscula y contiene letras minúsculas, dígitos y guiones bajos |
label | string | Etiqueta de UI plural requerida |
labelSingular | string | Etiqueta de UI singular opcional |
description | string | Descripción opcional de administración |
icon | string | Nombre de icono opcional |
admin.listColumns | string[] | Hasta cuatro slugs de campo declarados mostrados en la lista de contenido |
supports | string[] | Cualquiera de drafts, revisions, preview, scheduling, search y seo |
urlPattern | string | Patrón público como /posts/{slug} |
routable | boolean | Si las entradas publicadas requieren un slug; por defecto true |
hidden | boolean | Oculta el enlace generado de la barra lateral y la acción rápida del panel; la collection sigue siendo accesible por URL y API |
sortOrder | number | Posición explícita en la barra lateral de administración; las collections ordenadas van primero, ascendente |
group | string | Carpeta de la barra lateral de administración; las collections con el mismo grupo comparten una entrada plegable |
commentsEnabled | boolean | Habilita comentarios para la collection |
editLocking | boolean | Habilita bloqueos de edición; por defecto true |
titleField | string | Campo usado para el título de la lista de contenido |
dateField | string | Campo datetime usado para la fecha de la lista de contenido |
fields | SeedField[] | Definiciones de campo requeridas |
sortOrder pertenece a la collection y controla el orden de la barra lateral. SeedField no tiene propiedad sortOrder. Los campos se crean en el orden de su array.
Propiedades de campo
| Property | Type | Purpose |
|---|---|---|
slug | string | Nombre de campo requerido con el patrón de slug de collection |
label | string | Etiqueta de UI requerida |
type | FieldType | Tipo de campo almacenado requerido |
required | boolean | Rechaza un valor requerido vacío |
unique | boolean | Añade una restricción de unicidad |
searchable | boolean | Incluye el campo en la búsqueda de la collection |
indexed | boolean | Añade un índice de consulta para tipos escalares admitidos |
translatable | boolean | Almacena un valor por locale; por defecto true. false comparte un valor entre traducciones |
defaultValue | any | Valor inicial cuando se omite el campo |
validation | object | Reglas de validación usadas por los esquemas de contenido generados |
widget | string | Anulación del widget de campo de administración |
options | object | Opciones específicas del widget |
Los tipos de campo admitidos son:
string,text,urlyslug.number,integeryboolean.datetime.selectymultiSelect.portableText,jsonyrepeater.blocks.image,fileyreference.
Un campo reference no almacena nada en la tabla de la collection. Sus enlaces viven en la relación a la que se vincula;
consulta Relations.
Solo string, url, number, integer, boolean, datetime, select, reference y slug pueden establecer indexed: true. En un campo reference el flag solo se aplica mientras el campo no tenga relación, ya que un campo vinculado no tiene columna que indexar.
Validación de campo
El esquema de collection generado reconoce estas reglas donde el tipo de campo las admite:
| Rule | Used by |
|---|---|
min, max | Campos numéricos |
minLength, maxLength, pattern | Campos con forma de cadena |
options | select y multiSelect |
subFields, minItems, maxItems | repeater |
allowedTypes, minItems, maxItems | blocks |
allowedMimeTypes | Campos de medios |
retiredTypes en un campo blocks es gestionado por el servidor. Quitar un slug de allowedTypes lo retira para que los bloques almacenados existentes sigan siendo válidos mientras los bloques nuevos no puedan usarlo.
validateSeed() no comprueba en profundidad cada regla de validation u options. Por tanto, una regla inválida puede pasar la validación del seed y fallar más tarde cuando se construye el esquema de la collection o se escribe contenido.
Relations
Una relación une dos collections y posee los enlaces entre sus entradas. Un campo reference se vincula a
una y ve sus enlaces desde un extremo. El siguiente seed declara una relación entre posts y
authors que permite un autor por entrada:
{
"relations": [
{
"slug": "post_authors",
"parentCollection": "posts",
"childCollection": "authors",
"parentLabel": "Posts",
"parentLabelSingular": "Post",
"childLabel": "Authors",
"childLabelSingular": "Author",
"maxChildrenPerParent": 1
}
]
}
| Property | Type | Required | Description |
|---|---|---|---|
slug | string | Sí | Nombre único por el que un campo reference la aborda |
parentCollection | string | Sí | Collection en el extremo padre |
childCollection | string | Sí | Collection en el extremo hijo |
parentLabel | string | Sí | Nombra el rol del padre, visto desde el hijo |
parentLabelSingular | string | No | Forma singular de parentLabel |
childLabel | string | Sí | Nombra el rol del hijo, visto desde el padre |
childLabelSingular | string | No | Forma singular de childLabel |
maxChildrenPerParent | number | null | No | Cuántos hijos puede vincular un padre (null: sin límite) |
maxParentsPerChild | number | null | No | Cuántos padres puede vincular un hijo (null: sin límite) |
Un campo reference en collections nombra la relación a la que se vincula:
{
"slug": "author",
"label": "Author",
"type": "reference",
"validation": { "relation": "post_authors" }
}
Un campo puede nombrar un targetCollection en su lugar y hacer que se cree una relación para él, que es el
camino más corto para un enlace que solo ve una collection. Declara la relación en relations cuando ambas
collections deban verla, o para establecer sus etiquetas y límites.
reference documenta ambas formas y las claves de validación que cada
una toma.
Las dos collections de una relación quedan fijas una vez que existe: un seed que nombra otras distintas falla en lugar de
dejar que los enlaces que contiene apunten a una collection que ya no es un extremo de ella. Las etiquetas y
los límites se actualizan cuando el seed se aplica con onConflict: "update".
Taxonomías
Las definiciones de taxonomía identifican sus collections de destino. Los términos son datos de ejemplo y solo se aplican cuando includeContent es true.
{
"version": "1",
"taxonomies": [
{
"name": "category",
"label": "Categories",
"labelSingular": "Category",
"hierarchical": true,
"collections": ["posts"],
"terms": [
{ "slug": "engineering", "label": "Engineering" },
{ "slug": "platform", "label": "Platform", "parent": "engineering" }
]
}
]
}
Una taxonomía puede llevar un id local al seed, locale y translationOf. Los términos también pueden llevar esas propiedades. translationOf se refiere a otro ID local al seed. Un término debe ordenarse después del término que traduce. Las entradas de taxonomía de un name pueden aparecer en cualquier orden, porque la entrada que declara la estructura de la taxonomía se aplica antes que sus traducciones.
hierarchical y collections son compartidos por cada locale de una taxonomía, de modo que una entrada de taxonomía cuyo translationOf apunta a una entrada con el mismo name puede omitirlos. El motor de aplicación sigue translationOf a través de entradas con el mismo name y los toma de la última. La validación advierte cuando una traducción declara valores distintos de los que toma, o cuando dos entradas que los declaran para una taxonomía discrepan. Una taxonomía existente conserva sus valores a menos que una entrada sin translationOf los reemplace. Eso ocurre con onConflict: "update", y en cada modo cuando el locale de la entrada tiene la definición integrada sin editar category o tag descrita en Conflict behavior, o cuando esa definición integrada es la única de la taxonomía. La exportación las escribe solo en la entrada a la que apuntan las traducciones.
El parent del término es el slug del término padre en el mismo locale. Un padre en una taxonomía no jerárquica produce una advertencia y se ignora. Un término con translationOf y sin parent toma el padre del término que traduce.
Bylines
Los bylines raíz definen créditos de presentación. Son datos de ejemplo y requieren includeContent: true.
{
"version": "1",
"bylines": [
{
"id": "byline-editor",
"slug": "alex-editor",
"displayName": "Alex Editor",
"isGuest": true
}
]
}
El id es local al seed y lo usan los créditos de contenido. Las propiedades opcionales son bio, websiteUrl, isGuest y avatar.
Un avatar de byline apunta a un archivo que ya existe en el almacenamiento configurado:
{
"id": "byline-editor",
"slug": "alex-editor",
"displayName": "Alex Editor",
"avatar": {
"storageKey": "avatars/alex.jpg",
"filename": "alex.jpg",
"mimeType": "image/jpeg",
"alt": "Alex Editor",
"width": 400,
"height": 400
}
}
El seeding de avatar de byline crea o reutiliza una fila de medios para la clave de almacenamiento. No sube ni descarga el archivo.
Content
content agrupa entradas por slug de collection. Cada entrada requiere un id local al seed y un objeto data. Las collections routable también requieren un slug no vacío.
{
"version": "1",
"content": {
"posts": [
{
"id": "post-welcome",
"slug": "welcome",
"status": "published",
"data": {
"title": "Welcome",
"content": []
},
"taxonomies": {
"category": ["engineering"]
},
"bylines": [
{ "byline": "byline-editor", "roleLabel": "Editor" }
]
}
]
}
}
| Property | Required | Behavior |
|---|---|---|
id | Sí | ID de referencia local al seed |
slug | Para collections routable | Slug público y clave de conflicto |
status | No | published o draft; por defecto published |
data | Sí | Valores indexados por slug de campo de collection |
taxonomies | No | Nombre de taxonomía a array de slugs de término |
bylines | No | Créditos ordenados que referencian IDs de byline raíz |
locale | No | Locale BCP 47; por defecto a través de defaultLocale |
translationOf | No | ID de contenido local al seed en la misma collection |
Para una entrada routable, el id local al seed no es su identidad de base de datos. EmDash crea un ID de base de datos y registra el mapeo para referencias posteriores. Para una entrada sin slug en una collection con routable: false, EmDash usa el id del seed como ID almacenado para que la reaplicación siga siendo idempotente.
En lecturas, entry.id es el identificador de ruta de Astro y normalmente es el slug. El ID de base de datos almacenado es entry.data.id.
Referencias de contenido
Usa una cadena $ref: dentro de data para reemplazar un ID de contenido local al seed por el ID de base de datos creado:
{
"id": "event-opening",
"slug": "opening-night",
"data": {
"title": "Opening night",
"venue": "$ref:venue-main-hall"
}
}
Los destinos de referencia deben aparecer lo bastante pronto para estar presentes en el mapa de ID del motor de aplicación. Un valor $ref: sin resolver permanece como la cadena literal original; validateSeed() no lo rechaza.
Para un campo reference, declara los enlaces desde el extremo padre de su relación. Ambos extremos ven un conjunto de enlaces, de modo que un campo en la collection hija restaría enlaces que el padre ya lleva.
Referencias de medios
Usa $media en los datos de contenido para descargar una URL, subirla con el adaptador de almacenamiento suministrado, crear una fila de medios y reemplazar el objeto por un valor de campo de medios:
{
"featured_image": {
"$media": {
"url": "https://example.com/images/launch.jpg",
"filename": "launch.jpg",
"alt": "A product launch on stage",
"caption": "Launch event"
}
}
}
En un bloque Portable Text image o una imagen de gallery, $media en asset se convierte en una referencia de medios (_type: "reference", _ref, url, provider), y el texto alternativo y las dimensiones de los medios rellenan el propio alt, width y height de la imagen cuando faltan.
Dentro de una llamada de apply, las referencias repetidas a la misma URL reutilizan el valor de medios resuelto. Las referencias de medios del seed no aceptan una propiedad file local. mediaBasePath permanece en el tipo público SeedApplyOptions, pero el motor de apply actual no lo lee.
Cuando no se suministra un adaptador de almacenamiento, las referencias $media se omiten y se resuelven a null. Con skipMediaDownload: true, se convierten en valores de medios externos y no se requiere adaptador de almacenamiento.
Menús
Los menús son datos estructurales y se aplican incluso cuando includeContent es false:
{
"version": "1",
"menus": [
{
"name": "primary",
"label": "Primary navigation",
"items": [
{
"type": "page",
"label": "About",
"ref": "page-about",
"collection": "pages"
},
{
"type": "custom",
"label": "Contact",
"url": "/contact",
"target": "_self"
}
]
}
]
}
Los tipos de elemento permitidos son custom, page, post, taxonomy y collection. custom requiere url; page y post requieren ref. Los elementos pueden incluir id, translationOf, label, collection, titleAttr, cssClasses, locale, target e hijos anidados children.
Para page y post, ref nombra un ID de contenido del seed. Un destino ausente produce una advertencia de validación y un elemento de menú sin una referencia de contenido resuelta. Los elementos de menú existentes se eliminan y se recrean siempre que se aplica ese menú, independientemente de onConflict.
Redirects
Las redirecciones requieren rutas locales de origen y destino:
{
"version": "1",
"redirects": [
{
"source": "/old-path",
"destination": "/new-path",
"type": 308,
"enabled": true,
"groupName": "WordPress migration"
}
]
}
Ambas rutas deben empezar con una /. Se rechazan las URL relativas al protocolo, los segmentos de path traversal y los saltos de línea. Los códigos de estado permitidos son 301, 302, 307 y 308.
Áreas de widgets
Un área de widgets contiene widgets content, menu o component:
{
"version": "1",
"widgetAreas": [
{
"name": "sidebar",
"label": "Sidebar",
"widgets": [
{
"type": "menu",
"title": "Explore",
"menuName": "primary"
},
{
"type": "component",
"title": "Recent posts",
"componentId": "core:recent-posts",
"props": { "count": 5 }
}
]
}
]
}
Un widget de contenido almacena Portable Text en content. Un widget de menú requiere menuName. Un widget de componente requiere componentId y puede pasar props. No hay propiedad settings en SeedWidget.
Los widgets existentes en un área se eliminan y se recrean siempre que se aplica el área, independientemente de onConflict.
Sections
Las sections contienen contenido Portable Text reutilizable:
{
"version": "1",
"sections": [
{
"slug": "newsletter-signup",
"title": "Newsletter signup",
"description": "Signup call to action",
"keywords": ["newsletter", "email"],
"source": "theme",
"content": []
}
]
}
Los slugs de section contienen letras minúsculas, dígitos y guiones. source es theme, user o import; un seed lo establece por defecto en theme. Las sections de tema no se pueden eliminar en la administración. Las sections son estructurales y se aplican incluso cuando includeContent es false.
Localización
defaultLocale rellena los locales faltantes para taxonomías, términos, menús, elementos de menú y contenido. La configuración i18n activa en tiempo de ejecución tiene prioridad cuando está presente.
Las taxonomías, términos, menús, elementos de menú y contenido localizados usan campos id y translationOf locales al seed. Coloca el elemento de origen antes de una traducción para que el motor de aplicación pueda resolver su grupo de traducción. Una entrada de contenido traducida debe establecer locale, y su translationOf debe nombrar otra entrada en la misma collection.
Aplicar un seed de forma programática
applySeed() y validateSeed() se exportan desde emdash/seed. El siguiente helper valida antes de aplicar:
import {
applySeed,
validateSeed,
type SeedApplyOptions,
type SeedFile,
} from "emdash/seed";
type SeedDatabase = Parameters<typeof applySeed>[0];
export async function applyProjectSeed(
db: SeedDatabase,
seed: SeedFile,
options: SeedApplyOptions,
) {
const validation = validateSeed(seed);
if (!validation.valid) {
throw new Error(validation.errors.join("\n"));
}
return applySeed(db, seed, options);
}
SeedApplyOptions
| Option | Default | Current behavior |
|---|---|---|
includeContent | false | Incluye entradas de contenido, bylines y términos de taxonomía |
onConflict | "skip" | "skip", "update" o "error" para conflictos de entidad admitidos |
storage | none | Adaptador de almacenamiento requerido para descargar URLs $media |
skipMediaDownload | false | Conserva las URLs $media como valores de medios externos |
mediaBasePath | none | Presente en el tipo público pero no usado por el motor de apply actual |
La aplicación programática establece includeContent en false por defecto. El asistente de configuración pasa la elección de contenido de ejemplo del administrador. La CLI emdash seed incluye contenido por defecto a menos que se establezca --no-content.
Comportamiento de conflictos
onConflict no es una política de transacción para todo el seed:
- Collections, campos, bylines, contenido, redirects y sections admiten comportamiento de omitir, actualizar y error.
- Las definiciones y términos de taxonomía respetan el modo de conflicto aplicable. La excepción son las definiciones integradas
categoryytagcon las que empieza cada base de datos nueva: hasta que un sitio las edite, un seed que las declare las reemplaza en cada modo. - Settings usan manejo de conflictos por clave.
skipcrea ajustes faltantes y conserva los valores existentes.updatesobrescribe cada ajuste suministrado.errorse detiene en el primer ajuste existente; los ajustes creados antes en el orden del seed permanecen aplicados. - Los menús existentes conservan su fila de menú pero reemplazan todos los elementos.
- Las áreas de widgets existentes conservan su fila de área pero reemplazan todos los widgets.
- Un conflicto de contenido se empareja por collection, slug y locale. Una entrada sin slug en una collection no routable se empareja por su ID de seed.
Con onConflict: "update", los datos de contenido se reemplazan y sus asignaciones de byline y taxonomía se concilian con el seed. Prueba el modo de actualización en una copia antes de usarlo contra un sitio existente.
applySeed() devuelve contadores para collections, campos, taxonomías, bylines, menús, redirects, áreas de widgets, sections, settings, contenido y medios.
Comportamiento de validación
validateSeed() devuelve { valid, errors, warnings }. applySeed() lo llama y lanza Invalid seed file cuando hay errores.
El validador comprueba las reglas estructurales que requiere el motor de aplicación, incluyendo:
- Versión y
defaultLocaleno vacío. - Formas de contenedores de collection, campo, taxonomía, término, menú, área de widgets, section, byline y contenido.
- Nombres, etiquetas, IDs, slugs requeridos y tipos de campo o widget admitidos.
- Identificadores duplicados en su ámbito relevante.
- Tipos de campo indexados y referencias de
admin.listColumns. - Padres de taxonomía, traducciones de contenido, referencias de byline de contenido y requisitos de elementos de menú.
- Rutas de redirección locales seguras y códigos de estado.
Algunas condiciones son advertencias en lugar de errores. Ejemplos: una taxonomía sin collections, un padre en una taxonomía plana o una referencia de contenido de menú ausente del seed.
El validador no demuestra que todos los valores data se ajusten a sus campos de collection. Tampoco valida en profundidad los ajustes del sitio, la validation del campo, las options del campo, bloques Portable Text arbitrarios, props de widgets de componente, destinos $ref: en datos de contenido ni la disponibilidad remota de $media. Un seed válido aún puede fallar durante la creación del esquema, la validación de contenido, la descarga de red o la subida al almacenamiento.
Usa la URL $schema para ayuda del editor y ejecuta el validador ejecutable antes de aplicar:
npx emdash seed seed/seed.json --validate
Comandos CLI
Aplica un seed a una base de datos SQLite local con comportamiento de conflicto explícito:
npx emdash seed seed/seed.json --database ./data.db --on-conflict skip
Exporta el modelo local actual y todo el contenido de vuelta a la ruta de la plantilla:
npx emdash export-seed --database ./data.db --with-content=all > seed/seed.json
export-seed trabaja directamente sobre un archivo SQLite local. Para una base de datos D1 desplegada, expórtala primero a un archivo local. Revisa los ajustes, el contenido y las referencias de medios exportados antes de confirmar el resultado. Para hacer importables en otro sitio los medios exportados, pasa --media-base-url; consulta Media URLs.
Siguientes pasos
- Create a theme para usar un seed en una plantilla Astro reutilizable.
- Schema evolution para actualizar el modelo de un sitio desplegado existente.
- CLI reference para opciones de base de datos y exportación.