これは、プラグインレジストリに対して独自のソフトウェアを構築するための高度なトピックです。EmDash サイトにプラグインをインストールするだけであれば、これは必要ありません。設定でレジストリを有効にして、管理ダッシュボードを使用してください。
レジストリのディスカバリ側は、公開された読み取り専用の API です。@emdash-cms/registry-client パッケージがこれをラップしているため、EmDash の外部でプラグインディレクトリ、検索ページ、リリースフィードを構築できます。このクライアントは、fetch が利用可能な場所ならどこでも実行できます(Node、Workers、ブラウザ、Astro サイト)。Astro サイトでは、@emdash-cms/registry-loader を使用して、同じデータをライブコンテンツコレクション経由で公開できます。
インストール
discovery サブパスには、認証や OAuth の依存関係はありません。クライアントをインストールして、正確なバージョンに固定します:
npm install @emdash-cms/registry-client@0.6.0
パッケージの一覧表示と解決
次の Astro ページは、レジストリ内のすべてのプラグインを一覧表示します:
---
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>
現在の検証済みハンドルは、アグリゲーターが解決した場合に存在します。DID 形式をフォールバックとして保持し、後のレスポンスで提供された場合は現在のハンドル URL にリダイレクトしてください。DID とパッケージスラッグは、パブリッシャーがハンドルを変更した場合でも、安定したパッケージ識別子として残ります。
正確な q 値は、ハンドル、DID、またはいずれかの識別子の後に /slug を続けたものです。例えば、@example.com/my-gallery は 1 つのパッケージを選択し、example.com はそのパブリッシャーからのパッケージを返します。
Astro ライブローダーの使用
@emdash-cms/registry-loader をインストールし、src/live.config.ts にローダーを登録します:
import { registryLoader } from "@emdash-cms/registry-loader";
import { defineLiveCollection } from "astro:content";
export const collections = {
plugins: defineLiveCollection({ loader: registryLoader() }),
};
境界のある結果には getLiveCollection("plugins", { q, limit }) を使用し、1 つのパッケージには getLiveEntry("plugins", { publisher, slug }) を使用します。サイトに Astro キャッシュプロバイダーがある場合は、返されたキャッシュヒントを Astro.cache.set() に渡します。Astro ライブコレクションはページネーションメタデータを返さないため、インターフェースが次のカーソルを必要とする場合は、DiscoveryClient.searchPackages() を直接使用してください。
パッケージ詳細ページは、DID とスラッグでプラグインを取得し、次に最新リリースを取得します:
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 };
}
ディスカバリメソッド
クライアントは、アグリゲーターのクエリごとに 1 つのメソッドを公開します:
searchPackages({ q, capability?, limit?, cursor? })— フリーテキスト検索。オプションで、指定されたアクセスカテゴリを宣言するパッケージにフィルタリングします。{ packages, cursor? }を返します。resolvePackage({ handle, slug })— ハンドルとスラッグからパッケージを解決します。getPackage({ did, slug })— DID とスラッグでパッケージを取得します。listReleases({ did, package, limit?, cursor? })— セマンティックバージョンの降順でリリースを取得します。取り下げられたリリースを含みます。getLatestRelease({ did, package })— アグリゲーターによって選択された、取り下げられていない最高のリリース。
getPackage() と resolvePackage() は、historicalReleaseCount と releaseHistoryComplete を返すことができます。これらのフィールドは、アグリゲーターが保持する運用履歴を記述するものであり、パブリッシャーが署名したメタデータではありません。releaseHistoryComplete が true の場合にのみ、カウントが 1 の場合を最初のリリースとして扱ってください。欠落または不完全な証拠は、リリース期間ポリシーをバイパスしてはなりません。
getPackageStatus() と resolvePackageStatus() は、対応するパッケージクエリをラップし、安全な ListingUnavailable レスポンスを { status: "unavailable" } にマップします。成功した結果には { status: "passed", value } があります。パブリッシャーが制御するエラーコンテンツをレンダリングせずに、インデックス化されているが利用できないリストと、欠落しているパッケージを区別する必要があるユーザーインターフェースで、これらのメソッドを使用してください。
リリースを表示または選択する前に、取り下げヘルパーを使用してください:
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 になります。無効なラベルデータは、フェイルクローズします:withdrawn と malformed の両方が true になります。
信頼できないレコードの処理
アグリゲーターは、自分が作成していないレコードを中継する信頼できないインデックスであるため、クライアントは境界で各レコードを検証します。そこから 2 つのルールが導かれます:
profileとreleaseフィールドはnullになる可能性があります。 中継されたレコードが検証に失敗すると、クライアントは呼び出し全体を失敗させるのではなく、それをnullとして表示するため、1 つの不正なレコードが検索ページを空白にすることはありません。pkg.profile?.nameまたはlatest.release?.artifacts.packageを読み取る前に、常に null チェックを行ってください。- レンダリングする前に、URL スキームを自分で検証してください。 検証は構造をチェックするものであり、URL の安全性ではありません。
uriフィールドにはjavascript:スキームが含まれる可能性があります。レジストリ提供の URL をhrefまたはsrcに配置する前に、独自のhttp/https許可リストを適用してください。
2xx 以外のレスポンスは、.error、.description、.status、.headers を持つ ClientResponseError(パッケージから再エクスポート)をスローします。リファレンスアグリゲーターは、必要なすべての正のラベルソースによって承認された正確な CID リビジョンのみを返します。atproto-accept-labelers ヘッダーは、リクエストとキャッシュの識別のために、設定された裸の DID を宣言します。アグリゲーターは宣言を検証しますが、設定されたポリシーが権威を持ち続けます。
ホスト互換性によるフィルタリング
リリースは、requires ブロックで環境要件(EmDash または Astro のバージョン範囲)を宣言できます。@emdash-cms/registry-client/env サブパスがこれらを評価するため、ディレクトリは特定のホストで実行されないリリースにフラグを立てることができます:
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");
// getLatestRelease() の結果を渡します。返される配列は、
// このホストでリリースが実行される場合は空です。
export function envMismatches(latest: ValidatedReleaseView) {
return checkEnvCompatibility(latest.release?.requires, host);
}
次に読むべきもの
- プラグインレジストリ — EmDash サイトでレジストリを有効化して使用する
- バンドルと公開 — レジストリにプラグインを公開する