Interrogare il registro

In questa pagina

Questo è un argomento avanzato per costruire il proprio software contro il registro dei plugin. Se vuoi solo installare plugin su un sito EmDash, non ti serve nulla di questo — abilita il registro nella configurazione e usa il pannello di amministrazione.

Il lato discovery del registro è un’API pubblica in sola lettura. Il pacchetto @emdash-cms/registry-client la avvolge, così puoi creare una directory di plugin, una pagina di ricerca o un feed di release fuori da EmDash. Il client gira ovunque sia disponibile fetch — Node, Workers, il browser o un sito Astro. I siti Astro possono usare @emdash-cms/registry-loader per esporre gli stessi dati tramite una live content collection.

Installare

Il sottopercorso discovery non porta dipendenze di autenticazione o OAuth. Installa il client e fissalo a una versione esatta:

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

Elencare e risolvere i package

La seguente pagina Astro elenca ogni plugin in un registro:

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

L’handle verificato attuale è presente quando l’aggregatore lo ha risolto. Conserva la forma DID come fallback e reindirizzala all’URL dell’handle attuale quando una risposta successiva ne fornisce uno. Un DID e lo slug del package restano l’identità stabile del package quando un publisher cambia handle.

Un valore esatto di q può essere un handle, un DID, o una delle due identità seguita da /slug. Ad esempio, @example.com/my-gallery seleziona un package e example.com restituisce i package di quel publisher.

Usare il live loader Astro

Installa @emdash-cms/registry-loader, poi registra il loader in src/live.config.ts:

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

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

Usa getLiveCollection("plugins", { q, limit }) per risultati limitati e getLiveEntry("plugins", { publisher, slug }) per un package. Passa l’hint di cache restituito a Astro.cache.set() quando il sito ha un provider di cache Astro. Le live collection di Astro non restituiscono metadati di paginazione, quindi usa DiscoveryClient.searchPackages() direttamente quando l’interfaccia ha bisogno del cursore successivo.

Una pagina di dettaglio del package recupera un plugin per DID e slug, poi recupera l’ultima 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 };
}

Metodi di discovery

Il client espone un metodo per query dell’aggregatore:

  • searchPackages({ q, capability?, limit?, cursor? }) — ricerca a testo libero, opzionalmente filtrata ai package che dichiarano una data categoria di accesso. Restituisce { packages, cursor? }.
  • resolvePackage({ handle, slug }) — risolvere un package da handle e slug.
  • getPackage({ did, slug }) — recuperare un package per DID e slug.
  • listReleases({ did, package, limit?, cursor? }) — release in ordine discendente di versione semantica, incluse le release ritirate.
  • getLatestRelease({ did, package }) — la release non ritirata più alta selezionata dall’aggregatore.

getPackage() e resolvePackage() possono restituire historicalReleaseCount e releaseHistoryComplete. Questi campi descrivono la cronologia operativa trattenuta dall’aggregatore, non metadati firmati dal publisher. Tratta un conteggio di uno come prima release solo quando releaseHistoryComplete è true. Evidenza mancante o incompleta non deve aggirare una policy di età della release.

getPackageStatus() e resolvePackageStatus() avvolgono le rispettive query di package e mappano la risposta sicura ListingUnavailable a { status: "unavailable" }. Un risultato riuscito ha { status: "passed", value }. Usa questi metodi in un’interfaccia utente che deve distinguere un listing indicizzato ma non disponibile da un package mancante senza renderizzare contenuti di errore controllati dal publisher.

Usa l’helper di ritiro prima di mostrare o selezionare una 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 è true quando le etichette applicabili rimuovono la release dall’uso. Dati di etichetta non validi falliscono in chiusura: withdrawn e malformed sono entrambi true.

Gestire i record non attendibili

L’aggregatore è un indice non attendibile che ritrasmette record che non ha scritto, quindi il client valida ciascuno al confine. Ne seguono due regole:

  • I campi profile e release possono essere null. Quando un record ritrasmesso fallisce la validazione, il client lo espone come null invece di far fallire l’intera chiamata, così un record malformato non svuota una pagina di ricerca. Controlla sempre null prima di leggere pkg.profile?.name o latest.release?.artifacts.package.
  • Valida tu stesso gli schemi URL prima del rendering. La validazione controlla la struttura, non la sicurezza dell’URL — un campo uri può portare uno schema javascript:. Applica la tua allow-list http/https prima di mettere qualsiasi URL fornita dal registro in un href o src.

Una risposta non 2xx lancia ClientResponseError (riesportato dal pacchetto), con .error, .description, .status e .headers. L’aggregatore di riferimento restituisce solo revisioni CID esatte approvate da ogni fonte di etichetta positiva richiesta. Un header atproto-accept-labelers dichiara DID configurati bare per l’identità di richiesta e cache. L’aggregatore valida la dichiarazione, ma la sua policy configurata resta autorevole.

Filtrare per compatibilità dell’host

Una release può dichiarare requisiti di ambiente (un intervallo di versione EmDash o Astro) nel blocco requires. Il sottopercorso @emdash-cms/registry-client/env li valuta, così una directory può segnalare release che non gireranno su un dato host:

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

Cosa leggere dopo