Referencia de la API JavaScript

En esta página

Esta página describe la API pública que usan las páginas, layouts y componentes de Astro para leer y presentar un sitio EmDash. No enumera cada export del paquete raíz emdash: repositorios de base de datos, handlers de API, utilidades de migración, APIs para autores de plugins y otros internals del servidor tienen referencias aparte o están pensados para código de integración del framework.

El contrato de API de plantillas de sitio de esta referencia lista cada función importada desde emdash por las plantillas de sitio mantenidas de EmDash. Imports solo de tipos como MediaValue pertenecen a la documentación del modelo de datos correspondiente. Las funciones relacionadas se documentan en las mismas secciones cuando ayudan al autor del sitio, pero la página no cubre exports raíz no relacionados.

Contrato de API de plantillas de sitio

Las plantillas mantenidas invocan estos helpers en tiempo de ejecución:

FunciónUso
decodeSlugDecodificar un parámetro de ruta dinámica antes de la búsqueda
getEmDashCollectionLeer y filtrar entradas en una collection
getEmDashEntryLeer una entrada por ID o slug
getMenuWithCacheHintRenderizar un menú de navegación con invalidación de caché
getSeoMetaResolver los valores del panel SEO de una entrada y los fallbacks de plantilla
getSiteSettingsLeer la identidad pública del sitio y otras configuraciones globales
getSiteSettingsWithCacheHintLeer configuraciones globales con invalidación de caché
getTaxonomyTermsWithCacheHintRenderizar filtros de taxonomía con invalidación de caché
getTermsForEntriesCargar en lote una taxonomía para una lista de entradas
sanitizeHrefRechazar esquemas URL inseguros antes de renderizar enlaces almacenados
searchBuscar contenido publicado en todas las collections

Consultas de contenido

Las funciones de consulta de EmDash siguen el patrón de live content collections de Astro y devuelven { entries, error } o { entry, error } para un manejo de errores controlado.

getEmDashCollection()

Obtiene todas las entradas de una collection. El siguiente ejemplo carga todos los posts y comprueba si hay error:

import { getEmDashCollection } from "emdash";

const { entries: posts, error } = await getEmDashCollection("posts");

if (error) {
	console.error("Failed to load posts:", error);
}

Parameters

ParameterTypeDescription
collectionstringCollection slug
optionsCollectionFilterOptional filter options

Options

El parámetro options acepta el siguiente filtro:

interface WhereRange {
	gt?: string;
	gte?: string;
	lt?: string;
	lte?: string;
}

interface CollectionFilter {
	status?: "draft" | "published" | "archived";
	limit?: number;
	cursor?: string; // Keyset pagination — pass a previous `nextCursor`
	offset?: number; // Offset pagination — skip N entries (use with `limit`)
	where?: Record<string, string | string[] | WhereRange>;
	orderBy?: Record<string, "asc" | "desc">;
	locale?: string;
}

cursor y offset son mutuamente excluyentes. Las claves where pueden nombrar campos de contenido, taxonomías o byline; los objetos de rango permiten comparaciones ordenadas.

Returns

La función se resuelve a un CollectionResult:

interface CollectionResult<T> {
	entries: ContentEntry<T>[]; // Empty array if error or none found
	error?: Error; // Set if query failed
	cacheHint: CacheHint; // Tags and last-modified time for Astro route caching
	nextCursor?: string; // Cursor for the next keyset page, if any
	hasMore?: boolean; // Whether more entries exist beyond this page (when `limit` is set)
}

Examples

Los siguientes ejemplos filtran por estado y taxonomía, limitan resultados y manejan errores:

// Get all published posts
const { entries: posts } = await getEmDashCollection("posts", {
	status: "published",
});

// Get latest 5 posts
const { entries: latest } = await getEmDashCollection("posts", {
	limit: 5,
	status: "published",
});

// Filter by taxonomy
const { entries: newsPosts } = await getEmDashCollection("posts", {
	status: "published",
	where: { category: "news" },
});

