Il manifesto del plugin

In questa pagina

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

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

L’esempio seguente 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]" },

	// Profilo opzionale
	"name": "Gallery",
	"description": "Blocco galleria immagini per EmDash.",
	"keywords": ["gallery", "images"],
	"repo": "https://github.com/example/plugin-gallery",

	// Contratto di fiducia
	"capabilities": ["content:read"],
	"allowedHosts": [],
	"storage": {}
}

Identità

CampoRichiestoNote
slugID sicuro per URL nello spazio dei nomi dell’editore. /^[a-z][a-z0-9_-]*$/, max 64 caratteri.
publisherIl DID o l’handle del tuo account Atmosphere. Vedi Blocco dell’editore.
versionNoSemver 2.0 senza metadati di build. Di solito ometti — vedi sotto.

slug e publisher insieme sono l’identità del pacchetto. EmDash ne ricava automaticamente l’identificatore completo.

version va in package.json

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

  • Entrambi impostati e uguali → ok.
  • Entrambi impostati e diversi → errore grave.
  • Uno impostato → quel valore vince.
  • Nessuno impostato → errore grave.

Il pattern raccomandato per un plugin distribuito via npm è omettere version dal manifesto e lasciare che package.json sia l’unica fonte di verità (il tuo tooling di rilascio lo incrementa già lì). I plugin solo nel registro senza package.json devono impostare version nel manifesto — non c’è altro posto dove metterlo.

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 più autori. Impostarli entrambi è un errore.
security / securityContactsUno dei due. Ogni contatto necessita almeno di email o url. securityContacts: [...] (≤ 8) per più contatti. Impostarli entrambi è un errore.
nameNoNome visualizzato. Per impostazione predefinita lo slug.
descriptionNoMantienilo breve (circa 140 caratteri). I valori lunghi possono essere troncati nelle liste.
keywordsNo≤ 5 voci.
repoNoURL https:// del repository sorgente.

Usa la forma singolare author / security a meno che tu non abbia realmente più di uno — è il caso comune e lo scaffold lo emette così.

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 di privilegi aggiuntivi può ometterli interamente.

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

Capacità

I nomi riconosciuti:

CapacitàConcede
content:read / content:writeLeggere / modificare contenuti del sito tramite ctx.
taxonomies:readLeggere definizioni di tassonomie e termini (sola lettura).
media:read / media:writeLeggere / scrivere media.
users:readLeggere record utenti.
email:sendInviare email tramite ctx.
network:requestHTTP in uscita tramite ctx.http, limitato a allowedHosts.
network:request:unrestrictedHTTP in uscita verso 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 tra campi che la CLI applica (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 invece network:request:unrestricted.
  • network:request:unrestricted richiede che allowedHosts sia vuoto — la capacità senza restrizioni concede già tutti gli host, quindi una lista sarebbe contraddittoria.

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

Archiviazione

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 anche interrogabili — non elencarli anche in indexes.

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

Superficie di amministrazione

Opzionale. I plugin sandboxed renderizzano pagine di amministrazione e widget del dashboard tramite Block Kit; il manifesto dichiara solo dove appaiono. Ometti interamente la chiave admin se il plugin non ha un’interfaccia di amministrazione.

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

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

Blocco dell’editore

publisher blocca l’identità di pubblicazione in modo che non si possa 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 lo scaffold con emdash-plugin init e lo hai lasciato vuoto, la CLI scrive il DID della sessione attiva nel manifesto.

L’esempio seguente 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 bloccato 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 editore: modifica publisher nel manifesto.

Validare senza pubblicare

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

Controllo schema offline con diagnostici stile tsc file:riga:colonna, incluse le regole tra campi. Adatto per un hook pre-commit o step CI. Le chiavi duplicate e le chiavi sconosciute sono errori (la modalità strict cattura errori di battitura come "licens").

I flag della 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 interamente il manifesto (e avvisa se ne esiste uno al percorso predefinito, in modo che la storia di sicurezza del publisher-pin rimanga visibile).

Prossimi passi