O manifesto do plugin

Nesta página

Todo plugin sandboxed tem um emdash-plugin.jsonc ao lado do seu package.json. Ele é editado manualmente e contém a identidade do plugin, seu contrato de confiança (capacidades, hosts, armazenamento) e os campos de perfil que o registro exibe. emdash-plugin init gera um scaffold; 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]" },

	// Perfil opcional
	"name": "Gallery",
	"description": "Bloco de galeria de imagens para EmDash.",
	"keywords": ["gallery", "images"],
	"repo": "https://github.com/example/plugin-gallery",

	// Contrato de confiança
	"capabilities": ["content:read"],
	"allowedHosts": [],
	"storage": {}
}

Identidade

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

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

version fica em package.json

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

  • Ambos definidos e iguais → ok.
  • Ambos definidos e diferentes → erro grave.
  • Um definido → esse valor prevalece.
  • 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 de verdade (seu tooling de release já faz o bump lá). Plugins apenas no registro sem package.json devem definir version no manifesto — não há outro lugar onde colocá-lo.

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 restante é opcional.

CampoObrigatórioNotas
licenseSimExpressão SPDX ("MIT", "Apache-2.0", "MIT OR Apache-2.0"). Usada na primeira publicação; o perfil existente prevalece 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 emite assim.

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 inteiramente.

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

Capacidades

Os nomes reconhecidos:

CapacidadeConcede
content:read / content:writeLer / modificar conteúdo do site via ctx.
taxonomies:readLer definições de taxonomia e termos (somente leitura).
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 no lugar 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 (somente nativo).

Duas regras entre campos que a CLI aplica (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 capacidade irrestrita já concede todos os hosts, então uma lista seria contraditória.

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

Armazenamento

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 campo ou arrays compostos; uniqueIndexes também são consultáveis — não os liste adicionalmente em indexes.

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

Superfície de administração

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

"admin": {
	"pages": [{ "path": "/gallery", "label": "Galeria", "icon": "image" }],
	"widgets": [{ "id": "recent-uploads", "title": "Uploads recentes", "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 Block Kit — o schema não pode forçar isso (nomes de rota são sondados do código fonte, não do manifesto), mas o runtime verifica.

Fixação de publicador

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 corresponde à sessão ativa, ele permanece como escrito. Se você criou o scaffold com emdash-plugin init e 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 discrepância falha imediatamente com MANIFEST_PUBLISHER_MISMATCH — não há flag de override. Resolva deliberadamente:

  • Sessão errada: emdash-plugin switch <did>, depois publique novamente.
  • Transferência genuína do plugin para um novo publicador: 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 arquivo:linha:coluna, incluindo as regras entre campos. Adequado para um hook de pre-commit ou passo de CI. Chaves duplicadas e chaves desconhecidas são erros (modo estrito captura erros de digitação como "licens").

Flags da CLI sempre prevalecem

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

Próximo