Este é um tópico avançado para criar seu próprio software sobre o registro de plugins. Se você só quer instalar plugins em um site EmDash, não precisa de nada disto: ative 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 encapsula, 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 em qualquer lugar onde fetch esteja disponível: Node, Workers, o navegador ou um site Astro. Sites Astro podem usar @emdash-cms/registry-loader para expor os mesmos dados por meio de uma coleção de conteúdo live.
Instalar
O subcaminho discovery não traz dependências de autenticação nem de OAuth. Instale o cliente e fixe-o em uma versão exata:
npm install @emdash-cms/[email protected]
Listar e resolver pacotes
A página Astro a seguir lista todos os plugins de 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 um. Um DID e o slug do pacote continuam sendo a identidade estável do pacote quando um publicador muda de handle.
Um valor q exato 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 os pacotes desse publicador.
Usar o live loader do Astro
Instale @emdash-cms/registry-loader e, em seguida, 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 único pacote. Adicione includeLatestRelease: true ao filtro da coleção quando uma listagem precisar de dados de release, como ícones; isso custa uma requisição extra ao registro por pacote que tenha uma release publicada, e uma entrada cuja release não possa ser carregada em 3 segundos volta sem latestRelease. Passe a dica de cache retornada para Astro.cache.set() quando o site tiver um provedor de cache do Astro. As coleções live 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 detalhes do pacote busca um plugin pelo DID e pelo slug e, em seguida, busca a ú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 descoberta
O cliente expõe um método para cada consulta do agregador:
searchPackages({ q, capability?, limit?, cursor? })— busca de texto livre, opcionalmente filtrada para pacotes que declaram uma determinada categoria de acesso. Retorna{ packages, cursor? }.resolvePackage({ handle, slug })— resolve um pacote a partir de um handle e de um slug.getPackage({ did, slug })— busca um pacote pelo DID e pelo slug.listReleases({ did, package, limit?, cursor? })— releases em ordem decrescente de versão semântica, incluindo releases retiradas (yanked).getLatestRelease({ did, package })— a release mais alta não retirada 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 publicador. Trate uma contagem igual a um como primeira release somente quando
releaseHistoryComplete for true. Evidências ausentes ou incompletas não devem contornar uma política
de idade de release.
getPackageStatus() e resolvePackageStatus() encapsulam as 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 uma
listagem indexada, mas indisponível, de um pacote inexistente sem renderizar conteúdo de erro
controlado pelo publicador.
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 de uso. Dados de rótulo inválidos
falham de forma fechada (fail closed): withdrawn e malformed são ambos true.
Lidar com registros não confiáveis
O agregador é um índice não confiável que repassa registros que ele não criou, então o cliente valida cada um na fronteira. Duas regras decorrem disso:
- Os campos
profileereleasepodem sernull. Quando um registro repassado falha na validação, o cliente o expõe comonullem vez de fazer toda a chamada falhar, para que um registro malformado não deixe em branco uma página de busca. Sempre verifique se há valores nulos antes de lerpkg.profile?.nameoulatest.release?.artifacts.package. - Valide você mesmo os esquemas de URL antes de renderizar. A validação verifica a estrutura, não a segurança das URLs: um campo
uripode conter um esquemajavascript:. Aplique sua própria lista de permissõeshttp/httpsantes de colocar qualquer URL fornecida pelo registro em umhrefousrc.
Uma resposta que não seja 2xx lança ClientResponseError (reexportado do pacote), contendo .error, .description, .status e .headers. O agregador de referência retorna apenas revisões de CID exato aprovadas por todas as fontes de rótulos positivos exigidas. Um cabeçalho atproto-accept-labelers declara DIDs simples configurados para a identidade da requisição e do cache. O agregador valida a declaração, mas a política configurada nele continua sendo a que prevalece.
Filtrar por compatibilidade de host
Uma release pode declarar requisitos de ambiente (um intervalo de versões do EmDash ou do Astro) em seu bloco requires. O subcaminho @emdash-cms/registry-client/env os avalia, para que um diretório possa sinalizar as releases que não funcionarão em um determinado 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);
}
O que ler a seguir
- O registo de plugins — ative e use o registro em um site EmDash
- Empacotar e publicar — publique um plugin no registro