// Numbered archive page (e.g. /page/3) with offset pagination
const perPage = 20;
const page = Number(Astro.params.page ?? 1);
const { entries: pagePosts, hasMore } = await getEmDashCollection("posts", {
	status: "published",
	limit: perPage,
	offset: (page - 1) * perPage,
	orderBy: { published_at: "desc" },
});

// Handle errors
const { entries, error } = await getEmDashCollection("posts");
if (error) {
	return new Response("Server error", { status: 500 });
}

getEmDashEntry()

Obtiene una sola entrada por slug o ID. El siguiente ejemplo carga un post y redirige cuando falta:

import { getEmDashEntry } from "emdash";

const { entry: post, error } = await getEmDashEntry("posts", "my-post-slug");

if (!post) {
	return Astro.redirect("/404");
}

Parameters

ParameterTypeDescription
collectionstringCollection slug
slugOrIdstringEntry slug or ID
options{ locale?: string; references?: ReferenceSelection }Optional. Locale for slug resolution, and the reference fields to load

El modo preview se gestiona automáticamente: cuando la solicitud tiene un token _preview válido, la consulta sirve contenido en borrador. El estado de preview no requiere parámetro.

references nombra los campos reference a cargar, indexados por slug del campo. true solicita la primera página con el límite predeterminado de 50 entradas; la forma objeto acepta limit (como máximo 100) y el cursor de una página anterior. Los campos omitidos no se leen, y una llamada sin la opción no ejecuta consultas de reference.

Returns

La función se resuelve a un EntryResult:

interface EntryResult<T, R> {
	entry: ContentEntry<T, R> | null; // null if not found
	error?: Error; // Set only for actual errors, not "not found"
	isPreview: boolean; // true if draft content is being served
	fallbackLocale?: string; // Set when locale fallback returned another locale
	cacheHint: CacheHint; // Tags and last-modified time for Astro route caching
}

Examples

Los siguientes ejemplos obtienen por slug e ID, leen el estado de preview y distinguen errores de no encontrado:

// Get by slug
const { entry: post } = await getEmDashEntry("posts", "hello-world");

// Get by ID
const { entry: post } = await getEmDashEntry("posts", "01HXK5MZSN0FVXT2Q3KPRT9M7D");

// Preview is automatic — isPreview is true when a valid _preview token is present
const { entry, isPreview, error } = await getEmDashEntry("posts", slug);

// Handle errors vs not-found
if (error) {
	return new Response("Server error", { status: 500 });
}
if (!entry) {
	return Astro.redirect("/404");
}

getTranslations()

Obtiene las traducciones disponibles de una entrada por slug de collection e ID de base de datos:

import { getTranslations } from "emdash";

const { translations, error } = await getTranslations("posts", post.data.id);

El resultado contiene la translationGroup compartida, un array translations y un error opcional. Cada resumen de traducción incluye id, locale, slug y status.

resolveEmDashPath()

Resuelve un pathname público contra los patrones URL configurados para collections enrutables:

import { resolveEmDashPath } from "emdash";

const result = await resolveEmDashPath("/blog/hello-world");

if (result) {
	console.log(result.collection, result.entry.data.title);
}

El resultado contiene la collection coincidente, la entry y los params de ruta. La función devuelve null cuando ningún patrón URL configurado coincide.

getEditMeta()

Lee los metadatos de edición visual no enumerables adjuntos a un valor Portable Text:

import { getEditMeta } from "emdash";

const meta = getEditMeta(post.data.content);

Devuelve { collection, id, field } para un valor anotado, o undefined cuando el valor no tiene anotación.

Tipos de contenido

ContentEntry

Las funciones de consulta devuelven entradas con la siguiente forma:

interface ContentEntry<T = Record<string, unknown>, R = ReferencePages> {
	id: string;
	data: T;
	references?: R; // One page per reference field requested; absent otherwise
	edit: EditProxy; // Visual editing annotations
}

El proxy edit proporciona anotaciones de edición visual. Extiéndelo sobre elementos para habilitar edición inline: {...entry.edit.title}. Fuera del modo edición, no produce salida.

