JavaScript API 參考

本頁內容

EmDash 匯出用於查詢內容以及處理預覽、設定、選單、分類、小工具區域、區段和搜尋的函數。

內容查詢

EmDash 的查詢函數遵循 Astro 的 即時內容集合 模式,回傳 { entries, error }{ entry, error } 以優雅地處理錯誤。

getEmDashCollection()

從集合中取得所有項目。以下範例載入所有文章並檢查錯誤:

import { getEmDashCollection } from "emdash";

const { entries: posts, error } = await getEmDashCollection("posts");

if (error) {
	console.error("Failed to load posts:", error);
}

參數

參數類型說明
collectionstring集合 slug
optionsCollectionFilter可選的篩選器選項

選項

options 參數接受以下篩選器:

interface CollectionFilter {
	status?: "draft" | "published" | "archived";
	limit?: number;
	where?: Record<string, string | string[]>; // Filter by field or taxonomy
}

回傳值

該函數解析為 CollectionResult

interface CollectionResult<T> {
	entries: ContentEntry<T>[]; // Empty array if error or none found
	error?: Error; // Set if query failed
}

範例

以下範例按狀態和分類篩選、限制結果並處理錯誤:

// Get all published posts
const { entries: posts } = await getEmDashCollection("posts", {
	status: "published",
});

// Get latest 5 posts
const { entries: latest } = await getEmDashCollection("posts", {
	limit: 5,
	status: "published",
});

// Filter by taxonomy
const { entries: newsPosts } = await getEmDashCollection("posts", {
	status: "published",
	where: { category: "news" },
});

// Handle errors
const { entries, error } = await getEmDashCollection("posts");
if (error) {
	return new Response("Server error", { status: 500 });
}

getEmDashEntry()

透過 slug 或 ID 取得單一項目。以下範例載入一篇文章,並在缺少時重新導向:

import { getEmDashEntry } from "emdash";

const { entry: post, error } = await getEmDashEntry("posts", "my-post-slug");

if (!post) {
	return Astro.redirect("/404");
}

參數

參數類型說明
collectionstring集合 slug
slugOrIdstring項目 slug 或 ID
options{ locale?: string }可選。用於 slug 解析的語言環境

預覽模式會自動處理——中介軟體偵測 _preview 令牌並透過 AsyncLocalStorage 提供草稿內容。可選的 options 參數僅接受用於 slug 解析的 locale;預覽狀態不需要參數。

回傳值

該函數解析為 EntryResult

interface EntryResult<T> {
	entry: ContentEntry<T> | null; // null if not found
	error?: Error; // Set only for actual errors, not "not found"
	isPreview: boolean; // true if draft content is being served
}

範例

以下範例透過 slug 和 ID 取得、讀取預覽狀態並區分錯誤和「未找到」:

// Get by slug
const { entry: post } = await getEmDashEntry("posts", "hello-world");

// Get by ID
const { entry: post } = await getEmDashEntry("posts", "01HXK5MZSN0FVXT2Q3KPRT9M7D");

// Preview is automatic — isPreview is true when a valid _preview token is present
const { entry, isPreview, error } = await getEmDashEntry("posts", slug);

// Handle errors vs not-found
if (error) {
	return new Response("Server error", { status: 500 });
}
if (!entry) {
	return Astro.redirect("/404");
}

內容類型

ContentEntry

查詢函數以以下形式回傳項目:

interface ContentEntry<T = Record<string, unknown>> {
	id: string;
	data: T;
	edit: EditProxy; // Visual editing annotations
}

edit 代理提供視覺化編輯註解。將其展開到元素上以啟用內嵌編輯:{...entry.edit.title}。在生產環境中,這不會產生輸出。

data 物件包含所有內容欄位以及系統欄位:

  • id - 唯一識別碼
  • slug - URL 友善識別碼
  • status - “draft” | “published” | “archived”
  • createdAt - ISO 時間戳記
  • updatedAt - ISO 時間戳記
  • publishedAt - ISO 時間戳記或 null
  • 加上集合結構描述中定義的所有自訂欄位

預覽系統

generatePreviewToken()

為草稿內容產生預覽令牌。以下範例建立一個在一小時後過期的令牌:

import { generatePreviewToken } from "emdash";

const token = await generatePreviewToken({
	contentId: "posts:01HXK5MZSN...",
	secret: process.env.EMDASH_ADMIN_SECRET,
	expiresIn: 3600, // 1 hour
});

