Referencia de la API de JavaScript
EmDash exporta funciones para consultar contenido, gestionar medios y trabajar con la base de datos.
Consultas de contenido
Sección titulada «Consultas de contenido»Las funciones de consulta de EmDash siguen el patrón de colecciones de contenido en vivo de Astro, devolviendo { entries, error } o { entry, error } para un manejo elegante de errores.
getEmDashCollection()
Sección titulada «getEmDashCollection()»Obtiene todas las entradas de una colección.
import { getEmDashCollection } from "emdash";
const { entries: posts, error } = await getEmDashCollection("posts");
if (error) { console.error("No se pudieron cargar las publicaciones:", error);}Parámetros
Sección titulada «Parámetros»| Parámetro | Tipo | Descripción |
|---|---|---|
collection | string | Slug de la colección |
options | CollectionFilter | Opciones de filtro opcionales |
Opciones
Sección titulada «Opciones»interface CollectionFilter { status?: "draft" | "published" | "archived"; limit?: number; where?: Record<string, string | string[]>; // Filtra por campo o taxonomía}Devuelve
Sección titulada «Devuelve»interface CollectionResult<T> { entries: ContentEntry<T>[]; // Array vacío si hay error o no se encuentra nada error?: Error; // Se establece si la consulta falla}Ejemplos
Sección titulada «Ejemplos»// Obtener todas las publicaciones publicadasconst { entries: posts } = await getEmDashCollection("posts", { status: "published",});
// Obtener los últimos 5 postsconst { entries: latest } = await getEmDashCollection("posts", { limit: 5, status: "published",});
// Filtrar por taxonomíaconst { entries: newsPosts } = await getEmDashCollection("posts", { status: "published", where: { category: "news" },});
// Manejar erroresconst { entries, error } = await getEmDashCollection("posts");if (error) { return new Response("Error del servidor", { status: 500 });}getEmDashEntry()
Sección titulada «getEmDashEntry()»Obtiene una sola entrada por slug o ID.
import { getEmDashEntry } from "emdash";
const { entry: post, error } = await getEmDashEntry("posts", "my-post-slug");
if (!post) { return Astro.redirect("/404");}Parámetros
Sección titulada «Parámetros»| Parámetro | Tipo | Descripción |
|---|---|---|
collection | string | Slug de la colección |
slugOrId | string | Slug o ID de la entrada |
El modo de vista previa se maneja automáticamente — el middleware detecta los tokens _preview y sirve contenido borrador a través de AsyncLocalStorage. No se necesita un parámetro de opciones.
Devuelve
Sección titulada «Devuelve»interface EntryResult<T> { entry: ContentEntry<T> | null; // null si no se encuentra error?: Error; // Solo se establece en errores reales, no en "no encontrado" isPreview: boolean; // true si se está sirviendo contenido en borrador}Ejemplos
Sección titulada «Ejemplos»// Obtener por slugconst { entry: post } = await getEmDashEntry("posts", "hello-world");
// Obtener por IDconst { entry: post } = await getEmDashEntry("posts", "01HXK5MZSN0FVXT2Q3KPRT9M7D");
// La vista previa es automática — isPreview es true cuando hay un token _preview válido presenteconst { entry, isPreview, error } = await getEmDashEntry("posts", slug);
// Manejar errores vs no encontradoif (error) { return new Response("Error del servidor", { status: 500 });}if (!entry) { return Astro.redirect("/404");}Tipos de Contenido
Sección titulada «Tipos de Contenido»ContentEntry
Sección titulada «ContentEntry»La entrada devuelta por las funciones de consulta:
interface ContentEntry<T = Record<string, unknown>> { id: string; data: T; edit: EditProxy; // Anotaciones para edición visual}El proxy edit proporciona anotaciones de edición visual. Extiéndelo sobre elementos para habilitar la edición en línea: {...entry.edit.title}. En producción, esto no produce salida.
El objeto data contiene todos los campos de contenido más los campos del sistema:
id- Identificador únicoslug- Identificador amigable para URLstatus- “draft” | “published” | “archived”createdAt- Marca de tiempo ISOupdatedAt- Marca de tiempo ISOpublishedAt- Marca de tiempo ISO o null- Más todos los campos personalizados definidos en el esquema de tu colección
Funciones de base de datos
Sección titulada «Funciones de base de datos»createDatabase()
Sección titulada «createDatabase()»Crea una conexión a la base de datos.
import { createDatabase } from "emdash";
const db = createDatabase({ url: "file:./data.db" });runMigrations()
Sección titulada «runMigrations()»Ejecuta migraciones pendientes de la base de datos.
import { createDatabase, runMigrations } from "emdash";
const db = createDatabase({ url: "file:./data.db" });const { applied } = await runMigrations(db);console.log(`Se aplicaron ${applied.length} migraciones`);getMigrationStatus()
Sección titulada «getMigrationStatus()»Verifica el estado de las migraciones.
import { createDatabase, getMigrationStatus } from "emdash";
const db = createDatabase({ url: "file:./data.db" });const status = await getMigrationStatus(db);// { applied: ["0001_core", ...], pending: [] }Repositorios
Sección titulada «Repositorios»Acceso de bajo nivel a los datos mediante repositorios.
ContentRepository
Sección titulada «ContentRepository»import { ContentRepository, createDatabase } from "emdash";
const db = createDatabase({ url: "file:./data.db" });const repo = new ContentRepository(db);
// Buscar variosconst { items, nextCursor } = await repo.findMany("posts", { limit: 10, where: { status: "published" },});
// Buscar por IDconst post = await repo.findById("posts", "01HXK5MZSN...");
// Crearconst newPost = await repo.create({ type: "posts", slug: "new-post", data: { title: "Nueva publicación", content: [] }, status: "draft",});
// Actualizarconst updated = await repo.update("posts", "01HXK5MZSN...", { data: { title: "Título actualizado" },});
// Eliminarawait repo.delete("posts", "01HXK5MZSN...");MediaRepository
Sección titulada «MediaRepository»import { MediaRepository, createDatabase } from "emdash";
const db = createDatabase({ url: "file:./data.db" });const repo = new MediaRepository(db);
// Listar archivos multimediaconst { items } = await repo.findMany({ limit: 20 });
// Obtener por IDconst media = await repo.findById("01HXK5MZSN...");
// Crear (después de la subida)const newMedia = await repo.create({ filename: "photo.jpg", mimeType: "image/jpeg", size: 12345, storageKey: "uploads/photo.jpg",});Registro de Esquemas
Sección titulada «Registro de Esquemas»Gestión programática de esquemas.
import { SchemaRegistry, createDatabase } from "emdash";
const db = createDatabase({ url: "file:./data.db" });const registry = new SchemaRegistry(db);
// Listar coleccionesconst collections = await registry.listCollections();
// Obtener colección con camposconst postsSchema = await registry.getCollectionWithFields("posts");
// Crear colecciónawait registry.createCollection({ slug: "products", label: "Productos", labelSingular: "Producto", supports: ["drafts", "revisions"],});
// Añadir campoawait registry.createField("products", { slug: "price", label: "Precio", type: "number", required: true,});Sistema de Vista Previa
Sección titulada «Sistema de Vista Previa»generatePreviewToken()
Sección titulada «generatePreviewToken()»Genera un token de vista previa para contenido borrador.
import { generatePreviewToken } from "emdash";
const token = await generatePreviewToken({ contentId: "posts:01HXK5MZSN...", secret: process.env.EMDASH_ADMIN_SECRET, expiresIn: 3600, // 1 hora});verifyPreviewToken()
Sección titulada «verifyPreviewToken()»Verifica un token de vista previa.
import { verifyPreviewToken } from "emdash";
const result = await verifyPreviewToken({ token, secret: process.env.EMDASH_ADMIN_SECRET,});
if (result.valid) { const { cid, exp, iat } = result.payload; // cid está en formato "colección:id", ej. "posts:my-draft-post"}isPreviewRequest()
Sección titulada «isPreviewRequest()»Verifica si una solicitud incluye un token de vista previa.
import { isPreviewRequest, getPreviewToken } from "emdash";
if (isPreviewRequest(Astro.request)) { const token = getPreviewToken(Astro.request); // Verificar y mostrar el contenido en vista previa}Convertidores de Contenido
Sección titulada «Convertidores de Contenido»Convierte entre formatos Portable Text y ProseMirror.
import { prosemirrorToPortableText, portableTextToProsemirror } from "emdash";
// De ProseMirror (editor) a Portable Text (almacenamiento)const portableText = prosemirrorToPortableText(prosemirrorDoc);
// De Portable Text a ProseMirrorconst prosemirrorDoc = portableTextToProsemirror(portableText);Configuración del Sitio
Sección titulada «Configuración del Sitio»import { getSiteSettings, getSiteSetting } from "emdash";
// Obtener toda la configuraciónconst settings = await getSiteSettings();
// Obtener una sola configuraciónconst title = await getSiteSetting("siteTitle");La configuración es de solo lectura desde la API de tiempo de ejecución. Usa la API de administración para actualizarla.
import { getMenu, getMenus } from "emdash";
// Obtener todos los menúsconst menus = await getMenus();
// Obtener un menú específico con sus elementosconst primaryMenu = await getMenu("primary");
if (primaryMenu) { primaryMenu.items.forEach(item => { console.log(item.label, item.url); // Elementos anidados para menus desplegables item.children.forEach(child => console.log(" -", child.label)); });}Taxonomías
Sección titulada «Taxonomías»import { getTaxonomyTerms, getTerm, getEntryTerms, getEntriesByTerm } from "emdash";
// Obtener todos los términos para una taxonomía (estructura de árbol para jerárquicas)const categories = await getTaxonomyTerms("category");
// Obtener un término únicoconst news = await getTerm("category", "news");
// Obtener términos asignados a una entrada de contenidoconst postCategories = await getEntryTerms("posts", "post-123", "category");
// Obtener entradas con un término específicoconst newsPosts = await getEntriesByTerm("posts", "category", "news");Áreas de Widgets
Sección titulada «Áreas de Widgets»import { getWidgetArea, getWidgetAreas } from "emdash";
// Obtener todas las áreas de widgetsconst areas = await getWidgetAreas();
// Obtener un área de widget específica con sus widgetsconst sidebar = await getWidgetArea("sidebar");
if (sidebar) { sidebar.widgets.forEach(widget => { console.log(widget.type, widget.title); });}Secciones
Sección titulada «Secciones»import { getSection, getSections, getSectionCategories } from "emdash";
// Obtener todas las seccionesconst sections = await getSections();
// Filtrar seccionesconst heroes = await getSections({ category: "hero" });const themeSections = await getSections({ source: "theme" });const results = await getSections({ search: "newsletter" });
// Obtener una sección únicaconst cta = await getSection("newsletter-cta");
// Obtener categoríasconst categories = await getSectionCategories();Búsqueda
Sección titulada «Búsqueda»import { search, searchCollection } from "emdash";
// Búsqueda global en todas las coleccionesconst results = await search("hello world", { collections: ["posts", "pages"], status: "published", limit: 20,});
// Los resultados incluyen fragmentos con resaltadosresults.forEach(result => { console.log(result.title); console.log(result.snippet); // Contains <mark> tags console.log(result.score);});
// Búsqueda específica de una colecciónconst posts = await searchCollection("posts", "typescript", { limit: 10,});Manejo de Errores
Sección titulada «Manejo de Errores»EmDash exporta clases de error para manejar fallos específicos:
import { EmDashDatabaseError, EmDashValidationError, EmDashStorageError, SchemaError,} from "emdash";
try { await repo.create({ ... });} catch (error) { if (error instanceof EmDashValidationError) { console.error("Validation failed:", error.message); } if (error instanceof SchemaError) { console.error("Schema error:", error.code, error.details); }}