El objeto data contiene todos los campos de contenido más campos del sistema:

  • id - Unique identifier
  • slug - URL-friendly identifier
  • status - “draft” | “published” | “archived”
  • createdAt - Creation time as a Date
  • updatedAt - Last update time as a Date
  • publishedAt - Publication time as a Date, or null; retained when content is unpublished
  • Plus all custom fields defined in your collection schema

ReferencePage

entry.references contiene una página por campo reference que se pidió a getEmDashEntry, indexada por slug del campo:

interface ReferencePage<T = Record<string, unknown>> {
	entries: ContentEntry<T>[];
	nextCursor?: string; // Set when the field holds more entries than the limit
}

Cada entrada se mapea como una entrada cargada directamente, con una excepción: bylines y términos de taxonomía no se hidratan, así que data.bylines y data.terms están ausentes.

getEmDashReferences()

Obtiene una página de un solo campo reference sin volver a leer la entrada a la que pertenece. Úsalo para avanzar más allá de la primera página con el nextCursor que devolvió esa página.

ParameterTypeDescription
collectionstringCollection slug of the entry that holds the field
slugOrIdstringEntry slug or ID
fieldstringReference field slug
options{ limit?: number; cursor?: string; locale?: string }Optional. Defaults to 50 entries, at most 100

El siguiente ejemplo carga las siguientes 20 entradas enlazadas:

import { getEmDashReferences } from "emdash";

const { entries, nextCursor, error } = await getEmDashReferences(
	"posts",
	post.id,
	"related_posts",
	{ cursor, limit: 20 },
);

error solo se establece para errores reales. Un campo desconocido, una entrada faltante o una que la solicitud no puede ver resuelven a un array entries vacío.

El resultado también incluye un cacheHint que nombra las filas que leyó la página. Una ruta que cachea lo que renderiza debe fusionarlo en su propio hint, como getEmDashEntry({ references }) incorpora el de la primera página, para que escribir en una de esas entradas expire la página.

Helpers de URL

decodeSlug() and slugify()

Usa decodeSlug() en un parámetro de ruta dinámica antes de pasarlo a una consulta de contenido. Devuelve undefined para un parámetro faltante; en caso contrario aplica decodeURIComponent(), que lanza con codificación porcentual mal formada:

function decodeSlug(raw: string | undefined): string | undefined;
import { decodeSlug, getEmDashEntry } from "emdash";

const slug = decodeSlug(Astro.params.slug);
const { entry } = slug ? await getEmDashEntry("posts", slug) : { entry: null };

slugify(value) convierte texto en un slug en minúsculas separado por guiones. Úsalo cuando una plantilla necesite construir un slug desde una etiqueta; los slugs de entrada almacenados ya vienen de EmDash.

sanitizeHref() and isSafeHref()

Estos helpers rechazan esquemas de enlace inseguros como javascript:. isSafeHref(value) devuelve un booleano. sanitizeHref(value) devuelve la URL segura original o "#" cuando el valor está vacío o es inseguro.

import { sanitizeHref } from "emdash";

const href = sanitizeHref(menuItem.url);

Sistema de preview

generatePreviewToken()

Genera un token de preview para contenido en borrador. El siguiente ejemplo crea un token que expira en una hora:

import { generatePreviewToken } from "emdash";

const token = await generatePreviewToken({
	contentId: "posts:01HXK5MZSN...",
	secret: process.env.EMDASH_PREVIEW_SECRET!,
	expiresIn: 3600, // 1 hour
});

contentId debe usar el formato collection:id. expiresIn acepta segundos o una duración terminada en s, m, h, d o w y por defecto es "1h". Mantén el secreto de firma en el servidor.

verifyPreviewToken()

Verifica un token de preview y lee su payload:

import { verifyPreviewToken } from "emdash";

const result = await verifyPreviewToken({
	token,
	secret: process.env.EMDASH_PREVIEW_SECRET!,
});

if (result.valid) {
	const { cid, exp, iat } = result.payload;
	// cid is "collection:id" format, e.g. "posts:my-draft-post"
}

Pasa token o url con el secret de firma. Un token inválido devuelve { valid: false, error }, donde error es "none", "malformed", "invalid" o "expired".

parseContentId()

Divide el valor collection:id del payload de preview en sus dos partes:

