Ir al contenido

Visión general del sistema de plugins

El sistema de plugins de EmDash te permite extender el CMS sin modificar el código central. Los plugins pueden conectarse 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.

Los plugins de EmDash son transformadores de configuración, no aplicaciones separadas. Se ejecutan en el mismo proceso que tu sitio Astro e interactúan a través de interfaces bien definidas.

Principios clave:

  • Declarativo — Los hooks, el almacenamiento y las rutas se declaran en el momento de la definición, no se registran dinámicamente
  • Con seguridad de tipos — Soporte completo de TypeScript con objetos de contexto tipados
  • Listo para aislamiento — APIs diseñadas para ejecución aislada en Cloudflare Workers
  • Basado en capacidades — Los plugins declaran lo que necesitan; el tiempo de ejecución hace cumplir el acceso

Conectarse a eventos

Ejecutar código antes o después de que se guarden contenidos, se suban archivos multimedia y eventos del ciclo de vida del plugin.

Almacenar datos

Persistir datos específicos del plugin en colecciones indexadas sin escribir migraciones de base de datos.

Exponer configuraciones

Declarar un esquema de configuración y obtener una interfaz de administración generada automáticamente.

Añadir páginas de administración

Crear páginas de administración personalizadas y widgets del panel con componentes React.

Crear rutas API

Exponer endpoints para la interfaz de usuario de administración de tu plugin o integraciones externas.

Realizar solicitudes HTTP

Llamar a APIs externas con restricciones de host declaradas por seguridad.

Cada plugin se crea con definePlugin():

import { definePlugin } from "emdash";
export default definePlugin({
id: "my-plugin",
version: "1.0.0",
// A qué APIs necesita acceder el plugin
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("Contenido guardado", { id: event.content.id });
},
},
// Endpoints de la API REST
routes: {
status: {
handler: async (ctx) => ({ ok: true }),
},
},
// Configuración de la interfaz de usuario de administración
admin: {
settingsSchema: {
apiKey: { type: "secret", label: "Clave de API" },
},
pages: [{ path: "/dashboard", label: "Panel" }],
widgets: [{ id: "status", size: "half" }],
},
});

Cada hook y manejador de ruta recibe un objeto PluginContext con acceso a:

PropiedadDescripciónDisponibilidad
ctx.storageColecciones de documentos del pluginSiempre (si se declara)
ctx.kvAlmacén clave-valor para configuraciones y estadoSiempre
ctx.contentLeer/escribir contenido del sitioCon read:content o write:content
ctx.mediaLeer/escribir archivos multimediaCon read:media o write:media
ctx.httpCliente HTTP para solicitudes externasCon network:fetch
ctx.logLogger estructurado (debug, info, warn, error)Siempre
ctx.pluginMetadatos del plugin (id, versión)Siempre
ctx.siteInformación del sitio: name, url, localeSiempre
ctx.url()Generar URLs absolutas desde rutasSiempre
ctx.usersLeer información de usuario: get(), getByEmail(), list()Con read:users
ctx.cronProgramar tareas: schedule(), cancel(), list()Siempre
ctx.emailEnviar correo: send()Con email:send + proveedor configurado

La forma del contexto es idéntica en todos los hooks y rutas. Las propiedades controladas por capacidades solo están presentes cuando el plugin declara la capacidad requerida.

Las capacidades determinan qué APIs están disponibles en el contexto del plugin:

CapacidadOtorga Acceso A
read:contentctx.content.get(), ctx.content.list()
write:contentctx.content.create(), ctx.content.update(), ctx.content.delete()
read:mediactx.media.get(), ctx.media.list()
write:mediactx.media.getUploadUrl(), ctx.media.upload(), ctx.media.delete()
network:fetchctx.http.fetch() (restringido a allowedHosts)
network:fetch:anyctx.http.fetch() (sin restricciones — para URLs configuradas por el usuario)
read:usersctx.users.get(), ctx.users.getByEmail(), ctx.users.list()
email:sendctx.email.send() (requiere un plugin proveedor)
email:provideRegistrar hook exclusivo email:deliver (proveedor de transporte)
email:interceptRegistrar hooks email:beforeSend / email:afterSend
page:injectRegistrar hooks page:metadata / page:fragments

Registra los plugins en tu configuración de Astro:

astro.config.mjs
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 los hooks con la misma prioridad: los que aparezcan antes en el arreglo se ejecutan primero.

EmDash admite dos modos de ejecución de complementos:

ModoDescripciónPlataforma
ConfiableLos plugins se ejecutan en proceso con acceso completoCualquiera
AisladoLos plugins se ejecutan en workers V8 aisladosSolo Cloudflare

En el modo confiable, que es el predeterminado, las capacidades son documentación: los plugins pueden acceder a cualquier recurso. En el modo aislado, las capacidades sí se aplican en tiempo de ejecución.