El sistema de plugins de EmDash te permite extender el CMS sin modificar el código principal. Los plugins pueden engancharse a eventos del ciclo de vida del contenido, almacenar sus propios datos, exponer configuraciones a los administradores y añadir interfaz de usuario personalizada al panel de administración.
Filosofía de diseño
Los plugins de EmDash vienen en dos sabores: sandboxed y nativos. Los plugins sandboxed se ejecutan en workers V8 aislados y pueden instalarse desde el mercado con un clic. Los plugins nativos se ejecutan en proceso y se configuran en código.
Prefiere plugins sandboxed. Pueden ser instalados, actualizados y eliminados desde la interfaz de administración sin tocar código o redesplegar. Usa plugins nativos solo cuando necesites características que requieran integración en tiempo de compilación (páginas admin de React, componentes de renderizado Portable Text o inyección de fragmentos de página).
Principios clave:
- Sandbox-first — Diseña para el sandbox; usa el modo nativo solo cuando lo necesites
- Declarativo — Los hooks, el almacenamiento y las rutas se declaran en el momento de la definición, no se registran dinámicamente
- Type-safe — Soporte completo de TypeScript con objetos de contexto tipados
- Basado en capacidades — Los plugins declaran lo que necesitan; el sandbox lo aplica
Lo que los plugins pueden hacer
Engancharse a eventos
Ejecutar código antes o después de guardados de contenido, subidas de medios y eventos del ciclo de vida de plugins.
Almacenar datos
Persistir datos específicos del plugin en colecciones indexadas sin escribir migraciones de base de datos.
Exponer configuración
Declarar un esquema de configuración y obtener una interfaz admin autogenerada para la configuración.
Añadir páginas admin
Crear páginas admin personalizadas y widgets de panel con componentes React.
Crear rutas API
Exponer endpoints para la interfaz admin de tu plugin o integraciones externas.
Hacer solicitudes HTTP
Llamar a APIs externas con restricciones de host declaradas para seguridad.
Arquitectura de plugin
Cada plugin se crea con definePlugin():
import { definePlugin } from "emdash";
export default definePlugin({
id: "my-plugin",
version: "1.0.0",
// APIs a las que el plugin necesita acceder
capabilities: ["read:content", "network:fetch"],
// Hosts a los que el plugin puede hacer solicitudes HTTP
allowedHosts: ["api.example.com"],
// Colecciones de almacenamiento persistente
storage: {
entries: {
indexes: ["userId", "createdAt"],
},
},
// Manejadores de eventos
hooks: {
"content:afterSave": async (event, ctx) => {
ctx.log.info("Content saved", { id: event.content.id });
},
},
// Endpoints REST API
routes: {
status: {
handler: async (ctx) => ({ ok: true }),
},
},
// Configuración de interfaz admin
admin: {
settingsSchema: {
apiKey: { type: "secret", label: "API Key" },
},
pages: [{ path: "/dashboard", label: "Dashboard" }],
widgets: [{ id: "status", size: "half" }],
},
});
Contexto del plugin
Cada hook y manejador de ruta recibe un objeto PluginContext con acceso a:
| Propiedad | Descripción | Disponibilidad |
|---|---|---|
ctx.storage | Colecciones de documentos del plugin | Siempre (si se declara) |
ctx.kv | Almacén clave-valor para configuración y estado | Siempre |
ctx.content | Lectura/escritura de contenido del sitio | Con read:content o write:content |
ctx.media | Lectura/escritura de archivos multimedia | Con read:media o write:media |
ctx.http | Cliente HTTP para solicitudes externas | Con network:fetch |
ctx.log | Logger estructurado (debug, info, warn, error) | Siempre |
ctx.plugin | Metadatos del plugin (id, version) | Siempre |
ctx.site | Info del sitio: name, url, locale | Siempre |
ctx.url() | Generar URLs absolutas desde rutas | Siempre |
ctx.users | Leer info de usuarios: get(), getByEmail(), list() | Con read:users |
ctx.cron | Programar tareas: schedule(), cancel(), list() | Siempre |
ctx.email | Enviar correo: send() | Con email:send + proveedor configurado |
La forma del contexto es idéntica en todos los hooks y rutas. Las propiedades protegidas por capacidad solo están presentes cuando el plugin declara la capacidad requerida.
Capacidades
Las capacidades determinan qué APIs están disponibles en el contexto del plugin:
| Capacidad | Otorga acceso a |
|---|---|
read:content | ctx.content.get(), ctx.content.list() |
write:content | ctx.content.create(), ctx.content.update(), ctx.content.delete() |
read:media | ctx.media.get(), ctx.media.list() |
write:media | ctx.media.getUploadUrl(), ctx.media.upload(), ctx.media.delete() |
network:fetch | ctx.http.fetch() (restringido a allowedHosts) |
network:fetch:any | ctx.http.fetch() (sin restricciones — para URLs configuradas por usuario) |
read:users | ctx.users.get(), ctx.users.getByEmail(), ctx.users.list() |
email:send | ctx.email.send() (requiere plugin proveedor) |
email:provide | Registrar hook exclusivo email:deliver (proveedor transporte) |
email:intercept | Registrar hooks email:beforeSend / email:afterSend |
page:inject | Registrar hooks page:metadata / page:fragments |
Registro
Registra plugins en tu configuración Astro:
import { defineConfig } from "astro/config";
import { emdash } from "emdash/astro";
import seoPlugin from "@emdash-cms/plugin-seo";
import auditLogPlugin from "@emdash-cms/plugin-audit-log";
export default defineConfig({
integrations: [
emdash({
plugins: [seoPlugin({ generateSitemap: true }), auditLogPlugin({ retentionDays: 90 })],
}),
],
});
Los plugins se resuelven en tiempo de compilación. El orden importa para hooks con la misma prioridad — los plugins anteriores en el array se ejecutan primero.
Modos de ejecución
EmDash soporta dos modos de ejecución de plugins:
| Modo | Descripción | Plataforma |
|---|---|---|
| Sandboxed | Workers V8 aislados con límites forzados | Solo Cloudflare |
| Nativo | En proceso con acceso completo | Cualquiera |
En modo sandboxed, las capacidades se aplican a nivel de runtime — los plugins solo pueden acceder a lo que declaran. En modo nativo, las capacidades son consultivas y los plugins tienen acceso completo al proceso.
Próximos pasos
Crear un plugin
Construye tu primer plugin con almacenamiento, hooks e interfaz admin.
Hooks disponibles
Explora todos los hooks para contenido, medios y ciclo de vida de plugins.
Almacenamiento de plugin
Aprende sobre almacenamiento y cómo consultar datos de plugins.
Interfaz admin
Añade páginas admin y widgets de panel.
Nativo vs. Sandboxed
Compara modos de ejecución y elige el correcto para tu plugin.