import { parseContentId } from "emdash";

const parsed = parseContentId(result.payload.cid);

Devuelve { collection, id } y lanza cuando el valor no tiene separador de dos puntos.

getPreviewUrl() and buildPreviewUrl()

getPreviewUrl() crea y firma una URL de preview. Acepta collection, id y secret, más valores opcionales expiresIn, baseUrl, pathPattern y locale:

import { getPreviewUrl } from "emdash";

const previewUrl = await getPreviewUrl({
	collection: "posts",
	id: post.id,
	secret: process.env.EMDASH_PREVIEW_SECRET!,
	pathPattern: "/blog/{id}",
});

Sin baseUrl, devuelve una URL relativa al sitio. Usa buildPreviewUrl({ path, token, baseUrl? }) cuando ya existe un token.

isPreviewRequest()

Comprueba si una solicitud incluye un token de preview y luego léelo:

import { isPreviewRequest, getPreviewToken } from "emdash";

if (isPreviewRequest(Astro.url)) {
	const token = getPreviewToken(Astro.url);
	// Verify and show preview content
}

getPreviewToken() devuelve el parámetro de consulta _preview o null cuando falta. El middleware de EmDash verifica solicitudes de preview normales y proporciona el estado de preview a getEmDashEntry() automáticamente; estos helpers son para rutas de preview personalizadas y herramientas.

Convertidores de contenido

Convierte entre formatos Portable Text y ProseMirror:

import { prosemirrorToPortableText, portableTextToProsemirror } from "emdash";

// From ProseMirror (editor) to Portable Text (storage)
const portableText = prosemirrorToPortableText(prosemirrorDoc);

// From Portable Text to ProseMirror
const prosemirrorDoc = portableTextToProsemirror(portableText);

Configuración del sitio

Lee la configuración global del sitio con getSiteSettings y getSiteSetting:

function getSiteSettings(): Promise<Partial<SiteSettings>>;
function getSiteSetting<K extends SiteSettingKey>(key: K): Promise<SiteSettings[K] | undefined>;
import { getSiteSettings, getSiteSetting } from "emdash";

// Get all settings
const settings = await getSiteSettings();

// Get single setting
const title = await getSiteSetting("title");

La configuración es de solo lectura desde la API de runtime. Usa la API de admin para actualizarla.

getSiteSettings() devuelve un objeto parcial porque se omiten las claves no establecidas. Ajustes de medios como el logo y favicon del sitio se resuelven a objetos media-reference antes de que la función retorne.

getSiteSettingsWithCacheHint() devuelve { data, cacheHint }. Pasa el hint a Astro.cache.set() cuando una caché de ruta Astro deba invalidarse tras cambios en la configuración del sitio.

SEO

Resuelve los valores del panel SEO y los fallbacks de contenido de una página con getSeoMeta():

function getSeoMeta<T>(content: SeoContentInput<T>, options?: SeoMetaOptions): SeoMeta;

interface SeoMetaOptions {
	siteTitle?: string;
	siteUrl?: string;
	titleSeparator?: string; // Default: " | "
	path?: string;
	defaultOgImage?: string;
	defaultTitle?: string;
	defaultDescription?: string;
}

interface SeoMeta {
	title: string;
	description: string | null;
	ogTitle: string;
	ogDescription: string | null;
	ogImage: string | null;
	canonical: string | null;
	robots: string | null;
}
import { getSeoMeta } from "emdash";

const meta = getSeoMeta(post, {
	siteTitle: "Example Blog",
	siteUrl: "https://example.com",
	path: `/blog/${post.data.slug}`,
});

Devuelve el título, descripción, valores Open Graph, URL canónica y valor robots resueltos. getContentSeo(content) devuelve el objeto SEO sin aplicar fallbacks de plantilla.

Para contenido traducido, getHreflangAlternates(collection, entryId, { siteUrl? }) devuelve variantes de locale publicadas y enrutables como objetos { hreflang, href } y añade una entrada x-default. Devuelve un array vacío cuando la internacionalización está desactivada, la entrada actual está marcada noindex o no hay URL absoluta del sitio.

Comentarios

