Este es un tema avanzado para construir tu propio software contra el registro de plugins. Si solo quieres instalar plugins en un sitio EmDash, no necesitas nada de esto: habilita el registro en tu configuración y usa el panel de administración.
El lado de descubrimiento del registro es una API pública de solo lectura. El paquete @emdash-cms/registry-client la envuelve, de modo que puedes crear un directorio de plugins, una página de búsqueda o un feed de releases fuera de EmDash. El cliente se ejecuta donde haya fetch — Node, Workers, el navegador o un sitio Astro. Los sitios Astro pueden usar @emdash-cms/registry-loader para exponer los mismos datos a través de una live content collection.
Instalar
El subpath discovery no lleva dependencias de autenticación ni OAuth. Instala el cliente y fíjalo a una versión exacta:
npm install @emdash-cms/registry-client@0.6.0
Listar y resolver paquetes
La siguiente página Astro lista cada plugin de 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>
El handle verificado actual está presente cuando el agregador lo ha resuelto. Mantén la forma DID como respaldo y redirígela a la URL del handle actual cuando una respuesta posterior suministre uno. Un DID y el slug del paquete siguen siendo la identidad estable del paquete cuando un publisher cambia de handle.
Un valor exacto de q puede ser un handle, un DID, o cualquiera de las dos identidades seguido de /slug. Por ejemplo, @example.com/my-gallery selecciona un paquete y example.com devuelve paquetes de ese publisher.
Usar el live loader de Astro
Instala @emdash-cms/registry-loader y luego registra el loader en 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 }) para resultados limitados y getLiveEntry("plugins", { publisher, slug }) para un paquete. Pasa la pista de caché devuelta a Astro.cache.set() cuando el sitio tenga un proveedor de caché de Astro. Las live collections de Astro no devuelven metadatos de paginación, así que usa DiscoveryClient.searchPackages() directamente cuando la interfaz necesite el siguiente cursor.
Una página de detalle del paquete obtiene un plugin por su DID y slug, y luego obtiene la última 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étodos de descubrimiento
El cliente expone un método por consulta del agregador:
searchPackages({ q, capability?, limit?, cursor? })— búsqueda de texto libre, opcionalmente filtrada a paquetes que declaran una categoría de acceso dada. Devuelve{ packages, cursor? }.resolvePackage({ handle, slug })— resolver un paquete a partir de un handle y un slug.getPackage({ did, slug })— obtener un paquete por su DID y slug.listReleases({ did, package, limit?, cursor? })— releases en orden descendente de versión semántica, incluidas las releases retiradas.getLatestRelease({ did, package })— la release no retirada más alta seleccionada por el agregador.
getPackage() y resolvePackage() pueden devolver historicalReleaseCount y
releaseHistoryComplete. Estos campos describen el historial operativo retenido por el agregador,
no metadatos firmados por el publisher. Trata un recuento de uno como primera release solo cuando
releaseHistoryComplete sea true. La evidencia faltante o incompleta no debe eludir una política de antigüedad de
release.
getPackageStatus() y resolvePackageStatus() envuelven sus consultas de paquete correspondientes y
mapean la respuesta segura ListingUnavailable a { status: "unavailable" }. Un resultado correcto tiene
{ status: "passed", value }. Usa estos métodos en una interfaz de usuario que necesite distinguir un
listado indexado pero no disponible de un paquete ausente sin renderizar contenido de error controlado por el
publisher.
Usa el helper de retirada antes de mostrar o seleccionar 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 es true cuando las etiquetas aplicables quitan la release del uso. Los datos de etiqueta inválidos
fallan en cerrado: withdrawn y malformed son ambos true.
Manejar registros no confiables
El agregador es un índice no confiable que retransmite registros que no autoró, por lo que el cliente valida cada uno en el límite. De eso se derivan dos reglas:
- Los campos
profileyreleasepueden sernull. Cuando un registro retransmitido falla la validación, el cliente lo expone comonullen lugar de fallar toda la llamada, de modo que un registro malformado no vacíe una página de búsqueda. Comprueba siempre null antes de leerpkg.profile?.nameolatest.release?.artifacts.package. - Valida tú mismo los esquemas de URL antes de renderizar. La validación comprueba la estructura, no la seguridad de la URL: un campo
uripuede llevar un esquemajavascript:. Aplica tu propia lista de permitidoshttp/httpsantes de poner cualquier URL suministrada por el registro en unhrefosrc.
Una respuesta no 2xx lanza ClientResponseError (reexportado del paquete), con .error, .description, .status y .headers. El agregador de referencia solo devuelve revisiones de CID exacto aprobadas por cada fuente de etiqueta positiva requerida. Un encabezado atproto-accept-labelers declara DIDs configurados bare para la identidad de solicitud y caché. El agregador valida la declaración, pero su política configurada sigue siendo autoritativa.
Filtrar por compatibilidad de host
Una release puede declarar requisitos de entorno (un rango de versión de EmDash o Astro) en su bloque requires. El subpath @emdash-cms/registry-client/env los evalúa, de modo que un directorio puede marcar releases que no se ejecutarán en un host dado:
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);
}
Qué leer a continuación
- El registro de plugins — habilitar y usar el registro en un sitio EmDash
- Empaquetar y publicar — publicar un plugin en el registro