本教學建立一個記錄內容儲存事件並暴露小型健康檢查路由的沙箱外掛。你將用外掛 CLI 搭建套件、新增一個掛鉤與一條路由、向 EmDash 網站註冊外掛,並確認兩個處理常式都會執行。
若尚未選擇外掛格式,請先閱讀 選擇外掛格式。
先決條件
你需要:
- Node.js 與 pnpm;
- 已設定 沙箱執行器 的 EmDash 網站;以及
- 用於清單 publisher 欄位的 Atmosphere 帳戶 handle 或 DID。
搭建外掛
-
從將包含新專案的目錄執行外掛鷹架。
pnpm dlx @emdash-cms/plugin-cli init save-log命令會詢問 publisher、作者、安全性聯絡人與來源儲存庫,然後在建立以下結構前顯示專案摘要:
save-log/ ├── .agents/ │ └── skills -> ../skills ├── .claude/ │ ├── CLAUDE.md -> ../AGENTS.md │ └── skills -> ../skills ├── AGENTS.md ├── emdash-plugin.jsonc ├── package.json ├── pnpm-workspace.yaml ├── README.md ├── skills/ │ └── creating-plugins/SKILL.md ├── src/ │ └── plugin.ts ├── tests/ │ └── plugin.test.ts ├── tsconfig.json ├── vitest.config.ts └── .gitignore -
安裝產生套件的依賴。
cd save-log pnpm install
定義外掛的存取與儲存
emdash-plugin.jsonc 包含外掛身分、登錄庫資訊與信任契約。新增 content:read 能力,因為 content:afterSave 會將已儲存內容暴露給外掛。宣告 events 儲存集合,以便掛鉤可為每次儲存保留可查詢記錄。
以下清單包含本教學使用的欄位。保留鷹架產生的 publisher、author 與 security 值。
{
"$schema": "./node_modules/@emdash-cms/plugin-cli/schemas/emdash-plugin.schema.json",
"slug": "save-log",
"publisher": "did:plc:abc123def456",
"license": "MIT",
"author": { "name": "Jane Doe", "url": "https://example.com" },
"security": { "email": "security@example.com" },
"description": "Records content-save events.",
"capabilities": ["content:read"],
"allowedHosts": [],
"storage": {
"events": { "indexes": ["savedAt"] },
},
}
content:read 宣告告訴網站營運者掛鉤會接收已儲存內容,且當此外掛格式在行程內執行時是必要的。若集合缺失,存取 ctx.storage.events 會拋錯。清單參考 說明其餘欄位與驗證規則。
新增掛鉤與路由
將產生的 src/plugin.ts 取代為以下執行階段定義:
import type { SandboxedPlugin } from "emdash/plugin";
const plugin: SandboxedPlugin = {
hooks: {
"content:afterSave": {
handler: async (event, ctx) => {
const savedAt = new Date().toISOString();
const contentId = String(event.content.id);
await ctx.storage.events.put(`${savedAt}:${contentId}`, {
savedAt,
collection: event.collection,
contentId,
});
ctx.log.info("Content save recorded", {
collection: event.collection,
contentId,
});
},
},
},
routes: {
health: {
public: true,
handler: async (_routeCtx, ctx) => {
return { ok: true, plugin: ctx.plugin.id };
},
},
},
};
export default plugin;
src/plugin.ts 將定義指派給 SandboxedPlugin 類型的常數並預設匯出。註解在不為套件新增 EmDash 執行階段、也不產生套件管理員特定宣告路徑的情況下,為掛鉤與路由提供參數類型。
掛鉤處理常式接收 (event, ctx)。路由處理常式接收 (routeCtx, ctx)。health 路由是公開且唯讀的,因此無需管理工作階段即可檢查。公開路由面向網際網路;在暴露真實資料或變更之前,API 路由 說明身分驗證與瀏覽器 origin 規則。
更新產生的測試
鷹架測試透過 Worker Loader 傳輸主機呼叫原始 hello 路由。將其取代為 health 路由的測試:
import { afterEach, describe, expect, it } from "vitest";
import { createPluginTestHost, type PluginTestHost } from "@emdash-cms/plugin-test";
let host: PluginTestHost | undefined;
afterEach(async () => {
await host?.dispose();
host = undefined;
});
describe("health route", () => {
it("identifies the running plugin", async () => {
host = await createPluginTestHost();
const result = await host.invokeRoute("health");
expect(result).toEqual({ ok: true, plugin: "save-log" });
});
});
測試建置外掛並透過 Worker Loader 與 PluginBridge 呼叫路由。沙箱外掛測試指南 涵蓋掛鉤、內容夾具、儲存斷言以及本機 workerd 測試的限制。
驗證與建置
執行產生的測試、驗證清單並建置 npm 產物。
pnpm run validate
pnpm run typecheck
pnpm run test
pnpm run build
建置會建立:
- 包含掛鉤與路由程式碼的
dist/plugin.mjs; - 包含執行階段清單與發現的掛鉤與路由名稱的
dist/manifest.json;以及 - 網站匯入的預設匯出描述子
dist/index.mjs。
dist/ 是產生輸出。鷹架將其排除在 Git 之外,因為外掛建置會重新建立它。
註冊外掛
將本機套件安裝到 EmDash 網站。從網站目錄執行此命令;若專案不是兄弟目錄,請調整相對路徑。
pnpm add file:../save-log
在 astro.config.mjs 中匯入產生的預設匯出,並將其加入 sandboxed:
import { defineConfig } from "astro/config";
import emdash from "emdash/astro";
import saveLog from "save-log";
export default defineConfig({
integrations: [
emdash({
sandboxed: [saveLog],
sandboxRunner: "@emdash-cms/sandbox-workerd/sandbox",
}),
],
});
此範例使用 Node.js workerd 執行器。若網站使用 Cloudflare Workers 或其他受支援的設定,請保留網站已設定的執行器。
執行外掛
啟動兩個開發行程:
- 在外掛目錄執行
pnpm dev。來源或清單變更時,CLI 會重建外掛。 - 在網站目錄執行網站的開發命令。
在網站上開啟以下路由:
http://localhost:4321/_emdash/api/plugins/save-log/health
回應包含標準 API 信封與外掛回傳的值:
{
"success": true,
"data": { "ok": true, "plugin": "save-log" },
}
在 EmDash 管理中儲存一筆項目。網站日誌包含 Content save recorded,掛鉤向外掛的 events 集合寫入一項。