O manifesto do plugin

Nesta página

Cada plugin em sandbox tem um emdash-plugin.jsonc ao lado do seu package.json. É editado manualmente e contém a identidade do plugin, seu contrato de confiança (capabilities, hosts, storage) e os campos de perfil que o registro exibe. emdash-plugin init cria um; a CLI lê ./emdash-plugin.jsonc automaticamente para build, dev, validate, bundle e publish.

O arquivo é JSONC: comentários e vírgulas finais são permitidos.

O exemplo a seguir mostra um manifesto completo para um plugin de galeria de imagens:

{
	"$schema": "./node_modules/@emdash-cms/plugin-cli/schemas/emdash-plugin.schema.json",

	"slug": "gallery",
	"publisher": "did:plc:abc123def456",

	"license": "MIT",
	"author": { "name": "Jane Doe", "url": "https://example.com" },
	"security": { "email": "[email protected]" },

	// Optional profile
	"name": "Gallery",
	"description": "Image gallery block for EmDash.",
	"keywords": ["gallery", "images"],
	"repo": "https://github.com/example/plugin-gallery",

	// Trust contract
	"capabilities": ["content:read"],
	"allowedHosts": [],
	"storage": {}
}

Identidade

CampoObrigatórioNotas
slugSimID seguro para URL dentro do namespace do publisher. /^[a-z][a-z0-9_-]*$/, máx. 64 caracteres.
publisherSimO DID ou handle da sua conta Atmosphere. Veja Fixação de publisher.
versionNãoSemver 2.0 sem metadados de build. Geralmente omita — veja abaixo.

slug e publisher juntos formam a identidade do pacote. EmDash deriva o identificador completo do pacote deles automaticamente.

version vive em package.json

O build reconcilia o version do manifesto com package.json#version:

  • Ambos definidos e iguais → ok.
  • Ambos definidos e diferentes → erro grave.
  • Um definido → esse valor vence.
  • Nenhum definido → erro grave.

O padrão recomendado para um plugin distribuído via npm é omitir version do manifesto e deixar package.json ser a única fonte da verdade (suas ferramentas de release já a incrementam lá). Plugins somente de registro sem package.json devem definir version no manifesto — não há outro lugar para ela.

Perfil

Estes alimentam a listagem do registro. license, um autor (author ou authors) e um contato de segurança (security ou securityContacts) são obrigatórios; o resto é opcional.

CampoObrigatórioNotas
licenseSimExpressão SPDX ("MIT", "Apache-2.0", "MIT OR Apache-2.0"). Usado na primeira publicação; o perfil existente vence em publicações posteriores.
author / authorsSimUm dos dois. author: { name, url?, email? } para um único autor; authors: [...] (≤ 32) para vários. Definir ambos é um erro.
security / securityContactsSimUm dos dois. Cada contato precisa de pelo menos email ou url. securityContacts: [...] (≤ 8) para vários. Definir ambos é um erro.
nameNãoNome de exibição. Padrão é o slug.
descriptionNãoMantenha curto (cerca de 140 caracteres). Valores longos podem ser truncados em listas.
keywordsNão≤ 5 entradas.
repoNãoURL https:// do repositório fonte.

Use a forma singular author / security a menos que você genuinamente tenha múltiplos — é o caso comum e o scaffold a emite.

Contrato de confiança

O contrato de confiança é capabilities, allowedHosts e storage. Todos os três são vazios por padrão, então um plugin que não precisa de privilégios extras pode omiti-los completamente.

{
	"capabilities": ["network:request", "content:read"],
	"allowedHosts": ["api.example.com", "*.cdn.example.com"],
	"storage": {
		"events": { "indexes": ["timestamp"] },
		"submissions": { "indexes": ["email"], "uniqueIndexes": ["token"] }
	}
}

Capabilities

Os nomes reconhecidos:

