Dies ist ein fortgeschrittenes Thema für den Bau eigener Software gegen das Plugin-Registry. Wenn Sie nur Plugins auf einer EmDash-Site installieren möchten, brauchen Sie nichts davon – aktivieren Sie das Registry in Ihrer Konfiguration und nutzen Sie das Admin-Dashboard.
Die Discovery-Seite des Registrys ist eine öffentliche, schreibgeschützte API. Das Paket @emdash-cms/registry-client umhüllt sie, sodass Sie außerhalb von EmDash ein Plugin-Verzeichnis, eine Suchseite oder einen Release-Feed bauen können. Der Client läuft überall, wo fetch verfügbar ist – Node, Workers, im Browser oder auf einer Astro-Site. Astro-Sites können @emdash-cms/registry-loader verwenden, um dieselben Daten über eine Live-Content-Collection bereitzustellen.
Installieren
Der Unterpfad discovery bringt keine Abhängigkeiten für Authentifizierung oder OAuth mit. Installieren Sie den Client und fixieren Sie ihn auf eine exakte Version:
npm install @emdash-cms/[email protected]
Packages listen und auflösen
Die folgende Astro-Seite listet jedes Plugin in einem Registry auf:
---
import { DiscoveryClient } from "@emdash-cms/registry-client/discovery";
const discovery = new DiscoveryClient({
aggregatorUrl: "https://registry.emdashcms.com",
});
const { packages } = await discovery.searchPackages({ q: "", limit: 50 });
---
<ul>
{
packages.map((pkg) => (
<li>
<a href={`/plugins/${pkg.handle ? `@${pkg.handle}` : pkg.did}/${pkg.slug}`}>
{pkg.profile?.name ?? pkg.slug}
</a>
{pkg.latestVersion && <span>v{pkg.latestVersion}</span>}
<p>{pkg.profile?.description}</p>
</li>
))
}
</ul>
Der aktuell verifizierte Handle ist vorhanden, sobald der Aggregator ihn aufgelöst hat. Behalten Sie die DID-Form als Fallback bei und leiten Sie sie auf die aktuelle Handle-URL um, sobald eine spätere Antwort einen Handle liefert. Eine DID und der Package-Slug bleiben die stabile Package-Identität, wenn ein Publisher seinen Handle ändert.
Ein exakter q-Wert kann ein Handle, eine DID oder eine der beiden Identitäten gefolgt von /slug sein. Zum Beispiel wählt @example.com/my-gallery ein einzelnes Package aus, und example.com liefert Packages dieses Publishers.
Den Astro Live Loader verwenden
Installieren Sie @emdash-cms/registry-loader und registrieren Sie den Loader anschließend in src/live.config.ts:
import { registryLoader } from "@emdash-cms/registry-loader";
import { defineLiveCollection } from "astro:content";
export const collections = {
plugins: defineLiveCollection({ loader: registryLoader() }),
};
Verwenden Sie getLiveCollection("plugins", { q, limit }) für begrenzte Ergebnisse und getLiveEntry("plugins", { publisher, slug }) für ein einzelnes Package. Fügen Sie dem Collection-Filter includeLatestRelease: true hinzu, wenn eine Auflistung Release-Daten wie Icons benötigt; das kostet eine zusätzliche Registry-Anfrage pro Package mit veröffentlichtem Release, und ein Eintrag, dessen Release nicht innerhalb von 3 Sekunden geladen werden kann, kommt ohne latestRelease zurück. Übergeben Sie den zurückgegebenen Cache-Hinweis an Astro.cache.set(), wenn die Site einen Astro-Cache-Provider hat. Astro-Live-Collections liefern keine Paginierungs-Metadaten; verwenden Sie daher DiscoveryClient.searchPackages() direkt, wenn die Oberfläche den nächsten Cursor benötigt.
Eine Package-Detailseite ruft ein Plugin über seine DID und seinen Slug ab und dann das neueste Release:
import { DiscoveryClient } from "@emdash-cms/registry-client/discovery";
const discovery = new DiscoveryClient({
aggregatorUrl: "https://registry.emdashcms.com",
});
export async function getPlugin(did: string, slug: string) {
const pkg = await discovery.getPackage({ did, slug });
const latest = await discovery.getLatestRelease({
did: pkg.did,
package: pkg.slug,
});
return { pkg, latest };
}
Discovery-Methoden
Der Client stellt pro Aggregator-Abfrage eine Methode bereit:
searchPackages({ q, capability?, limit?, cursor? })– Freitextsuche, optional gefiltert auf Packages, die eine bestimmte Zugriffskategorie deklarieren. Gibt{ packages, cursor? }zurück.resolvePackage({ handle, slug })– löst ein Package anhand eines Handles und eines Slugs auf.getPackage({ did, slug })– ruft ein Package anhand seiner DID und seines Slugs ab.listReleases({ did, package, limit?, cursor? })– Releases in absteigender Reihenfolge der semantischen Version, einschließlich zurückgezogener (yanked) Releases.getLatestRelease({ did, package })– das höchste nicht zurückgezogene Release, das der Aggregator auswählt.
getPackage() und resolvePackage() können historicalReleaseCount und
releaseHistoryComplete zurückgeben. Diese Felder beschreiben den vom Aggregator vorgehaltenen
Betriebsverlauf, nicht vom Publisher signierte Metadaten. Behandeln Sie einen Zählerwert von eins nur dann als erstes Release, wenn
releaseHistoryComplete den Wert true hat. Fehlende oder unvollständige Nachweise dürfen eine Richtlinie zum Release-Alter
nicht umgehen.
getPackageStatus() und resolvePackageStatus() umhüllen die entsprechenden Package-Abfragen und
bilden die sichere Antwort ListingUnavailable auf { status: "unavailable" } ab. Ein erfolgreiches Ergebnis hat
{ status: "passed", value }. Verwenden Sie diese Methoden in einer Benutzeroberfläche, die einen
indizierten, aber nicht verfügbaren Eintrag von einem fehlenden Package unterscheiden muss, ohne vom Publisher kontrollierte
Fehlerinhalte zu rendern.
Verwenden Sie den Withdrawal-Helper, bevor Sie ein Release anzeigen oder auswählen:
import {
DiscoveryClient,
type ValidatedReleaseView,
} from "@emdash-cms/registry-client/discovery";
import { evaluateRegistryReleaseWithdrawal } from "@emdash-cms/registry-client/withdrawal";
const discovery = new DiscoveryClient({
aggregatorUrl: "https://registry.emdashcms.com",
});
export function canShowRelease(release: ValidatedReleaseView) {
const result = evaluateRegistryReleaseWithdrawal(release, discovery.labelerPolicy);
return release.release !== null && !result.withdrawn;
}
withdrawn ist true, wenn die anwendbaren Labels das Release aus der Nutzung entfernen. Ungültige Label-Daten
führen zu einem geschlossenen Fehlschlag (fail closed): withdrawn und malformed sind dann beide true.
Nicht vertrauenswürdige Records behandeln
Der Aggregator ist ein nicht vertrauenswürdiger Index, der Records weiterreicht, die er nicht selbst verfasst hat, daher validiert der Client jeden einzelnen an der Grenze. Daraus folgen zwei Regeln:
- Die Felder
profileundreleasekönnennullsein. Wenn ein weitergereichter Record die Validierung nicht besteht, gibt der Client ihn alsnullzurück, statt den gesamten Aufruf scheitern zu lassen, sodass ein fehlerhafter Record nicht eine ganze Suchseite leert. Prüfen Sie immer auf null, bevor Siepkg.profile?.nameoderlatest.release?.artifacts.packagelesen. - Validieren Sie URL-Schemata selbst, bevor Sie rendern. Die Validierung prüft die Struktur, nicht die Sicherheit von URLs – ein
uri-Feld kann einjavascript:-Schema enthalten. Wenden Sie Ihre eigenehttp/https-Allowlist an, bevor Sie eine vom Registry gelieferte URL in einhrefodersrceinsetzen.
Eine Nicht-2xx-Antwort wirft ClientResponseError (aus dem Paket erneut exportiert) mit .error, .description, .status und .headers. Der Referenz-Aggregator liefert nur Revisionen mit exakter CID, die von jeder erforderlichen Quelle positiver Labels freigegeben wurden. Ein atproto-accept-labelers-Header deklariert reine konfigurierte DIDs für Anfrage- und Cache-Identität. Der Aggregator validiert die Deklaration, aber seine konfigurierte Richtlinie bleibt maßgeblich.
Nach Host-Kompatibilität filtern
Ein Release kann in seinem requires-Block Umgebungsanforderungen deklarieren (einen EmDash- oder Astro-Versionsbereich). Der Unterpfad @emdash-cms/registry-client/env wertet sie aus, sodass ein Verzeichnis Releases markieren kann, die auf einem bestimmten Host nicht laufen:
import { checkEnvCompatibility, hostEnvFromVersions } from "@emdash-cms/registry-client/env";
import type { ValidatedReleaseView } from "@emdash-cms/registry-client/discovery";
const host = hostEnvFromVersions("0.37.0", "7.0.0");
// Pass a getLatestRelease() result. The returned array is empty when the
// release runs on this host.
export function envMismatches(latest: ValidatedReleaseView) {
return checkEnvCompatibility(latest.release?.requires, host);
}
Was als Nächstes lesen
- Das Plugin-Registry – das Registry auf einer EmDash-Site aktivieren und verwenden
- Bündeln und Veröffentlichen – ein Plugin im Registry veröffentlichen