Obtiene comentarios aprobados y su recuento para una entrada:

import { getCommentCount, getComments } from "emdash";

const { items: comments, total } = await getComments({
	collection: "posts",
	contentId: post.data.id,
	threaded: true,
	reactions: true,
	sort: "best",
});

const count = await getCommentCount("posts", post.data.id);

threaded anida respuestas bajo su comentario padre. El sort predeterminado es "oldest"; "best" ordena comentarios de nivel superior por reacciones y habilita recuentos de reacciones automáticamente. Las consultas de comentarios renderizadas en servidor devuelven como máximo 500 comentarios aprobados; usa la REST API cuando un cliente necesite paginación.

Menús

Obtiene menús de navegación e itera sus ítems, incluidos hijos anidados:

function getMenu(name: string, options?: { locale?: string }): Promise<Menu | null>;
function getMenus(options?: { locale?: string }): Promise<MenuSummary[]>;
import { getMenu, getMenus } from "emdash";

// Get all menus
const menus = await getMenus();

// Get specific menu with items
const primaryMenu = await getMenu("primary");

if (primaryMenu) {
	primaryMenu.items.forEach(item => {
		console.log(item.label, item.url);
		// Nested items for dropdowns
		item.children.forEach(child => console.log("  -", child.label));
	});
}

getMenu(name, { locale? }) sigue la cadena de fallback de locale configurada. getMenus({ locale? }) lista resúmenes de menú para la locale de la solicitud resuelta o configurada; sin internacionalización lista cada locale. getMenuWithCacheHint() devuelve { data, cacheHint } para rutas que usan la caché de Astro.

Bylines

Obtiene un perfil de autor por ID o slug, o lista entradas atribuidas a un byline:

import { getByline, getBylineBySlug, getEntriesByByline } from "emdash";

const profile = await getBylineBySlug("jane-doe", { locale: "en" });
const posts = profile
	? await getEntriesByByline("posts", profile.translationGroup ?? profile.id)
	: [];

getByline(id) devuelve un perfil o null. La búsqueda por slug acepta una locale opcional y sigue la cadena de fallback de locale. Las consultas de contenido ya hidratan los créditos ordenados de una entrada en entry.data.bylines; usa estos helpers independientes para páginas de autor y archivos por byline.

Taxonomías

Obtiene términos de taxonomía, un término, los términos de una entrada o entradas por término:

function getTaxonomyTerms(
	taxonomyName: string,
	options?: { locale?: string; includeCounts?: boolean },
): Promise<TaxonomyTerm[]>;

function getTerm(
	taxonomyName: string,
	slug: string,
	options?: { locale?: string; includeCounts?: boolean },
): Promise<TaxonomyTerm | null>;

function getTermsForEntries(
	collection: string,
	entryIds: string[],
	taxonomyName: string,
	options?: { locale?: string },
): Promise<Map<string, TaxonomyTerm[]>>;
import { getTaxonomyTerms, getTerm, getEntryTerms, getEntriesByTerm } from "emdash";

// Get all terms for a taxonomy (tree structure for hierarchical)
const categories = await getTaxonomyTerms("category");

// Get single term
const news = await getTerm("category", "news");

// Get terms assigned to a content entry
const postCategories = await getEntryTerms("posts", "post-123", "category");

// Get entries with a specific term
const newsPosts = await getEntriesByTerm("posts", "category", "news");

getTaxonomyDefs({ locale? }) lista una definición por taxonomía, y getTaxonomyDef(name, { locale? }) devuelve una definición o null cuando ninguna locale define la taxonomía. Ambos resuelven una taxonomía en cada locale; Traducir taxonomías y términos explica qué etiqueta de locale usan. Las búsquedas de términos siguen la cadena de fallback configurada.

getTaxonomyTerms(name, { locale?, includeCounts? }) devuelve un árbol para taxonomías jerárquicas e incluye recuentos de entradas visibles por defecto. Pasa includeCounts: false cuando la plantilla no renderiza recuentos. Las consultas de contenido también hidratan términos asignados en data.terms de cada entrada; usa getEntryTerms() cuando solo tienes nombre de collection e ID de entrada.

