Das Plugin-Manifest

Auf dieser Seite

Jedes sandboxed Plugin hat eine emdash-plugin.jsonc neben seiner package.json. Sie wird manuell bearbeitet und enthält die Identität des Plugins, seinen Vertrauensvertrag (Capabilities, Hosts, Storage) und die Profilfelder, die die Registry anzeigt. emdash-plugin init erstellt eine; die CLI liest ./emdash-plugin.jsonc automatisch für build, dev, validate, bundle und publish.

Die Datei ist JSONC: Kommentare und abschließende Kommas sind erlaubt.

Das folgende Beispiel zeigt ein vollständiges Manifest für ein Bildergalerie-Plugin:

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

Identität

FeldErforderlichHinweise
slugJaURL-sichere ID innerhalb des Publisher-Namespace. /^[a-z][a-z0-9_-]*$/, max. 64 Zeichen.
publisherJaDie DID oder Handle Ihres Atmosphere-Kontos. Siehe Publisher-Pinning.
versionNeinSemver 2.0 ohne Build-Metadaten. Normalerweise weglassen — siehe unten.

slug und publisher zusammen bilden die Identität des Pakets. EmDash leitet die vollständige Kennung des Pakets automatisch daraus ab.

version befindet sich in package.json

Der Build gleicht die version des Manifests mit package.json#version ab:

  • Beide gesetzt und gleich → in Ordnung.
  • Beide gesetzt und unterschiedlich → harter Fehler.
  • Eines gesetzt → dieser Wert gewinnt.
  • Keines gesetzt → harter Fehler.

Das empfohlene Muster für ein npm-verteiltes Plugin ist, version aus dem Manifest wegzulassen und package.json als einzige Quelle der Wahrheit zu verwenden (Ihr Release-Tooling erhöht sie dort bereits). Nur-Registry-Plugins ohne package.json müssen version im Manifest setzen — es gibt keinen anderen Ort dafür.

Profil

Diese speisen die Registry-Auflistung. license, ein Autor (author oder authors) und ein Sicherheitskontakt (security oder securityContacts) sind erforderlich; der Rest ist optional.

FeldErforderlichHinweise
licenseJaSPDX-Ausdruck ("MIT", "Apache-2.0", "MIT OR Apache-2.0"). Wird bei der ersten Veröffentlichung verwendet; das bestehende Profil gewinnt bei späteren Veröffentlichungen.
author / authorsJaEines von beiden. author: { name, url?, email? } für einen einzelnen Autor; authors: [...] (≤ 32) für mehrere. Beide zu setzen ist ein Fehler.
security / securityContactsJaEines von beiden. Jeder Kontakt benötigt mindestens email oder url. securityContacts: [...] (≤ 8) für mehrere. Beide zu setzen ist ein Fehler.
nameNeinAnzeigename. Standardmäßig der Slug.
descriptionNeinHalten Sie es kurz (etwa 140 Zeichen). Lange Werte können in Listen abgeschnitten werden.
keywordsNein≤ 5 Einträge.
repoNeinhttps://-URL des Quell-Repos.

Verwenden Sie die Singular-Form author / security, es sei denn, Sie haben wirklich mehrere — das ist der häufigste Fall und das Scaffold gibt sie aus.

Vertrauensvertrag

Der Vertrauensvertrag besteht aus capabilities, allowedHosts und storage. Alle drei sind standardmäßig leer, sodass ein Plugin, das keine zusätzlichen Berechtigungen benötigt, sie vollständig weglassen kann.

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

Capabilities

Die anerkannten Namen:

CapabilityGewährt
content:read / content:writeSite-Inhalte über ctx lesen / ändern.
media:read / media:writeMedien lesen / schreiben.
users:readBenutzerdatensätze lesen.
email:sendE-Mail über ctx senden.
network:requestAusgehende HTTP-Anfragen über ctx.http, beschränkt auf allowedHosts.
network:request:unrestrictedAusgehende HTTP-Anfragen an jeden Host. Wird anstelle von network:request verwendet.
hooks.email-transport:registerEinen E-Mail-Transport-Hook registrieren.
hooks.email-events:registerE-Mail-Lebenszyklus-Hooks registrieren.
hooks.page-fragments:registerEinen page:fragments-Hook registrieren (nur nativ).

