El manifiesto del plugin

En esta página

Cada plugin en sandbox tiene un emdash-plugin.jsonc junto a su package.json. Se edita manualmente y contiene la identidad del plugin, su contrato de confianza (capabilities, hosts, storage) y los campos de perfil que muestra el registro. emdash-plugin init crea uno; el CLI lee ./emdash-plugin.jsonc automáticamente para build, dev, validate, bundle y publish.

El archivo es JSONC: se permiten comentarios y comas finales.

El siguiente ejemplo muestra un manifiesto completo para un plugin de galería de imágenes:

{
	"$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": "security@example.com" },

	// 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": {}
}

Identidad

CampoRequeridoNotas
slugID segura para URL dentro del namespace del publisher. /^[a-z][a-z0-9_-]*$/, máx. 64 caracteres.
publisherEl DID o handle de tu cuenta Atmosphere. Ver Anclaje de publisher.
versionNoSemver 2.0 sin metadatos de build. Normalmente omítelo — ver abajo.

slug y publisher juntos forman la identidad del paquete. EmDash deriva el identificador completo del paquete automáticamente de ellos.

version vive en package.json

El build reconcilia la version del manifiesto con package.json#version:

  • Ambos establecidos e iguales → correcto.
  • Ambos establecidos y diferentes → error grave.
  • Uno establecido → ese valor gana.
  • Ninguno establecido → error grave.

El patrón recomendado para un plugin distribuido por npm es omitir version del manifiesto y dejar que package.json sea la única fuente de verdad (tus herramientas de release ya la incrementan allí). Los plugins solo de registro sin package.json deben establecer version en el manifiesto — no hay otro lugar para ella.

Perfil

Estos alimentan el listado del registro. license, un autor (author o authors) y un contacto de seguridad (security o securityContacts) son requeridos; el resto es opcional.

CampoRequeridoNotas
licenseExpresión SPDX ("MIT", "Apache-2.0", "MIT OR Apache-2.0"). Se usa en la primera publicación; el perfil existente gana en publicaciones posteriores.
author / authorsUno de los dos. author: { name, url?, email? } para un solo autor; authors: [...] (≤ 32) para varios. Establecer ambos es un error.
security / securityContactsUno de los dos. Cada contacto necesita al menos email o url. securityContacts: [...] (≤ 8) para varios. Establecer ambos es un error.
nameNoNombre para mostrar. Por defecto es el slug.
descriptionNoManténlo corto (alrededor de 140 caracteres). Los valores largos pueden truncarse en listas.
keywordsNo≤ 5 entradas.
repoNoURL https:// del repositorio fuente.

Usa la forma singular author / security a menos que genuinamente tengas múltiples — es el caso común y el scaffold la emite.

Contrato de confianza

El contrato de confianza es capabilities, allowedHosts y storage. Los tres por defecto están vacíos, por lo que un plugin que no necesita privilegios adicionales puede omitirlos por completo.

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

Capabilities

Los nombres reconocidos:

CapabilityOtorga
content:read / content:writeLeer / mutar contenido del sitio vía ctx.
media:read / media:writeLeer / escribir medios.
users:readLeer registros de usuarios.
email:sendEnviar email vía ctx.
network:requestHTTP saliente vía ctx.http, restringido a allowedHosts.
network:request:unrestrictedHTTP saliente a cualquier host. Se usa en lugar de network:request.
hooks.email-transport:registerRegistrar un hook de transporte de email.
hooks.email-events:registerRegistrar hooks de ciclo de vida de email.
hooks.page-fragments:registerRegistrar un hook page:fragments (solo nativo).

Dos reglas entre campos que el CLI hace cumplir (la verificación JSON-Schema del editor no — ejecuta emdash-plugin validate):

  • network:request requiere un allowedHosts no vacío. Si el plugin realmente debe alcanzar cualquier host, usa network:request:unrestricted en su lugar.
  • network:request:unrestricted requiere que allowedHosts esté vacío — la capability sin restricciones ya otorga cada host, por lo que una lista la contradiría.

Los patrones de host son nombres de host simples (sin esquema, ruta o espacios en blanco). Un *. inicial permite subdominios: *.cdn.example.com.

Storage

Un mapa de nombre de colección → configuración de índice. Los nombres de colección siguen la misma regla /^[a-z][a-z0-9_]*$/ (el runtime usa el nombre como sufijo de tabla SQL). Los índices son nombres de campo o arrays compuestos; uniqueIndexes también son consultables — no los listes también en indexes.

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

Superficie de administración

Opcional. Los plugins en sandbox renderizan páginas de administración y widgets de panel a través de Block Kit; el manifiesto solo declara dónde aparecen. Omite la clave admin por completo si el plugin no tiene UI de administración.

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

Un plugin que declara admin.pages o admin.widgets también debe servir una ruta admin en src/plugin.ts que renderice el contenido de Block Kit — el schema no puede hacer cumplir eso (los nombres de ruta se sondean desde el código fuente, no del manifiesto), pero el runtime lo verifica.

Anclaje de publisher

publisher ancla la identidad de publicación para que no puedas publicar accidentalmente un plugin bajo la cuenta incorrecta.

En tu primera publicación exitosa, si el publisher del manifiesto coincide con la sesión activa, permanece como está escrito. Si creaste un scaffold con emdash-plugin init y lo dejaste en blanco, el CLI escribe el DID de la sesión activa de vuelta en el manifiesto.

El siguiente ejemplo muestra la línea que escribe el CLI, con el handle resuelto agregado como comentario para legibilidad:

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

En cada publicación subsiguiente, el CLI resuelve la sesión activa y el publisher anclado a DIDs y los compara. Una discrepancia falla inmediatamente con MANIFEST_PUBLISHER_MISMATCH — no hay flag de anulación. Resuélvelo deliberadamente:

  • Sesión incorrecta: emdash-plugin switch <did>, luego publica nuevamente.
  • Transferencia genuina del plugin a un nuevo publisher: edita publisher en el manifiesto.

Validar sin publicar

emdash-plugin validate          # ./emdash-plugin.jsonc
emdash-plugin validate path/    # un directorio específico

Verificación de schema offline con diagnósticos estilo tsc file:line:column, incluyendo las reglas entre campos. Adecuado para un hook pre-commit o paso de CI. Las claves duplicadas y claves desconocidas son errores (el modo estricto atrapa errores tipográficos de "licens").

Los flags del CLI aún ganan

Los flags explícitos (--license, --author-name, …) anulan los valores del manifiesto cuando ambos están establecidos — útil para anulaciones de CI. --no-manifest omite el manifiesto por completo (y advierte si existe uno en la ruta predeterminada, para que la historia de seguridad del anclaje de publisher permanezca visible).

Siguiente