Para páginas de archivo que renderizan términos junto a muchas entradas, agrupa la búsqueda en lugar de llamar getEntryTerms() en un bucle:

import { getAllTermsForEntries, getTermsForEntries } from "emdash";

const termsByPost = await getTermsForEntries(
	"posts",
	posts.map(post => post.data.id),
	"category",
);

const allTermsByPost = await getAllTermsForEntries(
	"posts",
	posts.map(post => post.data.id),
);

getTermsForEntries() devuelve un mapa de ID de entrada a términos de la taxonomía solicitada. getAllTermsForEntries() devuelve un mapa de ID de entrada a términos agrupados por nombre de taxonomía. getTaxonomyTermsWithCacheHint() devuelve { data, cacheHint } para una ruta con caché Astro.

Áreas de widgets

Obtiene áreas de widgets y los widgets que contienen:

import { getWidgetArea, getWidgetAreas } from "emdash";

// Get all widget areas
const areas = await getWidgetAreas();

// Get specific widget area with widgets
const sidebar = await getWidgetArea("sidebar");

if (sidebar) {
	sidebar.widgets.forEach(widget => {
		console.log(widget.type, widget.title);
	});
}

getWidgetAreaWithCacheHint(name) devuelve { data, cacheHint } para rutas que usan la caché de Astro. Las áreas de widgets y sus widgets se ordenan según el orden configurado.

Secciones

Obtiene secciones y las filtra:

import { getSection, getSections } from "emdash";

// Get all sections (paginated)
const { items, nextCursor } = await getSections();

// Filter sections
const { items: themeSections } = await getSections({ source: "theme" });
const { items: results } = await getSections({ search: "newsletter" });

// Get a single section by slug
const cta = await getSection("newsletter-cta");

getSections(options?) devuelve { items: Section[]; nextCursor?: string }. Las opciones son source ("theme" | "user" | "import"), search, limit (predeterminado 50, máx. 100) y cursor.

Búsqueda

Ejecuta una búsqueda global en todas las collections. Los resultados incluyen fragmentos resaltados:

function search(query: string, options?: SearchOptions): Promise<SearchResponse>;

interface SearchOptions {
	collections?: string[]; // Default: every searchable collection
	status?: string; // Default: "published"
	locale?: string; // Default: all locales
	limit?: number; // Default: 20
	cursor?: string;
	scope?: "all" | "title"; // Default: "all"
}

interface SearchResponse {
	items: SearchResult[];
	nextCursor?: string;
}
import { search } from "emdash";

const results = await search("hello world", {
	collections: ["posts", "pages"],
	status: "published",
	limit: 20,
});

// search() resolves to { items, nextCursor? }
results.items.forEach(result => {
	console.log(result.title);
	console.log(result.snippet); // Contains <mark> tags
	console.log(result.score);
});

// Paginate: pass the previous nextCursor back as `cursor` to get the next page.
// nextCursor is undefined once there are no more results.
if (results.nextCursor) {
	const next = await search("hello world", {
		collections: ["posts", "pages"],
		limit: 20,
		cursor: results.nextCursor,
	});
}

Con scope: "title" la consulta coincide solo con el campo title de cada collection en lugar del texto indexado completo — útil para selectores y autocompletado donde coincidencias en el cuerpo sorprenderían. Las collections cuyo campo title no está indexado para búsqueda no devuelven resultados bajo este scope.

Manejo de errores

Las consultas de contenido devuelven errores operativos en su resultado en lugar de lanzar. Una entrada faltante no es un error operativo: entry es null y error permanece undefined.

const { entry, error } = await getEmDashEntry("posts", slug);

if (error) {
	return new Response("Content could not be loaded", { status: 500 });
}

if (!entry) {
	return Astro.redirect("/404");
}

Los helpers sin sobre de resultado pueden lanzar cuando la entrada es inválida o falla una operación de base de datos. Maneja esos errores en el límite de la ruta cuando la página pueda ofrecer un fallback útil. Las clases de error de repositorio y handler usadas por integraciones de servidor de nivel inferior quedan fuera de esta API de plantillas de sitio.