CapabilityConcede
content:read / content:writeLer / mutar conteúdo do site via ctx.
media:read / media:writeLer / escrever mídia.
users:readLer registros de usuários.
email:sendEnviar email via ctx.
network:requestHTTP de saída via ctx.http, restrito a allowedHosts.
network:request:unrestrictedHTTP de saída para qualquer host. Usado em vez de network:request.
hooks.email-transport:registerRegistrar um hook de transporte de email.
hooks.email-events:registerRegistrar hooks de ciclo de vida de email.
hooks.page-fragments:registerRegistrar um hook page:fragments (apenas nativo).

Duas regras entre campos que a CLI impõe (a verificação JSON-Schema do editor não — execute emdash-plugin validate):

  • network:request requer um allowedHosts não vazio. Se o plugin realmente precisa alcançar qualquer host, use network:request:unrestricted em vez disso.
  • network:request:unrestricted requer que allowedHosts esteja vazio — a capability irrestrita já concede todos os hosts, então uma lista a contradiz.

Padrões de host são nomes de host simples (sem esquema, caminho ou espaço em branco). Um *. inicial permite subdomínios: *.cdn.example.com.

Storage

Um mapa de nome de coleção → configuração de índice. Nomes de coleção seguem a mesma regra /^[a-z][a-z0-9_]*$/ (o runtime usa o nome como sufixo de tabela SQL). Índices são nomes de campos ou arrays compostos; uniqueIndexes também são consultáveis — não os liste também em indexes.

"storage": {
	"events": { "indexes": ["timestamp", ["collection", "timestamp"]] }
}

Superfície de administração

Opcional. Plugins em sandbox renderizam páginas de administração e widgets de painel através do Block Kit; o manifesto apenas declara onde eles aparecem. Omita a chave admin completamente se o plugin não tiver UI de administração.

"admin": {
	"pages": [{ "path": "/gallery", "label": "Gallery", "icon": "image" }],
	"widgets": [{ "id": "recent-uploads", "title": "Recent uploads", "size": "half" }]
}

Um plugin que declara admin.pages ou admin.widgets também deve servir uma rota admin em src/plugin.ts que renderiza o conteúdo do Block Kit — o schema não pode impor isso (nomes de rotas são sondados do fonte, não do manifesto), mas o runtime verifica.

Fixação de publisher

publisher fixa a identidade de publicação para que você não possa acidentalmente publicar um plugin sob a conta errada.

Na sua primeira publicação bem-sucedida, se o publisher do manifesto corresponder à sessão ativa, ele permanece como escrito. Se você fez scaffold com emdash-plugin init e o deixou em branco, a CLI escreve o DID da sessão ativa de volta no manifesto.

O exemplo a seguir mostra a linha que a CLI escreve, com o handle resolvido adicionado como comentário para legibilidade:

"publisher": "did:plc:abc123def456", // jane.example.com

Em cada publicação subsequente, a CLI resolve a sessão ativa e o publisher fixado para DIDs e os compara. Uma incompatibilidade falha imediatamente com MANIFEST_PUBLISHER_MISMATCH — não há flag de substituição. Resolva deliberadamente:

  • Sessão errada: emdash-plugin switch <did>, depois publique novamente.
  • Transferência genuína do plugin para um novo publisher: edite publisher no manifesto.

Validar sem publicar

emdash-plugin validate          # ./emdash-plugin.jsonc
emdash-plugin validate path/    # um diretório específico

Verificação de schema offline com diagnósticos estilo tsc file:line:column, incluindo as regras entre campos. Adequado para um hook de pré-commit ou etapa de CI. Chaves duplicadas e chaves desconhecidas são erros (o modo estrito captura erros de digitação "licens").

Flags da CLI ainda vencem

Flags explícitos (--license, --author-name, …) substituem valores do manifesto quando ambos estão definidos — útil para substituições de CI. --no-manifest pula o manifesto completamente (e avisa se um existe no caminho padrão, para que a história de segurança de fixação do publisher permaneça visível).

Próximo