Visão geral do sistema de plugins

Nesta página

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:

PropriedadeDescriçãoDisponibilidade
ctx.storageColeções de documentos do pluginSempre (se declarado)
ctx.kvArmazenamento chave-valor para configurações e estadoSempre
ctx.contentLeitura/escrita de conteúdo do siteCom read:content ou write:content
ctx.mediaLeitura/escrita de arquivos de mídiaCom read:media ou write:media
ctx.httpCliente HTTP para solicitações externasCom network:fetch
ctx.logLogger estruturado (debug, info, warn, error)Sempre
ctx.pluginMetadados do plugin (id, version)Sempre
ctx.siteInfo do site: name, url, localeSempre
ctx.url()Gerar URLs absolutas a partir de caminhosSempre
ctx.usersLer info de usuários: get(), getByEmail(), list()Com read:users
ctx.cronAgendar tarefas: schedule(), cancel(), list()Sempre
ctx.emailEnviar 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:

CapacidadeConcede acesso 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() (restrito a allowedHosts)
network:fetch:anyctx.http.fetch() (sem restrições — para URLs configuradas pelo usuário)
read:usersctx.users.get(), ctx.users.getByEmail(), ctx.users.list()
email:sendctx.email.send() (requer plugin provedor)
email:provideRegistrar hook exclusivo email:deliver (provedor transporte)
email:interceptRegistrar hooks email:beforeSend / email:afterSend
page:injectRegistrar 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:

ModoDescriçãoPlataforma
SandboxedWorkers V8 isolados com limites forçadosApenas Cloudflare
NativoEm processo com acesso completoQualquer

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