Este é um tópico avançado para construir seu próprio software contra o registro de plugins. Se você só quer instalar plugins em um site EmDash, não precisa de nada disso — habilite o registro na sua configuração e use o painel de administração.
O lado de descoberta do registro é uma API pública somente leitura. O pacote @emdash-cms/registry-client a envolve, para que você possa criar um diretório de plugins, uma página de busca ou um feed de releases fora do EmDash. O cliente roda onde houver fetch — Node, Workers, o navegador ou um site Astro. Sites Astro podem usar @emdash-cms/registry-loader para expor os mesmos dados por uma live content collection.
Instalar
O subcaminho discovery não carrega dependências de autenticação ou OAuth. Instale o cliente e fixe-o em uma versão exata:
npm install @emdash-cms/registry-client@0.6.0
Listar e resolver pacotes
A seguinte página Astro lista cada plugin em um 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>
O handle verificado atual está presente quando o agregador o resolveu. Mantenha a forma DID como fallback e redirecione-a para a URL do handle atual quando uma resposta posterior fornecer uma. Um DID e o slug do pacote permanecem a identidade estável do pacote quando um publisher muda de handle.
Um valor exato de q pode ser um handle, um DID, ou qualquer uma das duas identidades seguida de /slug. Por exemplo, @example.com/my-gallery seleciona um pacote e example.com retorna pacotes desse publisher.
Usar o live loader do Astro
Instale @emdash-cms/registry-loader e então registre o loader em src/live.config.ts:
import { registryLoader } from "@emdash-cms/registry-loader";
import { defineLiveCollection } from "astro:content";
export const collections = {
plugins: defineLiveCollection({ loader: registryLoader() }),
};
Use getLiveCollection("plugins", { q, limit }) para resultados limitados e getLiveEntry("plugins", { publisher, slug }) para um pacote. Passe a dica de cache retornada a Astro.cache.set() quando o site tiver um provedor de cache Astro. Live collections do Astro não retornam metadados de paginação, então use DiscoveryClient.searchPackages() diretamente quando a interface precisar do próximo cursor.
Uma página de detalhe do pacote busca um plugin pelo DID e slug, e então busca a release mais recente:
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 descoberta
O cliente expõe um método por consulta do agregador:
searchPackages({ q, capability?, limit?, cursor? })— busca de texto livre, opcionalmente filtrada a pacotes que declaram uma categoria de acesso dada. Retorna{ packages, cursor? }.resolvePackage({ handle, slug })— resolver um pacote a partir de um handle e um slug.getPackage({ did, slug })— buscar um pacote pelo DID e slug.listReleases({ did, package, limit?, cursor? })— releases em ordem decrescente de versão semântica, incluindo releases retiradas.getLatestRelease({ did, package })— a release não retirada mais alta selecionada pelo agregador.
getPackage() e resolvePackage() podem retornar historicalReleaseCount e
releaseHistoryComplete. Esses campos descrevem o histórico operacional retido pelo agregador,
não metadados assinados pelo publisher. Trate uma contagem de um como primeira release somente quando
releaseHistoryComplete for true. Evidência ausente ou incompleta não deve contornar uma política de idade de
release.
getPackageStatus() e resolvePackageStatus() envolvem suas consultas de pacote correspondentes e
mapeiam a resposta segura ListingUnavailable para { status: "unavailable" }. Um resultado bem-sucedido tem
{ status: "passed", value }. Use esses métodos em uma interface de usuário que precise distinguir um
listagem indexada mas indisponível de um pacote ausente sem renderizar conteúdo de erro controlado pelo
publisher.
Use o helper de retirada antes de mostrar ou selecionar uma 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 os rótulos aplicáveis removem a release do uso. Dados de rótulo inválidos
falham em fechamento: withdrawn e malformed são ambos true.
Lidar com registros não confiáveis
O agregador é um índice não confiável que retransmite registros que não autorou, então o cliente valida cada um na fronteira. Duas regras seguem disso:
- Os campos
profileereleasepodem sernull. Quando um registro retransmitido falha na validação, o cliente o apresenta comonullem vez de falhar toda a chamada, de modo que um registro malformado não esvazie uma página de busca. Sempre verifique null antes de lerpkg.profile?.nameoulatest.release?.artifacts.package. - Valide você mesmo os esquemas de URL antes de renderizar. A validação verifica estrutura, não segurança de URL — um campo
uripode carregar um esquemajavascript:. Aplique sua própria lista de permissãohttp/httpsantes de colocar qualquer URL fornecida pelo registro em umhrefousrc.
Uma resposta não 2xx lança ClientResponseError (reexportado do pacote), carregando .error, .description, .status e .headers. O agregador de referência retorna apenas revisões de CID exato aprovadas por cada fonte de rótulo positivo exigida. Um cabeçalho atproto-accept-labelers declara DIDs configurados bare para identidade de solicitação e cache. O agregador valida a declaração, mas sua política configurada permanece autoritativa.
Filtrar por compatibilidade de host
Uma release pode declarar requisitos de ambiente (um intervalo de versão EmDash ou Astro) em seu bloco requires. O subcaminho @emdash-cms/registry-client/env os avalia, para que um diretório possa marcar releases que não rodarão em um 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);
}
O que ler a seguir
- O registro de plugins — habilitar e usar o registro em um site EmDash
- Empacotar e publicar — publicar um plugin no registro