Archivos seed

En esta página

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:

  1. .emdash/seed.json.
  2. La ruta en package.json#emdash.seed.
  3. seed/seed.json.
  4. 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": []
}
PropertyRequiredPurpose
$schemaNoURL del esquema del editor
versionSíFormato seed; el único valor aceptado es "1"
defaultLocaleNoLocale para filas con locale que omiten locale; por defecto la configuración en tiempo de ejecución, luego en
metaNoNombre descriptivo, descripción y autor mostrados durante la configuración
settingsNoAjustes parciales del sitio
blockTypesNoDefiniciones versionadas usadas por campos blocks
collectionsNoDefiniciones de collection y campo
taxonomiesNoDefiniciones de taxonomía y términos opcionales
bylinesNoPerfiles opcionales de créditos de presentación
contentNoEntradas de ejemplo agrupadas por slug de collection
menusNoMenús e elementos anidados
redirectsNoReglas de redirección locales
widgetAreasNoÁreas de widgets y widgets
sectionsNoSections 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

PropertyTypeBehavior
slugstringNombre requerido de base de datos y API; comienza con una letra minúscula y contiene letras minúsculas, dígitos y guiones bajos
labelstringEtiqueta de UI plural requerida
labelSingularstringEtiqueta de UI singular opcional
descriptionstringDescripción opcional de administración
iconstringNombre de icono opcional
admin.listColumnsstring[]Hasta cuatro slugs de campo declarados mostrados en la lista de contenido
supportsstring[]Cualquiera de drafts, revisions, preview, scheduling, search y seo
urlPatternstringPatrón público como /posts/{slug}
routablebooleanSi las entradas publicadas requieren un slug; por defecto true
hiddenbooleanOculta el enlace generado de la barra lateral y la acción rápida del panel; la collection sigue siendo accesible por URL y API
sortOrdernumberPosición explícita en la barra lateral de administración; las collections ordenadas van primero, ascendente
groupstringCarpeta de la barra lateral de administración; las collections con el mismo grupo comparten una entrada plegable
commentsEnabledbooleanHabilita comentarios para la collection
editLockingbooleanHabilita bloqueos de edición; por defecto true
titleFieldstringCampo usado para el título de la lista de contenido
dateFieldstringCampo datetime usado para la fecha de la lista de contenido
fieldsSeedField[]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

PropertyTypePurpose
slugstringNombre de campo requerido con el patrón de slug de collection
labelstringEtiqueta de UI requerida
typeFieldTypeTipo de campo almacenado requerido
requiredbooleanRechaza un valor requerido vacío
uniquebooleanAñade una restricción de unicidad
searchablebooleanIncluye el campo en la búsqueda de la collection
indexedbooleanAñade un índice de consulta para tipos escalares admitidos
translatablebooleanAlmacena un valor por locale; por defecto true. false comparte un valor entre traducciones
defaultValueanyValor inicial cuando se omite el campo
validationobjectReglas de validación usadas por los esquemas de contenido generados
widgetstringAnulación del widget de campo de administración
optionsobjectOpciones específicas del widget

Los tipos de campo admitidos son:

  • string, text, url y slug.
  • number, integer y boolean.
  • datetime.
  • select y multiSelect.
  • portableText, json y repeater.
  • blocks.
  • image, file y reference.

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:

RuleUsed by
min, maxCampos numéricos
minLength, maxLength, patternCampos con forma de cadena
optionsselect y multiSelect
subFields, minItems, maxItemsrepeater
allowedTypes, minItems, maxItemsblocks
allowedMimeTypesCampos 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
		}
	]
}
PropertyTypeRequiredDescription
slugstringSíNombre único por el que un campo reference la aborda
parentCollectionstringSíCollection en el extremo padre
childCollectionstringSíCollection en el extremo hijo
parentLabelstringSíNombra el rol del padre, visto desde el hijo
parentLabelSingularstringNoForma singular de parentLabel
childLabelstringSíNombra el rol del hijo, visto desde el padre
childLabelSingularstringNoForma singular de childLabel
maxChildrenPerParentnumber | nullNoCuántos hijos puede vincular un padre (null: sin límite)
maxParentsPerChildnumber | nullNoCuá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" }
        ]
      }
    ]
  }
}
PropertyRequiredBehavior
idSíID de referencia local al seed
slugPara collections routableSlug público y clave de conflicto
statusNopublished o draft; por defecto published
dataSíValores indexados por slug de campo de collection
taxonomiesNoNombre de taxonomía a array de slugs de término
bylinesNoCréditos ordenados que referencian IDs de byline raíz
localeNoLocale BCP 47; por defecto a través de defaultLocale
translationOfNoID 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

OptionDefaultCurrent behavior
includeContentfalseIncluye entradas de contenido, bylines y términos de taxonomía
onConflict"skip""skip", "update" o "error" para conflictos de entidad admitidos
storagenoneAdaptador de almacenamiento requerido para descargar URLs $media
skipMediaDownloadfalseConserva las URLs $media como valores de medios externos
mediaBasePathnonePresente 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 category y tag con 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. skip crea ajustes faltantes y conserva los valores existentes. update sobrescribe cada ajuste suministrado. error se 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 defaultLocale no 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