Un file seed descrive il modello iniziale e i dati di esempio opzionali per un sito EmDash. I template attuali lo memorizzano in seed/seed.json e lo indicano con package.json#emdash.seed.
EmDash incorpora il seed in fase di build. È destinato alla prima configurazione e ai comandi seed espliciti, non come migrazione che viene eseguita a ogni deployment.
Individuazione del file
L’integrazione Astro cerca un seed in questo ordine:
.emdash/seed.json.- Il percorso in
package.json#emdash.seed. seed/seed.json.- Il seed predefinito incorporato quando non esiste un seed utente.
Il seguente campo del pacchetto seleziona il percorso convenzionale del template:
{
"emdash": {
"seed": "seed/seed.json"
}
}
Forma radice
Il seguente esempio contiene ogni proprietà radice:
{
"$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 dello schema dell’editor |
version | Sì | Formato seed; l’unico valore accettato è "1" |
defaultLocale | No | Locale per le righe con locale che omettono locale; predefinito la configurazione runtime, poi en |
meta | No | Nome descrittivo, descrizione e autore mostrati durante la configurazione |
settings | No | Impostazioni parziali del sito |
blockTypes | No | Definizioni versionate usate dai campi blocks |
collections | No | Definizioni di collection e campo |
taxonomies | No | Definizioni di tassonomia e termini opzionali |
bylines | No | Profili opzionali di crediti di presentazione |
content | No | Voci di esempio raggruppate per slug di collection |
menus | No | Menu ed elementi annidati |
redirects | No | Regole di redirect locali |
widgetAreas | No | Aree widget e widget |
sections | No | Section Portable Text riutilizzabili |
defaultLocale deve essere una stringa non vuota senza spazi iniziali o finali.
Settings
settings è un oggetto parziale di impostazioni del sito. Le proprietà comuni sono title, tagline, logo, favicon, url, postsPerPage, dateFormat, timezone, social e seo.
La procedura guidata di configurazione consente all’amministratore di sostituire il titolo e il tagline seedati. Il valore predefinito onConflict: "skip" conserva quei valori quando il seed viene riapplicato e completa qualsiasi impostazione fornita ancora mancante.
{
"version": "1",
"settings": {
"title": "Field Notes",
"tagline": "Reports from the team",
"postsPerPage": 12,
"dateFormat": "MMMM d, yyyy",
"timezone": "Europe/London"
}
}
Tipi di blocco
blockTypes definisce le forme versionate usate dai campi blocks delle collection. EmDash applica queste definizioni prima delle collection, così un campo può nominarle in validation.allowedTypes.
Il seguente seed mantiene la versione 1 per il contenuto e le revisioni memorizzati mentre rende attiva la versione 2 per i nuovi blocchi:
{
"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 }
}
]
}
]
}
I numeri di versione sono interi positivi contigui a partire da 1. currentVersion deve nominare una versione dichiarata. Esportare e riapplicare un seed conserva i numeri di versione esatti e il puntatore attivo; EmDash non li rinumera.
Con onConflict: "update", un seed può modificare una versione memorizzata solo quando la nuova definizione è compatibile. Riutilizzare un numero esistente per una definizione incompatibile fallisce con BLOCK_TYPE_VERSION_CONFLICT. Aggiungi un nuovo numero di versione per una definizione incompatibile.
I valori di blocco memorizzati includono _type, _version e _key. Fornisci quelle proprietà quando il contenuto del seed punta a una versione trattenuta. Il runtime assegna la versione attiva e una chiave quando un blocco appena scritto le omette.
Collections
Una collection richiede slug, label e 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" }
]
}
]
}
Proprietà della collection
| Property | Type | Behavior |
|---|---|---|
slug | string | Nome richiesto di database e API; inizia con una lettera minuscola e contiene lettere minuscole, cifre e underscore |
label | string | Etichetta UI plurale richiesta |
labelSingular | string | Etichetta UI singolare opzionale |
description | string | Descrizione di amministrazione opzionale |
icon | string | Nome icona opzionale |
admin.listColumns | string[] | Fino a quattro slug di campo dichiarati mostrati nell’elenco dei contenuti |
supports | string[] | Qualunque di drafts, revisions, preview, scheduling, search e seo |
urlPattern | string | Pattern pubblico come /posts/{slug} |
routable | boolean | Se le voci pubblicate richiedono uno slug; predefinito true |
hidden | boolean | Nasconde il link generato della barra laterale e l’azione rapida della dashboard; la collection resta raggiungibile per URL e API |
sortOrder | number | Posizione esplicita nella barra laterale di amministrazione; le collection ordinate vengono prima, in ordine crescente |
group | string | Cartella della barra laterale di amministrazione; le collection con lo stesso gruppo condividono una voce comprimibile |
commentsEnabled | boolean | Abilita i commenti per la collection |
editLocking | boolean | Abilita i lock di modifica; predefinito true |
titleField | string | Campo usato per il titolo dell’elenco dei contenuti |
dateField | string | Campo datetime usato per la data dell’elenco dei contenuti |
fields | SeedField[] | Definizioni di campo richieste |
sortOrder appartiene alla collection e controlla l’ordine della barra laterale. SeedField non ha proprietà sortOrder. I campi vengono creati nell’ordine del loro array.
Proprietà del campo
| Property | Type | Purpose |
|---|---|---|
slug | string | Nome di campo richiesto con il pattern di slug della collection |
label | string | Etichetta UI richiesta |
type | FieldType | Tipo di campo memorizzato richiesto |
required | boolean | Rifiuta un valore richiesto vuoto |
unique | boolean | Aggiunge un vincolo di univocità |
searchable | boolean | Include il campo nella ricerca della collection |
indexed | boolean | Aggiunge un indice di query per i tipi scalari supportati |
translatable | boolean | Memorizza un valore per locale; predefinito true. false condivide un valore tra le traduzioni |
defaultValue | any | Valore iniziale quando il campo viene omesso |
validation | object | Regole di validazione usate dagli schemi di contenuto generati |
widget | string | Override del widget di campo di amministrazione |
options | object | Opzioni specifiche del widget |
I tipi di campo supportati sono:
string,text,urleslug.number,integereboolean.datetime.selectemultiSelect.portableText,jsonerepeater.blocks.image,fileereference.
Un campo reference non memorizza nulla nella tabella della collection. I suoi link vivono nella relazione a cui è legato;
vedi Relations.
Solo string, url, number, integer, boolean, datetime, select, reference e slug possono impostare indexed: true. Su un campo reference il flag si applica solo mentre il campo non ha relazione, poiché un campo legato non ha una colonna da indicizzare.
Validazione del campo
Lo schema di collection generato riconosce queste regole dove il tipo di campo le supporta:
| Rule | Used by |
|---|---|
min, max | Campi numerici |
minLength, maxLength, pattern | Campi a forma di stringa |
options | select e multiSelect |
subFields, minItems, maxItems | repeater |
allowedTypes, minItems, maxItems | blocks |
allowedMimeTypes | Campi media |
retiredTypes su un campo blocks è gestito dal server. Rimuovere uno slug da allowedTypes lo ritira così i blocchi memorizzati esistenti restano validi mentre i nuovi blocchi non possono usarlo.
validateSeed() non controlla in profondità ogni regola in validation o options. Una regola non valida può quindi superare la validazione del seed e fallire in seguito quando lo schema della collection viene creato o viene scritto contenuto.
Relations
Una relazione unisce due collection e possiede i link tra le loro voci. Un campo reference si lega a
una e vede i suoi link da un’estremità. Il seguente seed dichiara una relazione tra posts e
authors che consente un autore per articolo:
{
"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ì | Nome univoco con cui un campo reference la indirizza |
parentCollection | string | Sì | Collection all’estremità padre |
childCollection | string | Sì | Collection all’estremità figlio |
parentLabel | string | Sì | Nomina il ruolo del padre, visto dal figlio |
parentLabelSingular | string | No | Forma singolare di parentLabel |
childLabel | string | Sì | Nomina il ruolo del figlio, visto dal padre |
childLabelSingular | string | No | Forma singolare di childLabel |
maxChildrenPerParent | number | null | No | Quanti figli un padre può collegare (null: nessun limite) |
maxParentsPerChild | number | null | No | Quanti padri un figlio può collegare (null: nessun limite) |
Un campo reference in collections nomina la relazione a cui si lega:
{
"slug": "author",
"label": "Author",
"type": "reference",
"validation": { "relation": "post_authors" }
}
Un campo può nominare invece un targetCollection e far creare una relazione per esso, che è il
percorso più breve per un link che solo una collection vede. Dichiara la relazione in relations quando entrambe
le collection devono vederla, o per impostarne etichette e limiti.
reference documenta entrambe le forme e le chiavi di validazione che ciascuna
accetta.
Le due collection di una relazione sono fisse una volta che esiste: un seed che ne nomina di diverse fallisce piuttosto che
lasciare che i link che detiene puntino a una collection che non ne è più un’estremità. Etichette e
limiti vengono aggiornati quando il seed viene applicato con onConflict: "update".
Tassonomie
Le definizioni di tassonomia identificano le loro collection di destinazione. I termini sono dati di esempio e vengono applicati solo quando includeContent è 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 tassonomia può portare un id locale al seed, locale e translationOf. Anche i termini possono portare quelle proprietà. translationOf si riferisce a un altro ID locale al seed. Un termine deve essere ordinato dopo il termine che traduce. Le voci di tassonomia di un name possono apparire in qualsiasi ordine, perché la voce che dichiara la struttura della tassonomia viene applicata prima delle sue traduzioni.
hierarchical e collections sono condivisi da ogni locale di una tassonomia, così una voce di tassonomia il cui translationOf punta a una voce con lo stesso name può ometterli. Il motore di apply segue translationOf attraverso le voci con lo stesso name e li prende dall’ultima. La validazione avvisa quando una traduzione dichiara valori diversi da quelli che prende, o quando due voci che li dichiarano per una tassonomia non concordano. Una tassonomia esistente conserva i suoi valori a meno che una voce senza translationOf non li sostituisca. Ciò avviene con onConflict: "update", e in ogni modalità quando il locale della voce ha la definizione integrata non modificata category o tag descritta in Conflict behavior, o quando quella definizione integrata è l’unica della tassonomia. L’export le scrive solo sulla voce a cui puntano le traduzioni.
Il parent del termine è lo slug del termine padre nello stesso locale. Un padre su una tassonomia non gerarchica produce un avviso e viene ignorato. Un termine con translationOf e senza parent prende il padre del termine che traduce.
Bylines
I bylines radice definiscono crediti di presentazione. Sono dati di esempio e richiedono includeContent: true.
{
"version": "1",
"bylines": [
{
"id": "byline-editor",
"slug": "alex-editor",
"displayName": "Alex Editor",
"isGuest": true
}
]
}
L’id è locale al seed e viene usato dai crediti di contenuto. Le proprietà opzionali sono bio, websiteUrl, isGuest e avatar.
Un avatar di byline punta a un file che esiste già nello storage configurato:
{
"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
}
}
Il seeding dell’avatar di byline crea o riutilizza una riga media per la chiave di storage. Non carica né scarica il file.
Content
content raggruppa le voci per slug di collection. Ogni voce richiede un id locale al seed e un oggetto data. Le collection routable richiedono anche uno slug non vuoto.
{
"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 di riferimento locale al seed |
slug | Per le collection routable | Slug pubblico e chiave di conflitto |
status | No | published o draft; predefinito published |
data | Sì | Valori indicizzati per slug di campo della collection |
taxonomies | No | Nome della tassonomia a array di slug di termine |
bylines | No | Crediti ordinati che fanno riferimento a ID di byline radice |
locale | No | Locale BCP 47; predefinito tramite defaultLocale |
translationOf | No | ID di contenuto locale al seed nella stessa collection |
Per una voce routable, l’id locale al seed non è la sua identità di database. EmDash crea un ID di database e registra il mapping per i riferimenti successivi. Per una voce senza slug in una collection con routable: false, EmDash usa l’id del seed come ID memorizzato così la riapplicazione resta idempotente.
In lettura, entry.id è l’identificatore di route Astro e normalmente è lo slug. L’ID di database memorizzato è entry.data.id.
Riferimenti di contenuto
Usa una stringa $ref: dentro data per sostituire un ID di contenuto locale al seed con l’ID di database creato:
{
"id": "event-opening",
"slug": "opening-night",
"data": {
"title": "Opening night",
"venue": "$ref:venue-main-hall"
}
}
Le destinazioni di riferimento devono apparire abbastanza presto da essere presenti nella mappa ID del motore di apply. Un valore $ref: non risolto rimane come stringa letterale originale; validateSeed() non lo rifiuta.
Per un campo reference, dichiara i link dall’estremità padre della sua relazione. Entrambe le estremità vedono un insieme di link, così un campo sulla collection figlio riaffermerebbe link che il padre porta già.
Riferimenti media
Usa $media nei dati di contenuto per scaricare un URL, caricarlo con l’adapter di storage fornito, creare una riga media e sostituire l’oggetto con un valore di campo media:
{
"featured_image": {
"$media": {
"url": "https://example.com/images/launch.jpg",
"filename": "launch.jpg",
"alt": "A product launch on stage",
"caption": "Launch event"
}
}
}
In un blocco Portable Text image o un’immagine di gallery, $media in asset diventa un riferimento media (_type: "reference", _ref, url, provider), e il testo alternativo e le dimensioni dei media riempiono alt, width e height dell’immagine quando mancano.
All’interno di una chiamata di apply, i riferimenti ripetuti allo stesso URL riutilizzano il valore media risolto. I riferimenti media del seed non accettano una proprietà file locale. mediaBasePath rimane nel tipo pubblico SeedApplyOptions, ma il motore di apply attuale non lo legge.
Quando non viene fornito un adapter di storage, i riferimenti $media vengono saltati e si risolvono in null. Con skipMediaDownload: true, diventano valori media esterni e non è richiesto un adapter di storage.
Menu
I menu sono dati strutturali e vengono applicati anche quando includeContent è 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"
}
]
}
]
}
I tipi di elemento consentiti sono custom, page, post, taxonomy e collection. custom richiede url; page e post richiedono ref. Gli elementi possono includere id, translationOf, label, collection, titleAttr, cssClasses, locale, target e children annidati.
Per page e post, ref nomina un ID di contenuto del seed. Una destinazione mancante produce un avviso di validazione e un elemento di menu senza un riferimento di contenuto risolto. Gli elementi di menu esistenti vengono eliminati e ricreati ogni volta che quel menu viene applicato, indipendentemente da onConflict.
Redirect
I redirect richiedono percorsi di origine e destinazione locali:
{
"version": "1",
"redirects": [
{
"source": "/old-path",
"destination": "/new-path",
"type": 308,
"enabled": true,
"groupName": "WordPress migration"
}
]
}
Entrambi i percorsi devono iniziare con una /. URL relativi al protocollo, segmenti di path traversal e newline vengono rifiutati. I codici di stato consentiti sono 301, 302, 307 e 308.
Aree widget
Un’area widget contiene widget 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 di contenuto memorizza Portable Text in content. Un widget di menu richiede menuName. Un widget di componente richiede componentId e può passare props. Non c’è proprietà settings su SeedWidget.
I widget esistenti in un’area vengono eliminati e ricreati ogni volta che l’area viene applicata, indipendentemente da onConflict.
Section
Le section contengono contenuto Portable Text riutilizzabile:
{
"version": "1",
"sections": [
{
"slug": "newsletter-signup",
"title": "Newsletter signup",
"description": "Signup call to action",
"keywords": ["newsletter", "email"],
"source": "theme",
"content": []
}
]
}
Gli slug delle section contengono lettere minuscole, cifre e trattini. source è theme, user o import; un seed lo imposta per impostazione predefinita su theme. Le section di tema non possono essere eliminate nell’amministrazione. Le section sono strutturali e vengono applicate anche quando includeContent è false.
Localizzazione
defaultLocale riempie i locale mancanti per tassonomie, termini, menu, elementi di menu e contenuto. La configurazione i18n runtime attiva ha la precedenza quando presente.
Tassonomie, termini, menu, elementi di menu e contenuto localizzati usano i campi id e translationOf locali al seed. Colloca l’elemento sorgente prima di una traduzione così il motore di apply può risolvere il suo gruppo di traduzione. Una voce di contenuto tradotta deve impostare locale e il suo translationOf deve nominare un’altra voce nella stessa collection.
Applicare un seed in modo programmatico
applySeed() e validateSeed() sono esportati da emdash/seed. Il seguente helper convalida prima di applicare:
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 | Include voci di contenuto, byline e termini di tassonomia |
onConflict | "skip" | "skip", "update" o "error" per i conflitti di entità supportati |
storage | none | Adapter di storage richiesto per scaricare URL $media |
skipMediaDownload | false | Mantiene gli URL $media come valori media esterni |
mediaBasePath | none | Presente nel tipo pubblico ma non usato dal motore di apply attuale |
L’applicazione programmatica imposta includeContent su false per impostazione predefinita. La procedura guidata di configurazione passa la scelta di contenuto di esempio dell’amministratore. La CLI emdash seed include il contenuto per impostazione predefinita a meno che non sia impostato --no-content.
Comportamento di conflitto
onConflict non è una politica di transazione per l’intero seed:
- Collection, campi, byline, contenuto, redirect e section supportano i comportamenti skip, update ed error.
- Le definizioni e i termini di tassonomia rispettano la modalità di conflitto applicabile. L’eccezione sono le definizioni integrate
categoryetagcon cui inizia ogni nuovo database: finché un sito non le modifica, un seed che le dichiara le sostituisce in ogni modalità. - Settings usano la gestione dei conflitti per chiave.
skipcrea le impostazioni mancanti e conserva i valori esistenti.updatesovrascrive ogni impostazione fornita.errorsi ferma alla prima impostazione esistente; le impostazioni create prima nell’ordine del seed restano applicate. - I menu esistenti conservano la loro riga di menu ma sostituiscono tutti gli elementi.
- Le aree widget esistenti conservano la loro riga di area ma sostituiscono tutti i widget.
- Un conflitto di contenuto viene abbinato per collection, slug e locale. Una voce senza slug in una collection non routable viene abbinata per il suo ID di seed.
Con onConflict: "update", i dati di contenuto vengono sostituiti e le sue assegnazioni di byline e tassonomia vengono riconciliate con il seed. Prova la modalità update su una copia prima di usarla contro un sito esistente.
applySeed() restituisce contatori per collection, campi, tassonomie, byline, menu, redirect, aree widget, section, settings, contenuto e media.
Comportamento di validazione
validateSeed() restituisce { valid, errors, warnings }. applySeed() lo chiama e lancia Invalid seed file quando sono presenti errori.
Il validatore controlla le regole strutturali richieste dal motore di apply, tra cui:
- Versione e
defaultLocalenon vuoto. - Forme di contenitori di collection, campo, tassonomia, termine, menu, area widget, section, byline e contenuto.
- Nomi, etichette, ID, slug richiesti e tipi di campo o widget supportati.
- Identificatori duplicati nel loro ambito pertinente.
- Tipi di campo indicizzati e riferimenti
admin.listColumns. - Padri di tassonomia, traduzioni di contenuto, riferimenti di byline di contenuto e requisiti degli elementi di menu.
- Percorsi di redirect locali sicuri e codici di stato.
Alcune condizioni sono avvisi anziché errori. Esempi: una tassonomia senza collection, un padre su una tassonomia piatta o un riferimento di contenuto di menu assente dal seed.
Il validatore non dimostra che tutti i valori data siano conformi ai loro campi di collection. Non valida nemmeno in profondità le impostazioni del sito, la validation del campo, le options del campo, blocchi Portable Text arbitrari, props di widget di componente, destinazioni $ref: nei dati di contenuto o la disponibilità remota di $media. Un seed valido può comunque fallire durante la creazione dello schema, la validazione del contenuto, il download di rete o il caricamento nello storage.
Usa l’URL $schema per l’assistenza dell’editor ed esegui il validatore eseguibile prima di applicare:
npx emdash seed seed/seed.json --validate
Comandi CLI
Applica un seed a un database SQLite locale con comportamento di conflitto esplicito:
npx emdash seed seed/seed.json --database ./data.db --on-conflict skip
Esporta il modello locale corrente e tutto il contenuto di nuovo nel percorso del template:
npx emdash export-seed --database ./data.db --with-content=all > seed/seed.json
export-seed lavora direttamente su un file SQLite locale. Per un database D1 distribuito, esportalo prima in un file locale. Rivedi impostazioni, contenuto e riferimenti media esportati prima di fare commit del risultato. Per rendere i media esportati importabili su un altro sito, passa --media-base-url; vedi Media URLs.
Passi successivi
- Create a theme per usare un seed in un template Astro riutilizzabile.
- Schema evolution per aggiornare il modello di un sito distribuito esistente.
- CLI reference per le opzioni di database ed export.