Zwei feldübergreifende Regeln, die die CLI durchsetzt (die JSON-Schema-Prüfung des Editors nicht — führen Sie emdash-plugin validate aus):

  • network:request erfordert ein nicht-leeres allowedHosts. Wenn das Plugin wirklich jeden Host erreichen muss, verwenden Sie stattdessen network:request:unrestricted.
  • network:request:unrestricted erfordert, dass allowedHosts leer ist — die uneingeschränkte Capability gewährt bereits jeden Host, sodass eine Liste ihr widersprechen würde.

Host-Muster sind reine Hostnamen (kein Schema, Pfad oder Leerzeichen). Ein führendes *. erlaubt Subdomains: *.cdn.example.com.

Storage

Eine Zuordnung von Sammlungsname → Index-Konfiguration. Sammlungsnamen folgen der gleichen /^[a-z][a-z0-9_]*$/-Regel (die Laufzeit verwendet den Namen als SQL-Tabellen-Suffix). Indizes sind Feldnamen oder zusammengesetzte Arrays; uniqueIndexes sind auch abfragbar — listen Sie sie nicht auch in indexes auf.

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

Admin-Oberfläche

Optional. Sandboxed Plugins rendern Admin-Seiten und Dashboard-Widgets über Block Kit; das Manifest deklariert nur, wo sie erscheinen. Lassen Sie den admin-Schlüssel vollständig weg, wenn das Plugin keine Admin-UI hat.

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

Ein Plugin, das admin.pages oder admin.widgets deklariert, muss auch eine admin-Route in src/plugin.ts bereitstellen, die den Block Kit-Inhalt rendert — das Schema kann das nicht durchsetzen (Routennamen werden aus der Quelle, nicht aus dem Manifest, ermittelt), aber die Laufzeit prüft es.

Publisher-Pinning

publisher fixiert die Publishing-Identität, damit Sie nicht versehentlich ein Plugin unter dem falschen Konto veröffentlichen können.

Bei Ihrem ersten erfolgreichen Publish, wenn der publisher des Manifests mit der aktiven Sitzung übereinstimmt, bleibt er wie geschrieben. Wenn Sie mit emdash-plugin init ein Scaffold erstellt und es leer gelassen haben, schreibt die CLI die DID der aktiven Sitzung zurück ins Manifest.

Das folgende Beispiel zeigt die Zeile, die die CLI schreibt, mit dem aufgelösten Handle als Kommentar für bessere Lesbarkeit:

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

Bei jedem nachfolgenden Publish löst die CLI die aktive Sitzung und den fixierten publisher zu DIDs auf und vergleicht sie. Eine Nichtübereinstimmung schlägt sofort mit MANIFEST_PUBLISHER_MISMATCH fehl — es gibt kein Override-Flag. Lösen Sie es bewusst:

  • Falsche Sitzung: emdash-plugin switch <did>, dann erneut veröffentlichen.
  • Echte Übertragung des Plugins an einen neuen Publisher: publisher im Manifest bearbeiten.

Ohne Veröffentlichung validieren

emdash-plugin validate          # ./emdash-plugin.jsonc
emdash-plugin validate path/    # ein bestimmtes Verzeichnis

Offline-Schema-Prüfung mit tsc-artigen file:line:column-Diagnosen, einschließlich der feldübergreifenden Regeln. Geeignet für einen Pre-Commit-Hook oder CI-Schritt. Doppelte Schlüssel und unbekannte Schlüssel sind Fehler (Strict-Modus fängt "licens"-Tippfehler).

CLI-Flags gewinnen trotzdem

Explizite Flags (--license, --author-name, …) überschreiben Manifest-Werte, wenn beide gesetzt sind — nützlich für CI-Overrides. --no-manifest überspringt das Manifest vollständig (und warnt, wenn eines am Standardpfad existiert, sodass die Publisher-Pin-Sicherheitsgeschichte sichtbar bleibt).

Weiter