Le manifeste du plugin

Sur cette page

Chaque plugin sandboxé a un emdash-plugin.jsonc à côté de son package.json. Il est édité manuellement et contient l’identité du plugin, son contrat de confiance (capacités, hôtes, stockage) et les champs de profil que le registre affiche. emdash-plugin init en génère un ; la CLI lit ./emdash-plugin.jsonc automatiquement pour build, dev, validate, bundle et publish.

Le fichier est en JSONC : les commentaires et les virgules finales sont autorisés.

L’exemple suivant montre un manifeste complet pour un plugin de galerie d’images :

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

	// Profil optionnel
	"name": "Gallery",
	"description": "Bloc galerie d'images pour EmDash.",
	"keywords": ["gallery", "images"],
	"repo": "https://github.com/example/plugin-gallery",

	// Contrat de confiance
	"capabilities": ["content:read"],
	"allowedHosts": [],
	"storage": {}
}

Identité

ChampRequisNotes
slugOuiID sûr pour les URL dans l’espace de noms de l’éditeur. /^[a-z][a-z0-9_-]*$/, max 64 caractères.
publisherOuiLe DID ou handle de votre compte Atmosphere. Voir Épinglage d’éditeur.
versionNonSemver 2.0 sans métadonnées de build. Habituellement omis — voir ci-dessous.

slug et publisher ensemble constituent l’identité du package. EmDash en dérive automatiquement l’identifiant complet du package.

version appartient à package.json

Le build réconcilie la version du manifeste avec package.json#version :

  • Les deux définis et égaux → ok.
  • Les deux définis et différents → erreur fatale.
  • Un seul défini → cette valeur l’emporte.
  • Aucun défini → erreur fatale.

Le schéma recommandé pour un plugin distribué via npm est d’omettre version du manifeste et de laisser package.json être la source unique de vérité (votre outillage de release l’incrémente déjà là). Les plugins uniquement dans le registre sans package.json doivent définir version dans le manifeste — il n’y a nulle part ailleurs où le mettre.

Profil

Ces éléments alimentent la fiche du registre. license, un auteur (author ou authors) et un contact de sécurité (security ou securityContacts) sont requis ; le reste est optionnel.

ChampRequisNotes
licenseOuiExpression SPDX ("MIT", "Apache-2.0", "MIT OR Apache-2.0"). Utilisé lors de la première publication ; le profil existant l’emporte lors des publications ultérieures.
author / authorsOuiL’un des deux. author: { name, url?, email? } pour un seul auteur ; authors: [...] (≤ 32) pour plusieurs. Définir les deux est une erreur.
security / securityContactsOuiL’un des deux. Chaque contact nécessite au moins email ou url. securityContacts: [...] (≤ 8) pour plusieurs. Définir les deux est une erreur.
nameNonNom d’affichage. Par défaut le slug.
descriptionNonRestez bref (environ 140 caractères). Les valeurs longues peuvent être tronquées dans les listes.
keywordsNon≤ 5 entrées.
repoNonURL https:// du dépôt source.

Utilisez la forme singulière author / security sauf si vous avez réellement plusieurs — c’est le cas courant et le scaffold l’émet ainsi.

Contrat de confiance

Le contrat de confiance est capabilities, allowedHosts et storage. Les trois sont vides par défaut, donc un plugin qui n’a pas besoin de privilèges supplémentaires peut les omettre entièrement.

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

Capacités

Les noms reconnus :

CapacitéAccorde
content:read / content:writeLire / modifier le contenu du site via ctx.
taxonomies:readLire les définitions de taxonomies et les termes (lecture seule).
media:read / media:writeLire / écrire des médias.
users:readLire les enregistrements utilisateurs.
email:sendEnvoyer des emails via ctx.
network:requestHTTP sortant via ctx.http, restreint à allowedHosts.
network:request:unrestrictedHTTP sortant vers n’importe quel hôte. Utilisé à la place de network:request.
hooks.email-transport:registerEnregistrer un hook de transport email.
hooks.email-events:registerEnregistrer des hooks de cycle de vie email.
hooks.page-fragments:registerEnregistrer un hook page:fragments (natif uniquement).

Deux règles inter-champs que la CLI applique (la vérification JSON-Schema de l’éditeur ne le fait pas — exécutez emdash-plugin validate) :

  • network:request nécessite un allowedHosts non vide. Si le plugin doit vraiment atteindre n’importe quel hôte, utilisez plutôt network:request:unrestricted.
  • network:request:unrestricted nécessite que allowedHosts soit vide — la capacité non restreinte accorde déjà tous les hôtes, donc une liste serait contradictoire.

Les motifs d’hôte sont des noms d’hôte nus (pas de schéma, chemin ou espace). Un *. initial autorise les sous-domaines : *.cdn.example.com.

Stockage

Une correspondance nom de collection → configuration d’index. Les noms de collection suivent la même règle /^[a-z][a-z0-9_]*$/ (le runtime utilise le nom comme suffixe de table SQL). Les index sont des noms de champ ou des tableaux composites ; uniqueIndexes sont aussi interrogeables — ne les listez pas en plus dans indexes.

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

Surface d’administration

Optionnel. Les plugins sandboxés affichent des pages d’administration et des widgets de tableau de bord via Block Kit ; le manifeste déclare uniquement où ils apparaissent. Omettez entièrement la clé admin si le plugin n’a pas d’interface d’administration.

"admin": {
	"pages": [{ "path": "/gallery", "label": "Galerie", "icon": "image" }],
	"widgets": [{ "id": "recent-uploads", "title": "Téléchargements récents", "size": "half" }]
}

Un plugin qui déclare admin.pages ou admin.widgets doit aussi servir une route admin dans src/plugin.ts qui rend le contenu Block Kit — le schéma ne peut pas l’imposer (les noms de route sont sondés depuis le code source, pas le manifeste), mais le runtime le vérifie.

Épinglage d’éditeur

publisher épingle l’identité de publication pour que vous ne puissiez pas accidentellement publier un plugin sous le mauvais compte.

Lors de votre première publication réussie, si le publisher du manifeste correspond à la session active, il reste tel qu’écrit. Si vous avez créé le scaffold avec emdash-plugin init et l’avez laissé vide, la CLI écrit le DID de la session active dans le manifeste.

L’exemple suivant montre la ligne que la CLI écrit, avec le handle résolu ajouté en commentaire pour la lisibilité :

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

Lors de chaque publication ultérieure, la CLI résout la session active et le publisher épinglé en DIDs et les compare. Une discordance échoue immédiatement avec MANIFEST_PUBLISHER_MISMATCH — il n’y a pas de flag d’override. Résolvez-le délibérément :

  • Mauvaise session : emdash-plugin switch <did>, puis publiez à nouveau.
  • Transfert réel du plugin à un nouvel éditeur : éditez publisher dans le manifeste.

Valider sans publier

emdash-plugin validate          # ./emdash-plugin.jsonc
emdash-plugin validate path/    # un répertoire spécifique

Vérification de schéma hors ligne avec des diagnostics style tsc fichier:ligne:colonne, incluant les règles inter-champs. Convient pour un hook de pre-commit ou une étape CI. Les clés dupliquées et les clés inconnues sont des erreurs (le mode strict détecte les fautes de frappe comme "licens").

Les flags CLI l’emportent toujours

Les flags explicites (--license, --author-name, …) remplacent les valeurs du manifeste quand les deux sont définis — utile pour les overrides CI. --no-manifest ignore entièrement le manifeste (et avertit s’il en existe un au chemin par défaut, pour que l’histoire de sécurité du publisher-pin reste visible).

Suivant