Interroger le registre

Sur cette page

Ceci est un sujet avancé pour construire votre propre logiciel contre le registre de plugins. Si vous voulez seulement installer des plugins sur un site EmDash, vous n’avez besoin d’aucun de ceci — activez le registre dans votre configuration et utilisez le tableau de bord d’administration.

Le côté découverte du registre est une API publique en lecture seule. Le paquet @emdash-cms/registry-client l’enveloppe, afin que vous puissiez construire un annuaire de plugins, une page de recherche ou un flux de releases en dehors d’EmDash. Le client s’exécute partout où fetch est disponible — Node, Workers, le navigateur ou un site Astro. Les sites Astro peuvent utiliser @emdash-cms/registry-loader pour exposer les mêmes données via une live content collection.

Installer

Le sous-chemin discovery ne porte aucune dépendance d’authentification ou OAuth. Installez le client et épinglez-le sur une version exacte :

npm install @emdash-cms/registry-client@0.6.0

Lister et résoudre les packages

La page Astro suivante liste chaque plugin d’un registre :

---
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>

Le handle vérifié actuel est présent lorsque l’agrégateur l’a résolu. Gardez la forme DID comme secours et redirigez-la vers l’URL du handle actuel lorsqu’une réponse ultérieure en fournit un. Un DID et le slug du package restent l’identité stable du package lorsqu’un éditeur change de handle.

Une valeur exacte de q peut être un handle, un DID, ou l’une des deux identités suivie de /slug. Par exemple, @example.com/my-gallery sélectionne un package et example.com renvoie les packages de cet éditeur.

Utiliser le live loader Astro

Installez @emdash-cms/registry-loader, puis enregistrez le loader dans src/live.config.ts :

import { registryLoader } from "@emdash-cms/registry-loader";
import { defineLiveCollection } from "astro:content";

export const collections = {
	plugins: defineLiveCollection({ loader: registryLoader() }),
};

Utilisez getLiveCollection("plugins", { q, limit }) pour des résultats bornés et getLiveEntry("plugins", { publisher, slug }) pour un package. Passez l’indice de cache renvoyé à Astro.cache.set() lorsque le site a un fournisseur de cache Astro. Les live collections Astro ne renvoient pas de métadonnées de pagination, utilisez donc DiscoveryClient.searchPackages() directement lorsque l’interface a besoin du curseur suivant.

Une page de détail de package récupère un plugin par son DID et son slug, puis récupère la dernière 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 };
}

Méthodes de découverte

Le client expose une méthode par requête d’agrégateur :

  • searchPackages({ q, capability?, limit?, cursor? }) — recherche en texte libre, éventuellement filtrée aux packages déclarant une catégorie d’accès donnée. Renvoie { packages, cursor? }.
  • resolvePackage({ handle, slug }) — résoudre un package à partir d’un handle et d’un slug.
  • getPackage({ did, slug }) — récupérer un package par son DID et son slug.
  • listReleases({ did, package, limit?, cursor? }) — releases en ordre décroissant de version sémantique, y compris les releases retirées.
  • getLatestRelease({ did, package }) — la release non retirée la plus élevée sélectionnée par l’agrégateur.

getPackage() et resolvePackage() peuvent renvoyer historicalReleaseCount et releaseHistoryComplete. Ces champs décrivent l’historique opérationnel retenu par l’agrégateur, pas des métadonnées signées par l’éditeur. Traitez un compte de un comme première release uniquement lorsque releaseHistoryComplete est true. Une évidence manquante ou incomplète ne doit pas contourner une politique d’âge de release.

getPackageStatus() et resolvePackageStatus() encapsulent leurs requêtes de package correspondantes et mappent la réponse sûre ListingUnavailable vers { status: "unavailable" }. Un résultat réussi a { status: "passed", value }. Utilisez ces méthodes dans une interface utilisateur qui doit distinguer une fiche indexée mais indisponible d’un package manquant sans rendre de contenu d’erreur contrôlé par l’éditeur.

Utilisez l’aide au retrait avant d’afficher ou de sélectionner une release :

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 est true lorsque les labels applicables retirent la release de l’usage. Des données de label invalides échouent en fermeture : withdrawn et malformed sont tous deux true.

Gérer les enregistrements non fiables

L’agrégateur est un index non fiable qui relaie des enregistrements qu’il n’a pas écrits, donc le client valide chacun à la frontière. Deux règles en découlent :

  • Les champs profile et release peuvent être null. Lorsqu’un enregistrement relayé échoue à la validation, le client le présente comme null plutôt que de faire échouer tout l’appel, de sorte qu’un enregistrement malformé ne vide pas une page de recherche. Vérifiez toujours null avant de lire pkg.profile?.name ou latest.release?.artifacts.package.
  • Validez vous-même les schémas d’URL avant le rendu. La validation vérifie la structure, pas la sécurité de l’URL — un champ uri peut porter un schéma javascript:. Appliquez votre propre liste d’autorisation http/https avant de mettre une URL fournie par le registre dans un href ou src.

Une réponse non 2xx lance ClientResponseError (réexporté du paquet), portant .error, .description, .status et .headers. L’agrégateur de référence ne renvoie que des révisions CID exactes approuvées par chaque source d’étiquette positive requise. Un en-tête atproto-accept-labelers déclare des DID configurés bare pour l’identité de requête et de cache. L’agrégateur valide la déclaration, mais sa politique configurée reste autoritaire.

Filtrer par compatibilité d’hôte

Une release peut déclarer des exigences d’environnement (une plage de version EmDash ou Astro) dans son bloc requires. Le sous-chemin @emdash-cms/registry-client/env les évalue, afin qu’un annuaire puisse signaler les releases qui ne s’exécuteront pas sur un hôte donné :

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);
}

Suite de lecture