Consultar o registro

Nesta página

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 profile e release podem ser null. Quando um registro repassado falha na validação, o cliente o expõe como null em 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 ler pkg.profile?.name ou latest.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 uri pode conter um esquema javascript:. Aplique sua própria lista de permissões http/https antes de colocar qualquer URL fornecida pelo registro em um href ou src.

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