O sistema de plugins do EmDash permite que você estenda o CMS sem modificar o código principal. Os plugins podem se conectar a eventos do ciclo de vida do conteúdo, armazenar seus próprios dados, expor configurações aos administradores e adicionar UI personalizada ao painel admin.
Filosofia de design
Os plugins EmDash vêm em dois tipos: sandboxed e nativos. Plugins sandboxed são executados em workers V8 isolados e podem ser instalados do marketplace com um clique. Plugins nativos são executados em processo e são configurados no código.
Prefira plugins sandboxed. Eles podem ser instalados, atualizados e removidos da UI admin sem tocar no código ou reimplantar. Use plugins nativos apenas quando precisar de recursos que exigem integração no momento do build (páginas admin React, componentes de renderização Portable Text ou injeção de fragmentos de página).
Princípios-chave:
- Sandbox-first — Projete para o sandbox; use o modo nativo apenas quando necessário
- Declarativo — Hooks, armazenamento e rotas são declarados no momento da definição, não registrados dinamicamente
- Type-safe — Suporte completo de TypeScript com objetos de contexto tipados
- Baseado em capacidades — Os plugins declaram o que precisam; o sandbox aplica
O que os plugins podem fazer
Conectar-se a eventos
Executar código antes ou depois de salvamentos de conteúdo, uploads de mídia e eventos do ciclo de vida de plugins.
Armazenar dados
Persistir dados específicos do plugin em coleções indexadas sem escrever migrações de banco de dados.
Expor configurações
Declarar um esquema de configurações e obter uma UI admin auto-gerada para configuração.
Adicionar páginas admin
Criar páginas admin personalizadas e widgets de dashboard com componentes React.
Criar rotas API
Expor endpoints para a UI admin do seu plugin ou integrações externas.
Fazer solicitações HTTP
Chamar APIs externas com restrições de host declaradas para segurança.
Arquitetura do plugin
Cada plugin é criado com definePlugin():
import { definePlugin } from "emdash";
export default definePlugin({
id: "my-plugin",
version: "1.0.0",
// APIs que o plugin precisa acessar
capabilities: ["read:content", "network:fetch"],
// Hosts aos quais o plugin pode fazer solicitações HTTP
allowedHosts: ["api.example.com"],
// Coleções de armazenamento persistente
storage: {
entries: {
indexes: ["userId", "createdAt"],
},
},
// Manipuladores 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 }),
},
},
// Configuração da UI admin
admin: {
settingsSchema: {
apiKey: { type: "secret", label: "API Key" },
},
pages: [{ path: "/dashboard", label: "Dashboard" }],
widgets: [{ id: "status", size: "half" }],
},
});
Contexto do plugin
Cada hook e manipulador de rota recebe um objeto PluginContext com acesso a:
| Propriedade | Descrição | Disponibilidade |
|---|---|---|
ctx.storage | Coleções de documentos do plugin | Sempre (se declarado) |
ctx.kv | Armazenamento chave-valor para configurações e estado | Sempre |
ctx.content | Leitura/escrita de conteúdo do site | Com read:content ou write:content |
ctx.media | Leitura/escrita de arquivos de mídia | Com read:media ou write:media |
ctx.http | Cliente HTTP para solicitações externas | Com network:fetch |
ctx.log | Logger estruturado (debug, info, warn, error) | Sempre |
ctx.plugin | Metadados do plugin (id, version) | Sempre |
ctx.site | Info do site: name, url, locale | Sempre |
ctx.url() | Gerar URLs absolutas a partir de caminhos | Sempre |
ctx.users | Ler info de usuários: get(), getByEmail(), list() | Com read:users |
ctx.cron | Agendar tarefas: schedule(), cancel(), list() | Sempre |
ctx.email | Enviar email: send() | Com email:send + provedor configurado |
A forma do contexto é idêntica em todos os hooks e rotas. Propriedades protegidas por capacidade só estão presentes quando o plugin declara a capacidade necessária.
Capacidades
As capacidades determinam quais APIs estão disponíveis no contexto do plugin:
| Capacidade | Concede acesso 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() (restrito a allowedHosts) |
network:fetch:any | ctx.http.fetch() (sem restrições — para URLs configuradas pelo usuário) |
read:users | ctx.users.get(), ctx.users.getByEmail(), ctx.users.list() |
email:send | ctx.email.send() (requer plugin provedor) |
email:provide | Registrar hook exclusivo email:deliver (provedor transporte) |
email:intercept | Registrar hooks email:beforeSend / email:afterSend |
page:inject | Registrar hooks page:metadata / page:fragments |
Registro
Registre plugins em sua configuração 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 })],
}),
],
});
Os plugins são resolvidos no momento do build. A ordem importa para hooks com a mesma prioridade — plugins anteriores no array são executados primeiro.
Modos de execução
EmDash suporta dois modos de execução de plugins:
| Modo | Descrição | Plataforma |
|---|---|---|
| Sandboxed | Workers V8 isolados com limites forçados | Apenas Cloudflare |
| Nativo | Em processo com acesso completo | Qualquer |
No modo sandboxed, as capacidades são aplicadas no nível de runtime — os plugins só podem acessar o que declaram. No modo nativo, as capacidades são consultivas e os plugins têm acesso completo ao processo.
Próximos passos
Criar um plugin
Construa seu primeiro plugin com armazenamento, hooks e UI admin.
Hooks disponíveis
Navegue por todos os hooks para conteúdo, mídia e ciclo de vida de plugins.
Armazenamento de plugin
Aprenda sobre armazenamento e como consultar dados de plugins.
UI admin
Adicione páginas admin e widgets de dashboard.
Nativo vs. Sandboxed
Compare modos de execução e escolha o certo para seu plugin.