プラグインシステム概要

このページ

EmDash のプラグインシステムを使用すると、コアコードを変更することなく CMS を拡張できます。プラグインは、コンテンツライフサイクルイベントにフックし、独自のデータを保存し、管理者に設定を公開し、管理パネルにカスタム UI を追加できます。

設計哲学

EmDash プラグインには、サンドボックス化ネイティブの 2 つのタイプがあります。サンドボックス化されたプラグインは分離された V8 ワーカーで実行され、マーケットプレイスからワンクリックでインストールできます。ネイティブプラグインはインプロセスで実行され、コードで設定されます。

サンドボックス化されたプラグインを優先してください。 管理 UI からコードに触れることなく、または再デプロイすることなくインストール、更新、削除できます。ビルド時統合が必要な機能(React 管理ページ、Portable Text レンダリングコンポーネント、またはページフラグメント注入)が必要な場合にのみ、ネイティブプラグインを使用してください。

主要な原則:

  • サンドボックス優先 — サンドボックス用に設計し、必要な場合にのみネイティブモードを使用
  • 宣言的 — フック、ストレージ、およびルートは定義時に宣言され、動的に登録されない
  • 型安全 — 型付きコンテキストオブジェクトを使用した完全な TypeScript サポート
  • 能力ベース — プラグインは必要なものを宣言し、サンドボックスがそれを強制する

プラグインができること

イベントにフック

コンテンツ保存、メディアアップロード、プラグインライフサイクルイベントの前後にコードを実行します。

データを保存

データベース移行を書くことなく、インデックス付きコレクションにプラグイン固有のデータを永続化します。

設定を公開

設定スキーマを宣言し、設定用の自動生成された管理 UI を取得します。

管理ページを追加

React コンポーネントを使用してカスタム管理ページとダッシュボードウィジェットを作成します。

API ルートを作成

プラグインの管理 UI または外部統合のエンドポイントを公開します。

HTTP リクエストを実行

セキュリティのために宣言されたホスト制限で外部 API を呼び出します。

プラグインアーキテクチャ

すべてのプラグインは definePlugin() で作成されます:

import { definePlugin } from "emdash";

export default definePlugin({
	id: "my-plugin",
	version: "1.0.0",

	// プラグインがアクセスする必要がある API
	capabilities: ["read:content", "network:fetch"],

	// プラグインが HTTP リクエストを行うことができるホスト
	allowedHosts: ["api.example.com"],

	// 永続ストレージコレクション
	storage: {
		entries: {
			indexes: ["userId", "createdAt"],
		},
	},

	// イベントハンドラー
	hooks: {
		"content:afterSave": async (event, ctx) => {
			ctx.log.info("Content saved", { id: event.content.id });
		},
	},

	// REST API エンドポイント
	routes: {
		status: {
			handler: async (ctx) => ({ ok: true }),
		},
	},

	// 管理 UI 設定
	admin: {
		settingsSchema: {
			apiKey: { type: "secret", label: "API Key" },
		},
		pages: [{ path: "/dashboard", label: "Dashboard" }],
		widgets: [{ id: "status", size: "half" }],
	},
});

プラグインコンテキスト

すべてのフックとルートハンドラーは、アクセスできる PluginContext オブジェクトを受け取ります:

プロパティ説明可用性
ctx.storageプラグインのドキュメントコレクション常に(宣言されている場合)
ctx.kv設定と状態のキーバリューストア常に
ctx.contentサイトコンテンツの読み取り/書き込みread:content または write:content を使用
ctx.mediaメディアファイルの読み取り/書き込みread:media または write:media を使用
ctx.http外部リクエスト用の HTTP クライアントnetwork:fetch を使用
ctx.log構造化ロガー(debug、info、warn、error)常に
ctx.pluginプラグインメタデータ(id、version)常に
ctx.siteサイト情報:nameurllocale常に
ctx.url()パスから絶対 URL を生成常に
ctx.usersユーザー情報の読み取り:get()getByEmail()list()read:users を使用
ctx.cronタスクのスケジュール:schedule()cancel()list()常に
ctx.emailメール送信:send()email:send を使用 + プロバイダー設定

コンテキストの形状は、すべてのフックとルートで同じです。能力でゲートされたプロパティは、プラグインが必要な能力を宣言した場合にのみ存在します。

能力

能力は、プラグインコンテキストで使用可能な API を決定します:

能力アクセス権を付与
read:contentctx.content.get()ctx.content.list()
write:contentctx.content.create()ctx.content.update()ctx.content.delete()
read:mediactx.media.get()ctx.media.list()
write:mediactx.media.getUploadUrl()ctx.media.upload()ctx.media.delete()
network:fetchctx.http.fetch()allowedHosts に制限)
network:fetch:anyctx.http.fetch()(制限なし — ユーザー設定 URL 用)
read:usersctx.users.get()ctx.users.getByEmail()ctx.users.list()
email:sendctx.email.send()(プロバイダープラグインが必要)
email:provideemail:deliver 排他的フックを登録(トランスポートプロバイダー)
email:interceptemail:beforeSend / email:afterSend フックを登録
page:injectpage:metadata / page:fragments フックを登録

登録

Astro 設定でプラグインを登録します:

import { defineConfig } from "astro/config";
import { emdash } from "emdash/astro";
import seoPlugin from "@emdash-cms/plugin-seo";
import auditLogPlugin from "@emdash-cms/plugin-audit-log";

export default defineConfig({
	integrations: [
		emdash({
			plugins: [seoPlugin({ generateSitemap: true }), auditLogPlugin({ retentionDays: 90 })],
		}),
	],
});

プラグインはビルド時に解決されます。同じ優先度のフックの場合、順序が重要です — 配列内の早いプラグインが最初に実行されます。

実行モード

EmDash は 2 つのプラグイン実行モードをサポートしています:

モード説明プラットフォーム
サンドボックス化強制制限付きの分離された V8 ワーカーCloudflare のみ
ネイティブフルアクセス付きのインプロセス任意

サンドボックスモードでは、能力はランタイムレベルで強制されます — プラグインは宣言したもののみにアクセスできます。ネイティブモードでは、能力はアドバイザリであり、プラグインは完全なプロセスアクセス権を持ちます。

次のステップ

ネイティブ vs. サンドボックス

実行モードを比較し、プラグインに適したものを選択します。