La configuración principal de EmDash se encuentra en astro.config.mjs, mientras que src/live.config.ts registra el cargador de contenido. Los valores específicos del despliegue también pueden provenir de variables de entorno. Un pequeño bloque de metadatos en package.json admite etiquetas de plantilla y flujos locales antiguos de la CLI.
Integración de Astro
Configura EmDash como una integración de Astro en astro.config.mjs:
import { defineConfig } from "astro/config";
import emdash, { local, s3 } from "emdash/astro";
import { sqlite, libsql } from "emdash/db";
export default defineConfig({
integrations: [
emdash({
database: sqlite({ url: "file:./data.db" }),
storage: local({
directory: "./uploads",
baseUrl: "/_emdash/api/media/file",
}),
plugins: [],
}),
],
});
Opciones de la integración
database
Obligatoria. Configuración del adaptador de base de datos. Elige un adaptador:
// SQLite (Node.js)
database: sqlite({ url: "file:./data.db" });
// PostgreSQL
database: postgres({ connectionString: process.env.DATABASE_URL });
// libSQL
database: libsql({
url: process.env.LIBSQL_DATABASE_URL,
authToken: process.env.LIBSQL_AUTH_TOKEN,
});
// Cloudflare D1 (import from @emdash-cms/cloudflare)
database: d1({ binding: "DB" });
Consulta Opciones de base de datos para más detalles.
migrations
Opcional. Controla cómo se gestionan en tiempo de ejecución las migraciones internas de base de datos de EmDash. Si se omite esta opción, el valor predeterminado es { runtime: "auto" }.
migrations: {
runtime: "check", // "auto" | "check" | "manual"
dev: "auto", // optional development override
}
auto comprueba y aplica las migraciones pendientes, check devuelve 503 cuando hay migraciones pendientes conocidas por la compilación en ejecución (excepto en las solicitudes al endpoint de imágenes que instala EmDash) y manual no realiza ninguna consulta de migración en tiempo de ejecución. EMDASH_MIGRATIONS_MODE sobrescribe el modo de ejecución efectivo. Consulta Gestionar las migraciones de la base de datos del núcleo antes de adoptar check o manual.
storage
Opcional. Configuración del adaptador de almacenamiento de medios. Cuando se omite esta opción, EmDash guarda los archivos en ./.emdash/uploads y los sirve a través de /_emdash/api/media/file. Elige un adaptador cuando el directorio local predeterminado no sea adecuado:
// Local filesystem (development)
storage: local({
directory: "./uploads",
baseUrl: "/_emdash/api/media/file",
});
// R2 binding (Cloudflare Workers)
storage: r2({
binding: "MEDIA",
publicUrl: "https://pub-xxxx.r2.dev", // optional
});
// S3-compatible (any platform) — all fields from S3_* environment variables
storage: s3()
// Or with explicit values
storage: s3({
endpoint: "https://s3.amazonaws.com",
bucket: "my-bucket",
accessKeyId: process.env.S3_ACCESS_KEY_ID,
secretAccessKey: process.env.S3_SECRET_ACCESS_KEY,
region: "us-east-1", // optional, default: "auto"
publicUrl: "https://cdn.example.com", // optional
});
Consulta Opciones de almacenamiento para más detalles.
images
Opcional. Controla si EmDash integra los medios almacenados con la optimización de imágenes de Astro. El valor predeterminado es true.
Cuando está habilitada, EmDash envuelve el endpoint de imágenes de Astro para que <Image> y getImage() puedan leer los bytes de origen directamente del adaptador de almacenamiento configurado. Esto también funciona cuando la URL original del medio está detrás de Cloudflare Access. Las solicitudes al endpoint envuelto omiten el arranque en tiempo de ejecución de EmDash, por lo que en ellas locals.emdash solo contiene storage, además de db para un usuario con sesión iniciada. locals.user se establece igual que en otras rutas públicas. Establece images: false cuando otro servicio de imágenes gestione los medios o cuando cada imagen deba renderizarse sin el envoltorio de endpoint de EmDash.
emdash({
images: false,
});
mediaProviders
Opcional. Añade servicios de medios a la biblioteca de medios. El proveedor local respaldado por el almacenamiento sigue disponible automáticamente; cada descriptor de este array añade otro lugar donde los editores pueden explorar o subir medios.
El siguiente ejemplo añade Cloudflare Images y Cloudflare Stream:
import { cloudflareImages, cloudflareStream } from "@emdash-cms/cloudflare";
emdash({
mediaProviders: [cloudflareImages({}), cloudflareStream({})],
});
Las credenciales de los proveedores se resuelven en tiempo de ejecución. Las configuraciones vacías anteriores usan las variables de entorno predeterminadas de Cloudflare descritas en las secciones de adaptadores cloudflareImages(config) y cloudflareStream(config). Consulta Biblioteca de medios: Proveedores de medios para la configuración de bindings y de renderizado.
objectCache
Opcional. Almacena en caché los resultados de las consultas de contenido y configuración en un almacén clave-valor, de modo que las lecturas se sirvan sin consultar la base de datos en cada solicitud. Está deshabilitada si se omite. Elige un adaptador:
// Cloudflare KV (shared across all isolates)
import { kvCache } from "@emdash-cms/cloudflare";
objectCache: kvCache({ binding: "CACHE" });
// In-memory (Node.js / development)
import { memoryCache } from "emdash/astro";
objectCache: memoryCache();
Consulta Caché de objetos para la configuración y las opciones.
middleware.outer
Opcional. Registra un módulo de middleware de Astro fuera de toda la pila de middleware de EmDash. Como la integración lo registra con order: "pre" de Astro, también se ejecuta antes del middleware definido en src/middleware.ts. Úsalo para filtros de solicitudes o cachés de respuesta completa que deban evitar la inicialización del entorno de ejecución y de la base de datos cuando hay un acierto, o para encabezados de respuesta que dependan del HTML final de EmDash.
emdash({
middleware: {
outer: "./src/outer-middleware.ts",
},
});
El orden de ejecución es:
- El middleware exterior se ejecuta hasta
await next(). - EmDash inicializa su entorno de ejecución y su base de datos, y luego ejecuta los middleware de configuración, autenticación y contexto de la solicitud.
- Se renderiza la ruta de Astro.
- EmDash aplica las modificaciones de la respuesta, incluidos el HTML de edición visual y los encabezados de seguridad y de temporización.
next()se resuelve en el middleware exterior con esa respuesta final.
Antes de llamar a next(), el middleware dispone de la solicitud normal de Astro y del contexto de ejecución de la plataforma, pero locals.emdash, locals.user, la base de datos y el estado de EmDash ligado a la solicitud no están disponibles. Una Response anticipada omite EmDash por completo, así que debe incluir los encabezados de seguridad y de caché que necesite. Una vez resuelto next(), es seguro finalizar los nonces de CSP, almacenar en caché el cuerpo completo o establecer Content-Length. Si el middleware cambia el cuerpo, elimina o recalcula cualquier encabezado Content-Length existente.
El hook usa la API de middleware de Astro tanto en Node como en Cloudflare. Este ejemplo mínimo con la Cache API de Cloudflare solo almacena en caché respuestas HTML anónimas y devuelve los aciertos antes de la inicialización de EmDash:
import { waitUntil } from "cloudflare:workers";
import { defineMiddleware } from "astro:middleware";
export const onRequest = defineMiddleware(async ({ request }, next) => {
if (request.method !== "GET" || request.headers.has("cookie")) {
return next();
}
const cacheKey = new Request(request.url, { method: "GET" });
const cached = await caches.default.match(cacheKey);
if (cached) return cached;
const response = await next();
const isHtml = response.headers.get("content-type")?.includes("text/html");
const isPrivate = response.headers.get("cache-control")?.includes("no-store");
if (response.ok && isHtml && !isPrivate) {
waitUntil(caches.default.put(cacheKey, response.clone()));
}
return response;
});
En Node, usa la misma forma de middleware con una caché compatible con Node, como Redis. Las claves de caché y las reglas de omisión deben incluir toda propiedad de la solicitud que cambie la respuesta renderizada.
playground
Opcional. Habilita el middleware que usan los playgrounds de EmDash desechables basados en el navegador. Crea una base de datos de Durable Object con permiso de escritura para cada sesión, aplica la semilla configurada e inicia sesión del visitante como administrador anónimo antes de que se ejecute el middleware normal de EmDash.
import { playgroundDatabase } from "@emdash-cms/cloudflare";
emdash({
database: playgroundDatabase({ binding: "PLAYGROUND_DB" }),
playground: {
middlewareEntrypoint: "@emdash-cms/cloudflare/db/playground-middleware",
},
});
Este modo requiere @emdash-cms/cloudflare y un binding de Durable Object. Omite el middleware normal de configuración y autenticación, así que úsalo solo para sitios de demostración efímeros y no para un CMS en producción.
plugins
Opcional. Array de plugins que se ejecutan en el mismo proceso que el sitio de Astro. Los plugins nativos van aquí. Un plugin compatible con el sandbox también puede ejecutarse aquí cuando confías en él con acceso completo al proceso y no necesitas aislamiento.
El siguiente ejemplo registra un plugin nativo:
import seoPlugin from "@emdash-cms/plugin-seo";
plugins: [seoPlugin()];
Los plugins nativos pueden usar directamente las API del servidor y del framework, por lo que no se pueden mover a sandboxed a menos que el paquete también ofrezca un punto de entrada de plugin compatible con el sandbox. Consulta Elegir un formato de plugin para ver las diferencias de desarrollo y despliegue.
sandboxed
Opcional. Array de plugins compatibles con el sandbox que usan las API de plugin declaradas de EmDash y se ejecutan en entornos aislados. No coloques aquí un plugin nativo: el código nativo puede depender de un acceso al proceso y al framework que el sandbox no ofrece.
import thirdPartyPlugin from "third-party-emdash-plugin";
import { sandbox } from "@emdash-cms/cloudflare";
emdash({
sandboxed: [thirdPartyPlugin()],
sandboxRunner: sandbox(),
});
Los plugins en sandbox se omiten cuando no hay configurado un ejecutor de sandbox utilizable. Consulta Sandbox de plugins para la configuración del ejecutor en Cloudflare y Node.js.
sandboxRunner
Opcional. Especificador de módulo de la fábrica que inicia los entornos de ejecución aislados de los plugins. Es obligatorio para los plugins de sandboxed y para los plugins del marketplace o del registro.
En Cloudflare Workers, usa el adaptador sandbox():
import { sandbox } from "@emdash-cms/cloudflare";
emdash({
sandboxRunner: sandbox(),
});
Los despliegues en Node.js usan el módulo ejecutor workerd documentado en Sandbox de plugins: Node.js.
sandbox
Opcional. Controla si un ejecutor de sandbox configurado aísla los plugins. El sandbox está habilitado cuando se configura sandboxRunner. Establece sandbox: false solo para diagnosticar si un problema proviene de un plugin o de su entorno de ejecución en sandbox:
emdash({
sandboxRunner: sandbox(),
sandbox: false,
});
Con false, los plugins declarados en sandboxed y los instalados desde el marketplace se ejecutan en el proceso principal del servidor sin aislamiento ni límites de recursos. Restablece el sandbox después del diagnóstico.
registry
Opcional. Configura el agregador y la política del registro de plugins. Sin un valor explícito, EmDash usa https://registry.emdashcms.com cuando sandboxRunner está configurado y sandbox no es false.
Establece registry: false para deshabilitar el descubrimiento en el registro y los plugins instalados desde el registro, manteniendo disponible el ejecutor de sandbox para los plugins declarados en sandboxed y los plugins antiguos del Marketplace:
emdash({
sandboxRunner: sandbox(),
registry: false,
});
Pasa la URL del servicio del registro como cadena, o usa un objeto cuando el sitio necesite fuentes de moderación o una política de antigüedad de versiones. El siguiente ejemplo usa la forma de objeto:
import { sandbox } from "@emdash-cms/cloudflare";
emdash({
sandboxRunner: sandbox(),
registry: {
aggregatorUrl: "https://registry.emdashcms.com",
acceptLabelers: "did:web:labels.emdashcms.com",
policy: {
minimumReleaseAge: "48h",
minimumReleaseAgeExclude: ["did:plc:yourfirstpartydid"],
},
},
});
| Opción | Tipo | Descripción |
|---|---|---|
aggregatorUrl | string | URL base del servicio del registro. Usa HTTPS en producción. |
acceptLabelers | string | Identificadores descentralizados (DID) opcionales, separados por comas, de los servicios de moderación que acepta la solicitud. Un DID es un identificador de cuenta estable de Atmosphere. Este ajuste no puede anular la política del servicio del registro. |
policy.minimumReleaseAge | string | number | Retiene las versiones más recientes que esta antigüedad. Cadena de duración ("48h", "7d") o segundos. |
policy.minimumReleaseAgeExclude | string[] | DID de editores, o pares <did>/<plugin-slug>, exentos de la retención. |
La política de antigüedad de versiones solo exime la primera versión de un paquete cuando el registro informa de una versión conservada y confirma que observó el paquete de forma continua. Un paquete rellenado a posteriori, una versión anterior eliminada o la falta de pruebas del historial mantienen la retención en vigor. Las exenciones explícitas de editores y paquetes se aplican con independencia del historial.
Consulta El registro de plugins para el flujo de instalación y el modelo de confianza.
marketplace
Obsoleta. URL base que se usa para actualizar los plugins instalados desde el Marketplace antiguo. La exploración del Marketplace y las nuevas instalaciones no se muestran en la administración. Los plugins existentes del Marketplace siguen siendo actualizables y desinstalables mientras esta opción esté configurada.
emdash({
marketplace: "https://marketplace.emdashcms.com",
sandboxRunner: sandbox(),
});
Las URL de producción deben usar HTTPS; HTTP solo se acepta para localhost y 127.0.0.1 durante el desarrollo. Conserva esta opción hasta que cada plugin del Marketplace se haya sustituido o desinstalado y luego elimínala. Sigue Migrar desde Marketplace para el procedimiento completo.
fonts
Opcional. Configuración de la tipografía de la interfaz de administración.
De forma predeterminada, EmDash carga Noto Sans mediante la API de fuentes de Astro. Las fuentes se descargan de Google en el momento de la compilación y se alojan por cuenta propia, por lo que no hay solicitudes a un CDN en tiempo de ejecución. La fuente base cubre los sistemas de escritura latino, cirílico, griego, devanagari y vietnamita.
Para añadir compatibilidad con otros sistemas de escritura, pasa nombres de scripts. El siguiente ejemplo añade árabe y japonés:
emdash({
fonts: {
scripts: ["arabic", "japanese"],
},
})
Los scripts disponibles son arabic, armenian, bengali, chinese-simplified, chinese-traditional, chinese-hongkong, devanagari, ethiopic, farsi, georgian, gujarati, gurmukhi, hebrew, japanese, kannada, khmer, korean, lao, malayalam, myanmar, oriya, sinhala, tamil, telugu, thai y tibetan.
Cada script se corresponde con la variante de Noto Sans respectiva en Google Fonts (p. ej., "arabic" carga Noto Sans Arabic). Todas las variantes de fuente comparten un único nombre de font-family y usan unicode-range, de modo que el navegador solo descarga los archivos que necesita para los caracteres de la página.
Establécela en false para deshabilitar por completo la inyección de fuentes y usar las fuentes del sistema:
emdash({
fonts: false,
})
El CSS de la administración usa la variable CSS --font-emdash. Esta se establece automáticamente mediante la configuración de fuentes anterior.
auth
Opcional. Un adaptador de autenticación. El inicio de sesión integrado de EmDash usa passkeys; al establecer auth se sustituyen por un proveedor externo. El adaptador de Cloudflare Access, access(), lo proporciona @emdash-cms/cloudflare:
import { access } from "@emdash-cms/cloudflare";
emdash({
auth: access({
teamDomain: "myteam.cloudflareaccess.com",
audience: "your-app-audience-tag",
roleMapping: {
Admins: 50,
Editors: 40,
},
}),
});
Opciones de access():
| Opción | Tipo | Valor predeterminado | Descripción |
|---|---|---|---|
teamDomain | string | obligatoria | El dominio de equipo de Cloudflare Access |
audience | string | — | Etiqueta de audiencia (AUD) de la aplicación. En Workers, prefiere audienceEnvVar. |
audienceEnvVar | string | "CF_ACCESS_AUDIENCE" | Variable de entorno de la que se lee la etiqueta de audiencia en tiempo de ejecución |
autoProvision | boolean | true | Crea un usuario de EmDash en el primer inicio de sesión |
defaultRole | number | 30 | Nivel de rol para los usuarios que no coincidan con roleMapping (consulta Roles de usuario) |
syncRoles | boolean | false | Vuelve a aplicar roleMapping en cada inicio de sesión en lugar de solo al aprovisionar |
roleMapping | object | — | Asigna nombres de grupos del IdP a niveles de rol de EmDash; gana la primera coincidencia |
authProviders
Opcional. Un array de proveedores de inicio de sesión conectables (de nivel superior, junto a auth). Cada entrada es el resultado de llamar a una fábrica de proveedor, como se muestra a continuación:
import { github } from "emdash/auth/providers/github";
import { google } from "emdash/auth/providers/google";
import { atproto } from "@emdash-cms/auth-atproto";
emdash({
authProviders: [github(), google(), atproto()],
});
Proveedores integrados:
github()— leeEMDASH_OAUTH_GITHUB_CLIENT_ID/EMDASH_OAUTH_GITHUB_CLIENT_SECRET(o alternativas sin prefijo).google()— leeEMDASH_OAUTH_GOOGLE_CLIENT_ID/EMDASH_OAUTH_GOOGLE_CLIENT_SECRET.microsoft()— leeEMDASH_OAUTH_MICROSOFT_CLIENT_ID/EMDASH_OAUTH_MICROSOFT_CLIENT_SECRET/EMDASH_OAUTH_MICROSOFT_TENANT_ID(o alternativas sin prefijo). Acepta{ emailVerified }. Consulta Microsoft.atproto()— inicio de sesión con una cuenta de Atmosphere (Bluesky y el resto de la red AT Protocol). No necesita variables de entorno. Acepta{ allowedDIDs, allowedHandles, defaultRole }. Consulta la guía de inicio de sesión con Atmosphere.
Los paquetes de terceros pueden registrar sus propios proveedores con la misma forma AuthProviderDescriptor; consulta Proveedores de inicio de sesión.
mcp
Opcional. Habilita el endpoint del Model Context Protocol (MCP) en /_emdash/api/mcp. El endpoint está habilitado de forma predeterminada y requiere un token de portador, por lo que habilitarlo no concede acceso anónimo.
Establece la opción en false cuando el sitio no deba exponer un endpoint MCP:
emdash({
mcp: false,
});
Consulta la Referencia del servidor MCP para la creación de tokens y la configuración de clientes.
siteUrl
El origen público del sitio, visible para el navegador (esquema + host + puerto opcional, sin ruta). Establécelo antes de ejecutar la configuración de producción, a menos que el sitio se despliegue en Cloudflare Workers. De lo contrario, solo los hosts de desarrollo de loopback pueden completar la configuración sin un origen configurado.
Detrás de un proxy inverso que termina TLS, Astro.url devuelve la dirección interna (http://localhost:4321) en lugar de la pública (https://cms.example.com). Esto rompe las passkeys, la comprobación de origen de CSRF, las redirecciones de OAuth, las redirecciones de inicio de sesión, el descubrimiento de MCP, las exportaciones de instantáneas, el sitemap, robots.txt y los datos estructurados JSON-LD. Establecer siteUrl soluciona todo esto de una vez.
La integración valida este valor al cargarse: debe ser una URL válida con protocolo http: o https: y se normaliza a origen (se elimina la ruta).
El siguiente ejemplo establece el origen público:
emdash({
database: sqlite({ url: "file:./data.db" }),
storage: local({
directory: "./uploads",
baseUrl: "/_emdash/api/media/file",
}),
siteUrl: "https://cms.example.com",
});
Cuando siteUrl no está establecida en la configuración, EmDash comprueba las variables de entorno en este orden: EMDASH_SITE_URL y luego SITE_URL. Esto es útil en despliegues con contenedores, donde la URL pública se establece en tiempo de ejecución.
La configuración falla con SITE_URL_REQUIRED en un host que no sea de loopback cuando no se establece ninguna de las dos fuentes. Esto impide que la primera solicitud de configuración no autenticada elija el origen que se usará en los correos de autenticación posteriores. Una compilación de producción desplegada en Cloudflare Workers es la excepción: Cloudflare solo enruta las solicitudes de los nombres de host vinculados al Worker, por lo que la configuración registra https:// y el nombre de host en el que se ejecuta. La misma compilación servida con wrangler dev, astro preview o workerd autoalojado en una dirección accesible no ofrece esa garantía; establece allí primero siteUrl o EMDASH_SITE_URL.
En Cloudflare Workers, la alternativa mediante variables de entorno lee process.env. Con nodejs_compat, Cloudflare rellena process.env de forma predeterminada para fechas de compatibilidad a partir del 2025-04-01. Los proyectos fijados a una fecha anterior también deben añadir nodejs_compat_populate_process_env.
// wrangler.jsonc
{
"compatibility_date": "2026-02-24",
"compatibility_flags": ["nodejs_compat"],
"vars": { "EMDASH_SITE_URL": "https://cms.example.com" },
}
allowedOrigins
Opcional. Orígenes adicionales del navegador que acepta la verificación de passkeys para un despliegue disponible en más de un nombre de host.
siteUrl define un único origen canónico. Cuando el mismo despliegue de EmDash es accesible bajo varios nombres de host que comparten un dominio padre registrable (p. ej., https://example.com y https://preview.example.com), la verificación de passkeys rechaza las aserciones cuyo origen no coincide exactamente con siteUrl, aunque WebAuthn permite que las passkeys sean válidas entre subdominios bajo el mismo rpId.
Declara los orígenes adicionales aceptados con allowedOrigins en astro.config.mjs o con la variable de entorno EMDASH_ALLOWED_ORIGINS. La siteUrl canónica sigue siendo la fuente del rpId; las entradas indicadas aquí se aceptan en el momento de la verificación. Las dos fuentes se combinan en tiempo de ejecución, de modo que la configuración puede declarar los orígenes estables (versionados y revisados en código) mientras que el entorno añade extras específicos del entorno (p. ej., vistas previas efímeras de PR).
El siguiente ejemplo declara un origen adicional en la configuración:
emdash({
siteUrl: "https://example.com",
allowedOrigins: ["https://preview.example.com"],
})
Los valores equivalentes también pueden provenir de variables de entorno:
EMDASH_SITE_URL=https://example.com
EMDASH_ALLOWED_ORIGINS=https://preview.example.com,https://staging.example.com
Validación
EmDash valida estos valores para evitar configuración inútil que el navegador nunca respetaría:
- Cada entrada debe ser una URL
http:ohttps:analizable, sin punto final y sin etiquetas vacías en el nombre de host. - Cuando
allowedOriginsno está vacío,siteUrldebe estar establecida (en cualquiera de las dos fuentes) y no puede ser un literal de IP ni tener un nombre de host con punto final. - Cada origen debe ser el mismo nombre de host que
siteUrlo un subdominio de este. (WebAuthn exige querpIdsea un sufijo registrable de cada origen.)
Cuando la validación falla, verás un error con la fuente indicada, como EmDash config error in EMDASH_ALLOWED_ORIGINS: "https://other-site.com" is not a subdomain of siteUrl "https://example.com". Allowed origins must be the same hostname as siteUrl or a subdomain of it.
Dónde aparece el error depende de dónde se declaren los valores:
- Al iniciar Astro, cuando tanto
config.allowedOriginscomoconfig.siteUrlprovienen deastro.config.mjs: los errores tipográficos en el código hacen fallar la compilación. - En la primera verificación de passkey, cuando alguno de los valores proviene de
EMDASH_ALLOWED_ORIGINSoEMDASH_SITE_URL: las discrepancias del entorno aparecen como errores 500 en el primer intento de verificación.
Configuración de un proxy inverso
Astro solo refleja X-Forwarded-* cuando el host público está permitido. Configura security.allowedDomains para el nombre de host (y los esquemas) a los que acceden tus usuarios. En astro dev, añade los vite.server.allowedHosts correspondientes para que Vite acepte el encabezado Host del proxy.
Prefiere corregir primero allowedDomains (y los encabezados reenviados); usa siteUrl cuando la URL reconstruida siga divergiendo del origen del navegador (típico cuando TLS se termina por delante y la solicitud upstream sigue siendo http://).
Con TLS por delante, enlazar el servidor de desarrollo a loopback (astro dev --host 127.0.0.1) suele bastar: el proxy se conecta localmente mientras siteUrl coincide con el origen HTTPS público.
Si tu proxy escribe un encabezado con la IP del cliente, establece trustedProxyHeaders para que los límites de tasa de EmDash puedan usar la IP real del cliente. Sin una IP de cliente de confianza, se omiten los límites de tasa de autenticación y todos los envíos públicos de comentarios comparten un único contenedor de límite de tasa (20 comentarios cada 10 minutos entre todos los visitantes).
La siguiente configuración establece juntos allowedDomains, vite.server.allowedHosts y siteUrl para un despliegue detrás de un proxy inverso:
import { defineConfig } from "astro/config";
import emdash, { local } from "emdash/astro";
import { sqlite } from "emdash/db";
export default defineConfig({
security: {
allowedDomains: [
{ hostname: "cms.example.com", protocol: "https" },
{ hostname: "cms.example.com", protocol: "http" },
],
},
vite: {
server: {
allowedHosts: ["cms.example.com"],
},
},
integrations: [
emdash({
database: sqlite({ url: "file:./data.db" }),
storage: local({
directory: "./uploads",
baseUrl: "/_emdash/api/media/file",
}),
siteUrl: "https://cms.example.com",
}),
],
});
trustedProxyHeaders
Opcional. Encabezados en los que confiar para resolver la IP del cliente cuando se ejecuta detrás de un proxy inverso que controlas. Lo usan los límites de tasa de autenticación (enlace mágico, registro, passkey, flujo de dispositivo de OAuth, registro de clientes de OAuth) y el endpoint público de comentarios.
En Cloudflare se usa automáticamente el objeto cf adjunto a la solicitud, por lo que normalmente no necesitas establecer esto. En despliegues autoalojados detrás de nginx, Caddy, Traefik, Fly, Railway o similares, establece aquí el encabezado que escribe tu proxy para que los límites de tasa puedan contar las solicitudes por IP real del cliente.
El siguiente ejemplo confía en el encabezado x-real-ip que establecen nginx, Caddy o Traefik:
emdash({
database: sqlite({ url: "file:./data.db" }),
trustedProxyHeaders: ["x-real-ip"],
});
Los encabezados se prueban por orden. Los valores que coinciden con *-forwarded-for se analizan como listas separadas por comas y se usa la primera entrada. El siguiente ejemplo prefiere el encabezado de Fly.io y recurre a x-forwarded-for:
emdash({
trustedProxyHeaders: ["fly-client-ip", "x-forwarded-for"],
});
Cuando no se establece en la configuración, EmDash lee la variable de entorno EMDASH_TRUSTED_PROXY_HEADERS (separada por comas). Un array vacío explícito en la configuración anula la variable de entorno.
maxUploadSize
Opcional. Tamaño máximo permitido, en bytes, para la subida de archivos multimedia. Se aplica tanto a las subidas multipart directas como a las subidas mediante URL firmada. El valor predeterminado es 52_428_800 (50 MB). El siguiente ejemplo sube el límite a 100 MB:
emdash({
database: sqlite({ url: "file:./data.db" }),
storage: local({
directory: "./uploads",
baseUrl: "/_emdash/api/media/file",
}),
maxUploadSize: 100 * 1024 * 1024, // 100 MB
});
| Valor | Descripción |
|---|---|
number (bytes) | Debe ser un entero finito positivo |
| omitido | Por defecto, 50 MB |
Las subidas que superan el límite configurado se rechazan con una respuesta 413 Payload Too Large en la ruta de subida directa, o con 400 Validation Error en la ruta de URL firmada.
admin
Opcional. Sustituye la marca de EmDash en la interfaz de administración. Estos valores no cambian el título, el logotipo ni el favicon del sitio público.
emdash({
admin: {
logo: "/images/agency-logo.webp",
siteName: "Agency CMS",
footerLabel: "AgencyX",
favicon: "/favicon.ico",
},
});
| Opción | Tipo | Descripción |
|---|---|---|
logo | string | URL o ruta del logotipo para la página de inicio de sesión y la barra lateral |
siteName | string | Nombre que se muestra en la cabecera de la barra lateral y en el título del navegador |
footerLabel | string | false | Etiqueta junto a la versión de la barra lateral. Por defecto "EmDash"; false la oculta |
favicon | string | URL o ruta del favicon para las páginas de administración |
updateCheck
Opcional. Controla el aviso de actualización del núcleo en el panel de administración. El valor predeterminado es true.
Cuando está habilitada, EmDash consulta el registro público de npm en busca de versiones publicadas de emdash (como máximo una vez al día, diferida tras la respuesta para que nunca ralentice una solicitud) y muestra un banner descartable en el panel de administración cuando hay una versión más reciente. Al descartar el banner se oculta para esa versión; reaparece cuando haya disponible una versión más nueva.
El banner nombra la versión estable más reciente que lleva al menos 24 horas pública, de modo que una versión se anuncia un día después de publicarse. Para ajustarte a una política de antigüedad de versiones de tu gestor de paquetes, como minimumReleaseAge de pnpm, establece minimumReleaseAge como una cadena de duración ("48h", "7d") o un número de segundos. Con "7d", el banner ofrece la versión más reciente que tenga al menos una semana:
emdash({
updateCheck: { minimumReleaseAge: "7d" },
})
La comprobación es un simple GET a https://registry.npmjs.org/emdash y no envía datos del sitio. Los despliegues sin acceso saliente a Internet simplemente nunca muestran el banner. Establécela en false para deshabilitar la comprobación por completo:
emdash({
updateCheck: false,
})
El banner es solo informativo: actualizar EmDash significa subir la versión del paquete emdash en tu proyecto y volver a desplegar, algo que la administración no puede (ni debería) desencadenar.
toolbar
Opcional. Controla cómo se entrega la barra de herramientas del editor (la píldora flotante en las páginas públicas). El valor predeterminado es "server".
| Valor | Comportamiento |
|---|---|
"server" (predeterminado) | La barra de herramientas se inyecta en el servidor en cada respuesta HTML renderizada para un editor autenticado. |
"client" | El HTML público es idéntico para todos los visitantes. Un diminuto script de arranque muestra una píldora «Edit» en los navegadores que han iniciado sesión en la administración; al hacer clic en ella se verifica la sesión y se recarga la página con un parámetro de consulta _edit, que siempre se renderiza al momento (nunca desde la caché) con la barra de herramientas completa. |
false | Nunca renderiza la barra de herramientas ni el script de arranque. |
emdash({
toolbar: "client",
})
Usa "client" cuando tu HTML público se sirva a través de una caché compartida (Cloudflare Cache Everything / Workers Cache, Fastly, Varnish, …). Con la inyección en el servidor, un editor que navega por el sitio público recibe la variante anónima en caché —sin la barra de herramientas— siempre que un visitante anónimo haya llenado antes la caché, de modo que la barra de herramientas aparece y desaparece según el estado de la caché. En modo cliente no se inyecta nada específico de la sesión en el HTML compartible, por lo que la caché sigue siendo plenamente eficaz y la barra de herramientas es fiable.
Notas sobre el modo "client":
- Los visitantes sin sesión que abren una URL
?_editcompartida son redirigidos a la URL canónica, de modo que el parámetro no puede filtrar borradores ni llenar entradas de caché adicionales con el contenido de la página. - La señal de «sesión iniciada» es una marca no secreta de
localStorageque establece la administración; la píldora verifica la sesión real antes de entrar en la vista de edición. - El script de arranque es un pequeño
<script>en línea. Si tu sitio envía unContent-Security-Policyestricto sin'unsafe-inline', añade un hash para él; lo mismo se aplica a la barra de herramientas inyectada en el servidor. - EmDash no inyecta nada específico de la sesión, pero si tus propias plantillas se ramifican según
Astro.locals.user(p. ej., un enlace de navegación «Admin» para usuarios con sesión iniciada), esa variación sigue estando en tu HTML y sigue fragmentando la caché.
En cualquier modo, la barra de herramientas puede descartarse en el navegador con su botón × (por navegador, hasta la próxima vez que un editor abra la administración). Las respuestas de vista previa y de modo de edición siempre se renderizan en el servidor con Cache-Control: private, no-store.
Adaptadores de base de datos
Importa los adaptadores desde emdash/db:
import { sqlite, libsql, postgres } from "emdash/db";
sqlite(config)
Base de datos SQLite que usa el controlador de base de datos integrado de Node.js. El siguiente ejemplo se conecta a un archivo local:
| Opción | Tipo | Descripción |
|---|---|---|
url | string | Ruta del archivo con prefijo file: |
sqlite({ url: "file:./data.db" });
libsql(config)
Base de datos libSQL. El siguiente ejemplo se conecta a una base de datos libSQL remota:
| Opción | Tipo | Descripción |
|---|---|---|
url | string | URL de la base de datos |
authToken | string | Token de autenticación en tiempo de ejecución (opcional para archivos locales) |
migrationAuthTokenEnv | string | Nombre de la variable del token de migración (por defecto TURSO_AUTH_TOKEN) |
libsql({
url: process.env.LIBSQL_DATABASE_URL,
authToken: process.env.LIBSQL_AUTH_TOKEN,
});
postgres(config)
Base de datos PostgreSQL con pool de conexiones.
| Opción | Tipo | Descripción |
|---|---|---|
connectionString | string | URL de conexión de PostgreSQL |
host | string | Host de la base de datos |
port | number | Puerto de la base de datos |
database | string | Nombre de la base de datos |
user | string | Usuario de la base de datos |
password | string | Contraseña de la base de datos |
ssl | boolean | Habilita SSL |
pool.min | number | Tamaño mínimo del pool (por defecto: 0) |
pool.max | number | Tamaño máximo del pool (por defecto: 10) |
pool.connectionTimeoutMillis | number | Espera máxima de conexión (por defecto en pg: 0, sin tiempo de espera) |
pool.idleTimeoutMillis | number | Vida útil de un cliente inactivo (por defecto en pg: 10 000 ms) |
migrationConnectionStringEnv | string | Nombre de la variable de la cadena de conexión de migración (por defecto DATABASE_URL) |
El siguiente ejemplo se conecta con una cadena de conexión:
postgres({ connectionString: process.env.DATABASE_URL });
d1(config)
Base de datos Cloudflare D1. Se importa desde @emdash-cms/cloudflare.
| Opción | Tipo | Valor predeterminado | Descripción |
|---|---|---|---|
binding | string | — | Nombre del binding de D1 de wrangler.jsonc |
session | string | "disabled" | Modo de replicación de lectura: "disabled", "auto" o "primary-first" |
bookmarkCookie | string | "__em_d1_bookmark" | Nombre de la cookie para los marcadores de sesión |
coalesce | boolean | false | Agrupa las lecturas concurrentes en el mismo turno del bucle de eventos; requiere un modo de sesión distinto de "disabled" |
El siguiente ejemplo muestra un binding básico y otro con réplicas de lectura habilitadas:
// Basic
d1({ binding: "DB" });
// With read replicas
d1({ binding: "DB", session: "auto" });
Cuando session es "auto" o "primary-first", EmDash usa la API de sesiones de D1 para dirigir las consultas de lectura a réplicas cercanas. Los usuarios autenticados obtienen consistencia de lectura de sus propias escrituras basada en marcadores. Consulta Opciones de base de datos — Réplicas de lectura para más detalles.
hyperdrive(config?)
PostgreSQL a través de un binding de Cloudflare Hyperdrive. Importa este adaptador desde @emdash-cms/cloudflare.
| Opción | Tipo | Valor predeterminado | Descripción |
|---|---|---|---|
binding | string | "HYPERDRIVE" | Binding principal de Hyperdrive con la caché de consultas deshabilitada |
cachedBinding | string | — | Segundo binding opcional, con caché habilitada, para lecturas públicas anónimas |
preferUncachedAfterWriteMs | number | 60_000 | Durante cuánto tiempo las lecturas públicas usan el binding principal tras una escritura de contenido cuando se establece cachedBinding |
migrationConnectionStringEnv | string | Derivado del binding principal | Variable de entorno que contiene la URL directa de PostgreSQL que usa emdash migrate |
max | number | 5 | Máximo de conexiones de un aislado de Worker a Hyperdrive |
El siguiente ejemplo dirige las solicitudes autenticadas y las escrituras por el binding sin caché, mientras que las lecturas públicas anónimas pueden usar el binding con caché:
hyperdrive({
binding: "HYPERDRIVE",
cachedBinding: "HYPERDRIVE_CACHED",
preferUncachedAfterWriteMs: 60_000,
});
Ambos bindings deben apuntar a la misma base de datos. Instala pg versión 8.16.3 o posterior, habilita la bandera de compatibilidad nodejs_compat y configura una URL directa de base de datos para las migraciones del despliegue. Consulta Opciones de base de datos: Hyperdrive para la configuración completa del Worker y de las migraciones.
durableObjects(config)
Almacena el CMS en un único Durable Object respaldado por SQLite. Importa este adaptador desde @emdash-cms/cloudflare.
| Opción | Tipo | Valor predeterminado | Descripción |
|---|---|---|---|
binding | string | obligatoria | Binding del espacio de nombres de Durable Object para la clase EmDashDB |
name | string | "emdash" | Nombre del objeto singleton; cámbialo solo para aislar varias bases de datos detrás de un binding |
session | string | "disabled" | "auto" dirige las lecturas anónimas a réplicas y las escrituras al primario |
bookmarkCookie | string | "__em_do_bookmark" | Cookie usada para la consistencia de lectura de las propias escrituras en el modo "auto" |
durableObjects({ binding: "DB_DO", session: "auto" });
El enrutamiento a réplicas requiere las banderas de compatibilidad experimental y replica_routing, además de las entradas de la clase de Durable Object y de migración en wrangler.jsonc.
previewDatabase(config)
Crea una base de datos de instantánea aislada por cada sesión de vista previa en un Durable Object. La única opción es el nombre binding, obligatorio:
previewDatabase({ binding: "PREVIEW_DB" });
Este adaptador es para la infraestructura de vistas previas, no para la base de datos principal de un sitio en producción.
playgroundDatabase(config)
Crea una base de datos con permiso de escritura y rellenada con semilla por cada sesión de playground en un Durable Object. Combínalo con la opción de integración playground:
playgroundDatabase({ binding: "PLAYGROUND_DB" });
El binding obligatorio identifica el espacio de nombres de Durable Object del playground. Usa este adaptador solo para sitios de demostración desechables.
Adaptadores de almacenamiento
Importa local y s3 desde emdash/astro. El adaptador r2 se importa desde @emdash-cms/cloudflare:
import emdash, { local, s3 } from "emdash/astro";
import { r2 } from "@emdash-cms/cloudflare";
local(config)
Almacenamiento en el sistema de archivos local. El siguiente ejemplo sirve las subidas desde un directorio local:
| Opción | Tipo | Descripción |
|---|---|---|
directory | string | Ruta del directorio |
baseUrl | string | URL base para servir los archivos |
local({
directory: "./uploads",
baseUrl: "/_emdash/api/media/file",
});
r2(config)
Binding de Cloudflare R2. El siguiente ejemplo usa un binding de R2 con una URL pública:
| Opción | Tipo | Descripción |
|---|---|---|
binding | string | Nombre del binding de R2 |
publicUrl | string | URL pública opcional |
r2({
binding: "MEDIA",
publicUrl: "https://pub-xxxx.r2.dev",
});
s3(config?)
Almacenamiento compatible con S3. Todos los campos de configuración son opcionales: cualquier campo omitido en
s3({...}) se resuelve a partir de la variable de entorno S3_* correspondiente cuando se inicia el
proceso de Node. Los valores explícitos siempre tienen prioridad.
Una vez combinados los valores de la configuración y del entorno, endpoint y bucket son obligatorios. Si se establece alguna de las credenciales, accessKeyId y secretAccessKey son ambas obligatorias. Los valores que faltan hacen fallar el arranque con el código de error MISSING_S3_CONFIG.
Requisito previo: instala @aws-sdk/client-s3 y @aws-sdk/s3-request-presigner
en tu proyecto. El núcleo de EmDash no incluye el SDK de AWS. Consulta
Opciones de almacenamiento: almacenamiento compatible con S3
para más detalles.
| Opción | Tipo | Descripción |
|---|---|---|
endpoint | string | URL del endpoint de S3 (S3_ENDPOINT) |
bucket | string | Nombre del bucket (S3_BUCKET) |
accessKeyId | string | Clave de acceso (S3_ACCESS_KEY_ID) |
secretAccessKey | string | Clave secreta (S3_SECRET_ACCESS_KEY) |
region | string | Región, por defecto "auto" (S3_REGION) |
publicUrl | string | URL de CDN opcional (S3_PUBLIC_URL) |
Los siguientes ejemplos resuelven todos los campos desde el entorno, mezclan configuración y entorno, o pasan cada campo de forma explícita:
// All fields from S3_* environment variables (Node container deployments)
s3()
// Mix: CDN from config, rest from environment
s3({ publicUrl: "https://cdn.example.com" })
// All explicit
s3({
endpoint: "https://xxx.r2.cloudflarestorage.com",
bucket: "media",
accessKeyId: process.env.R2_ACCESS_KEY_ID,
secretAccessKey: process.env.R2_SECRET_ACCESS_KEY,
publicUrl: "https://cdn.example.com",
})
La resolución de variables de entorno en tiempo de ejecución es una función exclusiva de Node. En Cloudflare
Workers, los secretos y las variables se exponen mediante el parámetro env del
manejador fetch, no mediante process.env, por lo que las variables de entorno S3_*
no se recogen. Los despliegues en Workers deben usar el adaptador r2(config)
o pasar valores explícitos a s3({...}). Consulta
Opciones de almacenamiento para más detalles.
Adaptadores de caché de objetos
Pasa uno de estos a la opción objectCache.
kvCache(config)
Backend de Cloudflare KV, compartido entre todos los aislados. Se importa desde @emdash-cms/cloudflare.
kvCache({
binding: "CACHE", // KV binding name (required)
defaultTtl: 3600, // entry TTL in seconds (optional, KV minimum 60)
revalidate: 1000, // cross-isolate staleness window in ms (optional)
timeout: 2000, // per-op timeout in ms before a miss (optional, 0 disables)
keyPrefix: "em", // cache key prefix (optional)
})
memoryCache(config?)
Backend en proceso para Node.js y desarrollo. Se importa desde emdash/astro.
memoryCache({
defaultTtl: 3600, // entry TTL in seconds (optional)
revalidate: 1000, // staleness window in ms (optional)
maxEntries: 1000, // max cached keys before eviction (optional)
keyPrefix: "em", // cache key prefix (optional)
})
Consulta Caché de objetos para la configuración y el comportamiento.
Adaptadores de autenticación y sandbox
Estos adaptadores devuelven valores para las opciones de integración auth y sandboxRunner.
access(config)
Sustituye el inicio de sesión con passkeys integrado por la autenticación de Cloudflare Access. Impórtalo desde @emdash-cms/cloudflare y pasa el resultado a auth:
import { access } from "@emdash-cms/cloudflare";
emdash({
auth: access({
teamDomain: "myteam.cloudflareaccess.com",
audienceEnvVar: "CF_ACCESS_AUDIENCE",
}),
});
teamDomain es obligatorio. El adaptador puede leer la audiencia de la aplicación de audience o de la variable que indica audienceEnvVar; también acepta autoProvision, defaultRole, syncRoles y roleMapping. La opción auth documenta sus valores predeterminados y el comportamiento de los roles.
sandbox()
Selecciona Cloudflare Worker Loader como ejecutor del sandbox de plugins. Impórtalo desde @emdash-cms/cloudflare y pasa su valor de retorno a sandboxRunner:
import { sandbox } from "@emdash-cms/cloudflare";
emdash({
sandboxRunner: sandbox(),
});
El sitio también necesita un binding de Worker Loader y el punto de entrada del puente de plugins. Consulta Sandbox de plugins: Cloudflare Workers para esos ajustes de despliegue.
Adaptadores de proveedores de medios
Pasa descriptores de proveedores de medios a mediaProviders. Ambos proveedores integrados de Cloudflare se importan desde @emdash-cms/cloudflare.
Cada opción *EnvVar siguiente nombra una variable de entorno. El proveedor la lee en este orden: un binding de Cloudflare Workers con ese nombre y, después, process.env en el adaptador de Node. La opción directa correspondiente (accountId, accountHash, apiToken) siempre tiene prioridad sobre ambos.
cloudflareImages(config)
Añade Cloudflare Images para explorar, subir, eliminar y entregar recursos de imagen.
| Opción | Tipo | Valor predeterminado | Descripción |
|---|---|---|---|
accountId | string | De CF_ACCOUNT_ID | ID de la cuenta de Cloudflare |
accountIdEnvVar | string | "CF_ACCOUNT_ID" | Variable usada cuando se omite accountId |
accountHash | string | De CF_IMAGES_ACCOUNT_HASH | Hash de la cuenta usado en las URL de entrega |
accountHashEnvVar | string | "CF_IMAGES_ACCOUNT_HASH" | Variable usada cuando se omite accountHash |
apiToken | string | De CF_IMAGES_TOKEN | Token con permisos de lectura y edición de Cloudflare Images |
apiTokenEnvVar | string | "CF_IMAGES_TOKEN" | Variable usada cuando se omite apiToken |
deliveryDomain | string | imagedelivery.net | Nombre de host personalizado para la entrega de imágenes |
defaultVariant | string | "public" | Variante de imagen usada para mostrar |
mediaProviders: [cloudflareImages({ defaultVariant: "public" })];
cloudflareStream(config)
Añade Cloudflare Stream para explorar, buscar, subir, eliminar y reproducir recursos de vídeo.
| Opción | Tipo | Valor predeterminado | Descripción |
|---|---|---|---|
accountId | string | De CF_ACCOUNT_ID | ID de la cuenta de Cloudflare |
accountIdEnvVar | string | "CF_ACCOUNT_ID" | Variable usada cuando se omite accountId |
apiToken | string | De CF_STREAM_TOKEN | Token con permisos de lectura y edición de Cloudflare Stream |
apiTokenEnvVar | string | "CF_STREAM_TOKEN" | Variable usada cuando se omite apiToken |
customerSubdomain | string | Predeterminado de Cloudflare | Nombre de host personalizado para la entrega de Stream |
controls | boolean | true | Muestra los controles del reproductor |
autoplay | boolean | false | Inicia la reproducción automáticamente |
loop | boolean | false | Repite la reproducción |
muted | boolean | false, o true con autoplay | Silencia la reproducción |
mediaProviders: [cloudflareStream({ controls: true })];
Consulta Biblioteca de medios: Proveedores de medios para los bindings y componentes de renderizado necesarios.
Colecciones en vivo
Configura el cargador de EmDash en src/live.config.ts:
import { defineLiveCollection } from "astro:content";
import { emdashLoader } from "emdash/runtime";
export const collections = {
_emdash: defineLiveCollection({
loader: emdashLoader(),
}),
};
Opciones del cargador
La función emdashLoader() no recibe argumentos:
emdashLoader();
Variables de entorno
EmDash respeta estas variables de entorno:
| Variable | Descripción |
|---|---|
EMDASH_SITE_URL | Origen público visible para el navegador (recurre a SITE_URL) |
EMDASH_ALLOWED_ORIGINS | Lista separada por comas de orígenes adicionales que acepta la verificación de passkeys (despliegues con varios subdominios). |
EMDASH_DATABASE_URL | Sobrescribe la URL de la base de datos |
EMDASH_ENCRYPTION_KEY | Clave para cifrar en reposo los secretos de los plugins. La proporciona el operador y nunca se almacena en la base de datos. |
EMDASH_PREVIEW_SECRET | Sobrescritura opcional del secreto HMAC de la vista previa. Si no se establece, se genera un valor estable por sitio y se almacena en la base de datos. |
EMDASH_IP_SALT | Sobrescritura opcional de la sal del hash de IP de quienes comentan. Si no se establece, se genera un valor estable por sitio y se almacena en la base de datos. |
EMDASH_AUTH_SECRET | Heredada. Se usa como fuente de la sal de IP si está establecida; las instalaciones existentes deben conservarla para mantener hashes de IP de comentaristas estables al actualizar. |
EMDASH_TURNSTILE_SECRET_KEY | Clave secreta de Cloudflare Turnstile (recurre a TURNSTILE_SECRET_KEY). Cuando se establece, los envíos de comentarios deben incluir un token de Turnstile válido; combínala con la propiedad turnstileSiteKey de <CommentForm>. |
EMDASH_URL | URL remota de EmDash para la sincronización del esquema |
Genera una clave de cifrado con el siguiente comando:
npx emdash secrets generate
Configuración de package.json
Las plantillas y los sitios pueden declarar metadatos opcionales bajo la clave emdash de package.json:
{
"emdash": {
"label": "My Blog Template",
"schema": ".emdash/schema.sql",
"seed": ".emdash/seed.json"
}
}
| Opción | Descripción |
|---|---|
label | Nombre de la plantilla para mostrar |
schema | Esquema SQL opcional que lee emdash init |
seed | Ruta del archivo JSON de semilla |
Configuración de TypeScript
Durante el desarrollo local, la integración de Astro genera emdash-env.d.ts en la raíz del proyecto y lo actualiza tras los cambios de esquema. El archivo amplía el módulo emdash, de modo que las importaciones estándar getEmDashCollection() y getEmDashEntry() infieren los campos de las colecciones locales sin un alias de ruta.
El comando independiente emdash types obtiene el esquema de una instancia local o remota en ejecución y escribe .emdash/types.ts de forma predeterminada. Añade un alias solo cuando el código de la aplicación importe directamente esa salida independiente:
{
"compilerOptions": {
"paths": {
"@emdash-cms/types": ["./.emdash/types.ts"]
}
}
}
Genera los tipos independientes del esquema remoto con el siguiente comando:
npx emdash types