Il manifesto del plugin

In questa pagina

Ogni plugin in sandbox ha un emdash-plugin.jsonc accanto al suo package.json. Viene modificato manualmente e contiene l’identità del plugin, il suo contratto di fiducia (capabilities, hosts, storage) e i campi del profilo che il registro mostra. emdash-plugin init ne crea uno; la CLI legge ./emdash-plugin.jsonc automaticamente per build, dev, validate, bundle e publish.

Il file è JSONC: i commenti e le virgole finali sono consentiti.

Il seguente esempio mostra un manifesto completo per un plugin galleria immagini:

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

Identità

CampoRichiestoNote
slugID sicuro per URL nello spazio dei nomi del publisher. /^[a-z][a-z0-9_-]*$/, max 64 caratteri.
publisherIl DID o handle del tuo account Atmosphere. Vedi Ancoraggio del publisher.
versionNoSemver 2.0 senza metadati di build. Solitamente omettilo — vedi sotto.

slug e publisher insieme formano l’identità del pacchetto. EmDash deriva l’identificatore completo del pacchetto automaticamente da essi.

version risiede in package.json

La build riconcilia la version del manifesto con package.json#version:

  • Entrambi impostati e uguali → va bene.
  • Entrambi impostati e diversi → errore critico.
  • Uno impostato → quel valore vince.
  • Nessuno impostato → errore critico.

Il pattern raccomandato per un plugin distribuito tramite npm è di omettere version dal manifesto e lasciare che package.json sia l’unica fonte di verità (i tuoi strumenti di release la incrementano già lì). I plugin solo per registro senza package.json devono impostare version nel manifesto — non c’è altro posto per essa.

Profilo

Questi alimentano l’elenco del registro. license, un autore (author o authors) e un contatto di sicurezza (security o securityContacts) sono richiesti; il resto è opzionale.

CampoRichiestoNote
licenseEspressione SPDX ("MIT", "Apache-2.0", "MIT OR Apache-2.0"). Usata alla prima pubblicazione; il profilo esistente vince nelle pubblicazioni successive.
author / authorsUno dei due. author: { name, url?, email? } per un singolo autore; authors: [...] (≤ 32) per diversi. Impostare entrambi è un errore.
security / securityContactsUno dei due. Ogni contatto necessita almeno di email o url. securityContacts: [...] (≤ 8) per diversi. Impostare entrambi è un errore.
nameNoNome visualizzato. Per impostazione predefinita è lo slug.
descriptionNoMantienilo breve (circa 140 caratteri). I valori lunghi potrebbero essere troncati negli elenchi.
keywordsNo≤ 5 voci.
repoNoURL https:// del repository sorgente.

Usa la forma singolare author / security a meno che tu non abbia davvero multipli — è il caso comune e lo scaffold la emette.

Contratto di fiducia

Il contratto di fiducia è capabilities, allowedHosts e storage. Tutti e tre sono vuoti per impostazione predefinita, quindi un plugin che non necessita privilegi aggiuntivi può ometterli completamente.

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

Capabilities

I nomi riconosciuti:

CapabilityConcede
content:read / content:writeLeggere / modificare i contenuti del sito tramite ctx.
media:read / media:writeLeggere / scrivere media.
users:readLeggere i record degli utenti.
email:sendInviare email tramite ctx.
network:requestHTTP in uscita tramite ctx.http, limitato a allowedHosts.
network:request:unrestrictedHTTP in uscita a qualsiasi host. Usato al posto di network:request.
hooks.email-transport:registerRegistrare un hook di trasporto email.
hooks.email-events:registerRegistrare hook del ciclo di vita email.
hooks.page-fragments:registerRegistrare un hook page:fragments (solo nativo).

Due regole inter-campo che la CLI impone (il controllo JSON-Schema dell’editor non lo fa — esegui emdash-plugin validate):

  • network:request richiede un allowedHosts non vuoto. Se il plugin deve davvero raggiungere qualsiasi host, usa network:request:unrestricted invece.
  • network:request:unrestricted richiede che allowedHosts sia vuoto — la capability senza restrizioni concede già ogni host, quindi un elenco la contraddirebbe.

I pattern host sono nomi host semplici (nessuno schema, percorso o spazio bianco). Un *. iniziale consente sottodomini: *.cdn.example.com.

Storage

Una mappa di nome collezione → configurazione indice. I nomi delle collezioni seguono la stessa regola /^[a-z][a-z0-9_]*$/ (il runtime usa il nome come suffisso della tabella SQL). Gli indici sono nomi di campo o array compositi; uniqueIndexes sono anch’essi interrogabili — non elencarli anche in indexes.

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

Superficie di amministrazione

Opzionale. I plugin in sandbox rendono pagine di amministrazione e widget della dashboard tramite Block Kit; il manifesto dichiara solo dove appaiono. Ometti completamente la chiave admin se il plugin non ha UI di amministrazione.

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

Un plugin che dichiara admin.pages o admin.widgets deve anche servire una route admin in src/plugin.ts che rende il contenuto Block Kit — lo schema non può imporre questo (i nomi delle route sono sondati dal codice sorgente, non dal manifesto), ma il runtime lo verifica.

Ancoraggio del publisher

publisher ancora l’identità di pubblicazione così non puoi accidentalmente pubblicare un plugin sotto l’account sbagliato.

Alla tua prima pubblicazione riuscita, se il publisher del manifesto corrisponde alla sessione attiva, rimane come scritto. Se hai creato uno scaffold con emdash-plugin init e l’hai lasciato vuoto, la CLI scrive il DID della sessione attiva di nuovo nel manifesto.

Il seguente esempio mostra la riga che la CLI scrive, con l’handle risolto aggiunto come commento per leggibilità:

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

Ad ogni pubblicazione successiva, la CLI risolve la sessione attiva e il publisher ancorato in DID e li confronta. Una discrepanza fallisce immediatamente con MANIFEST_PUBLISHER_MISMATCH — non c’è flag di override. Risolvilo deliberatamente:

  • Sessione sbagliata: emdash-plugin switch <did>, poi pubblica di nuovo.
  • Trasferimento genuino del plugin a un nuovo publisher: modifica publisher nel manifesto.

Validare senza pubblicare

emdash-plugin validate          # ./emdash-plugin.jsonc
emdash-plugin validate path/    # una directory specifica

Controllo schema offline con diagnostica in stile tsc file:line:column, incluse le regole inter-campo. Adatto per un hook pre-commit o passo CI. Le chiavi duplicate e chiavi sconosciute sono errori (la modalità strict cattura errori di battitura "licens").

I flag CLI vincono comunque

I flag espliciti (--license, --author-name, …) sovrascrivono i valori del manifesto quando entrambi sono impostati — utile per override CI. --no-manifest salta completamente il manifesto (e avverte se ne esiste uno al percorso predefinito, così la storia di sicurezza dell’ancoraggio del publisher rimane visibile).

Prossimo