verifyPreviewToken()

驗證預覽令牌並讀取其有效負載:

import { verifyPreviewToken } from "emdash";

const result = await verifyPreviewToken({
	token,
	secret: process.env.EMDASH_ADMIN_SECRET,
});

if (result.valid) {
	const { cid, exp, iat } = result.payload;
	// cid is "collection:id" format, e.g. "posts:my-draft-post"
}

isPreviewRequest()

檢查請求是否包含預覽令牌,然後讀取它:

import { isPreviewRequest, getPreviewToken } from "emdash";

if (isPreviewRequest(Astro.url)) {
	const token = getPreviewToken(Astro.url);
	// Verify and show preview content
}

內容轉換器

在 Portable Text 和 ProseMirror 格式之間轉換:

import { prosemirrorToPortableText, portableTextToProsemirror } from "emdash";

// From ProseMirror (editor) to Portable Text (storage)
const portableText = prosemirrorToPortableText(prosemirrorDoc);

// From Portable Text to ProseMirror
const prosemirrorDoc = portableTextToProsemirror(portableText);

網站設定

使用 getSiteSettingsgetSiteSetting 讀取全站設定:

import { getSiteSettings, getSiteSetting } from "emdash";

// Get all settings
const settings = await getSiteSettings();

// Get single setting
const title = await getSiteSetting("title");

設定從執行階段 API 唯讀。使用管理員 API 更新它們。

選單

取得導覽選單並遍歷它們的項目,包括巢狀的子項:

import { getMenu, getMenus } from "emdash";

// Get all menus
const menus = await getMenus();

// Get specific menu with items
const primaryMenu = await getMenu("primary");

if (primaryMenu) {
	primaryMenu.items.forEach(item => {
		console.log(item.label, item.url);
		// Nested items for dropdowns
		item.children.forEach(child => console.log("  -", child.label));
	});
}

分類

取得分類術語、單一術語、項目的術語或按術語取得項目:

import { getTaxonomyTerms, getTerm, getEntryTerms, getEntriesByTerm } from "emdash";

// Get all terms for a taxonomy (tree structure for hierarchical)
const categories = await getTaxonomyTerms("category");

// Get single term
const news = await getTerm("category", "news");

// Get terms assigned to a content entry
const postCategories = await getEntryTerms("posts", "post-123", "category");

// Get entries with a specific term
const newsPosts = await getEntriesByTerm("posts", "category", "news");

小工具區域

取得小工具區域及其包含的小工具:

import { getWidgetArea, getWidgetAreas } from "emdash";

// Get all widget areas
const areas = await getWidgetAreas();

// Get specific widget area with widgets
const sidebar = await getWidgetArea("sidebar");

if (sidebar) {
	sidebar.widgets.forEach(widget => {
		console.log(widget.type, widget.title);
	});
}

區段

取得區段並對其進行篩選:

import { getSection, getSections } from "emdash";

// Get all sections (paginated)
const { items, nextCursor } = await getSections();

// Filter sections
const { items: themeSections } = await getSections({ source: "theme" });
const { items: results } = await getSections({ search: "newsletter" });

// Get a single section by slug
const cta = await getSection("newsletter-cta");

getSections(options?) 回傳 { items: Section[]; nextCursor?: string }。選項包括 source ("theme" | "user" | "import")、searchlimit(預設 50,最大 100)和 cursor

搜尋

跨集合執行全域搜尋。結果包括醒目提示摘錄:

import { search } from "emdash";

const results = await search("hello world", {
	collections: ["posts", "pages"],
	status: "published",
	limit: 20,
});

// search() resolves to { items, nextCursor? }
results.items.forEach(result => {
	console.log(result.title);
	console.log(result.snippet); // Contains <mark> tags
	console.log(result.score);
});

錯誤處理

EmDash 匯出用於處理特定失敗的錯誤類別。以下範例捕獲驗證和結構描述錯誤:

import {
  EmDashDatabaseError,
  EmDashValidationError,
  EmDashStorageError,
  SchemaError,
} from "emdash";

try {
  await repo.create({ ... });
} catch (error) {
  if (error instanceof EmDashValidationError) {
    console.error("Validation failed:", error.message);
  }
  if (error instanceof SchemaError) {
    console.error("Schema error:", error.code, error.details);
  }
}