JavaScript API 参考
EmDash 导出了用于查询内容、管理媒体和处理数据库的函数。
EmDash 的查询函数遵循 Astro 的 实时内容集合 模式,返回 { entries, error } 或 { entry, error } 以实现优雅的错误处理。
getEmDashCollection()
Section titled “getEmDashCollection()”获取集合中的所有条目。
import { getEmDashCollection } from "emdash";
const { entries: posts, error } = await getEmDashCollection("posts");
if (error) { console.error("Failed to load posts:", error);}| 参数名 | 类型 | 描述 |
|---|---|---|
collection | string | 集合标识符 |
options | CollectionFilter | 可选的筛选选项 |
interface CollectionFilter { status?: "draft" | "published" | "archived"; limit?: number; where?: Record<string, string | string[]>; // Filter by field or taxonomy}interface CollectionResult<T> { entries: ContentEntry<T>[]; // Empty array if error or none found error?: Error; // Set if query failed}// Get all published postsconst { entries: posts } = await getEmDashCollection("posts", { status: "published",});
// Get latest 5 postsconst { entries: latest } = await getEmDashCollection("posts", { limit: 5, status: "published",});
// Filter by taxonomyconst { entries: newsPosts } = await getEmDashCollection("posts", { status: "published", where: { category: "news" },});
// Handle errorsconst { entries, error } = await getEmDashCollection("posts");if (error) { return new Response("Server error", { status: 500 });}getEmDashEntry()
Section titled “getEmDashEntry()”通过 slug 或 ID 获取单个条目。
import { getEmDashEntry } from "emdash";
const { entry: post, error } = await getEmDashEntry("posts", "my-post-slug");
if (!post) { return Astro.redirect("/404");}| 参数名 | 类型 | 描述 |
|---|---|---|
collection | string | 集合标识符 |
slugOrId | string | 条目的 slug 或 ID |
预览模式会自动处理 —— 中间件会检测 _preview 令牌并通过 AsyncLocalStorage 提供草稿内容。不需要 options 参数。
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}// Get by slugconst { entry: post } = await getEmDashEntry("posts", "hello-world");
// Get by IDconst { entry: post } = await getEmDashEntry("posts", "01HXK5MZSN0FVXT2Q3KPRT9M7D");
// Preview is automatic — isPreview is true when a valid _preview token is presentconst { entry, isPreview, error } = await getEmDashEntry("posts", slug);
// Handle errors vs not-foundif (error) { return new Response("Server error", { status: 500 });}if (!entry) { return Astro.redirect("/404");}ContentEntry
Section titled “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- 加上您在集合模式中定义的所有自定义字段
createDatabase()
Section titled “createDatabase()”创建数据库连接。
import { createDatabase } from "emdash";
const db = createDatabase({ url: "file:./data.db" });runMigrations()
Section titled “runMigrations()”运行待处理的数据库迁移。
import { createDatabase, runMigrations } from "emdash";
const db = createDatabase({ url: "file:./data.db" });const { applied } = await runMigrations(db);console.log(`Applied ${applied.length} migrations`);getMigrationStatus()
Section titled “getMigrationStatus()”检查迁移状态。
import { createDatabase, getMigrationStatus } from "emdash";
const db = createDatabase({ url: "file:./data.db" });const status = await getMigrationStatus(db);// { applied: ["0001_core", ...], pending: [] }通过存储库进行底层数据访问。
ContentRepository
Section titled “ContentRepository”import { ContentRepository, createDatabase } from "emdash";
const db = createDatabase({ url: "file:./data.db" });const repo = new ContentRepository(db);
// Find manyconst { items, nextCursor } = await repo.findMany("posts", { limit: 10, where: { status: "published" },});
// Find by IDconst post = await repo.findById("posts", "01HXK5MZSN...");
// Createconst newPost = await repo.create({ type: "posts", slug: "new-post", data: { title: "New Post", content: [] }, status: "draft",});
// Updateconst updated = await repo.update("posts", "01HXK5MZSN...", { data: { title: "Updated Title" },});
// Deleteawait repo.delete("posts", "01HXK5MZSN...");MediaRepository
Section titled “MediaRepository”import { MediaRepository, createDatabase } from "emdash";
const db = createDatabase({ url: "file:./data.db" });const repo = new MediaRepository(db);
// List mediaconst { items } = await repo.findMany({ limit: 20 });
// Get by IDconst media = await repo.findById("01HXK5MZSN...");
// Create (after upload)const newMedia = await repo.create({ filename: "photo.jpg", mimeType: "image/jpeg", size: 12345, storageKey: "uploads/photo.jpg",});编程式模式管理。
import { SchemaRegistry, createDatabase } from "emdash";
const db = createDatabase({ url: "file:./data.db" });const registry = new SchemaRegistry(db);
// List collectionsconst collections = await registry.listCollections();
// Get collection with fieldsconst postsSchema = await registry.getCollectionWithFields("posts");
// Create collectionawait registry.createCollection({ slug: "products", label: "Products", labelSingular: "Product", supports: ["drafts", "revisions"],});
// Add fieldawait registry.createField("products", { slug: "price", label: "Price", type: "number", required: true,});generatePreviewToken()
Section titled “generatePreviewToken()”为草稿内容生成预览令牌。
import { generatePreviewToken } from "emdash";
const token = await generatePreviewToken({ contentId: "posts:01HXK5MZSN...", secret: process.env.EMDASH_ADMIN_SECRET, expiresIn: 3600, // 1 hour});verifyPreviewToken()
Section titled “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()
Section titled “isPreviewRequest()”检查请求是否包含预览令牌。
import { isPreviewRequest, getPreviewToken } from "emdash";
if (isPreviewRequest(Astro.request)) { const token = getPreviewToken(Astro.request); // 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 ProseMirrorconst prosemirrorDoc = portableTextToProsemirror(portableText);import { getSiteSettings, getSiteSetting } from "emdash";
// Get all settingsconst settings = await getSiteSettings();
// Get single settingconst title = await getSiteSetting("siteTitle");设置从运行时 API 是只读的。请使用管理 API 来更新它们。
import { getMenu, getMenus } from "emdash";
// Get all menusconst menus = await getMenus();
// Get specific menu with itemsconst 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 termconst news = await getTerm("category", "news");
// Get terms assigned to a content entryconst postCategories = await getEntryTerms("posts", "post-123", "category");
// Get entries with a specific termconst newsPosts = await getEntriesByTerm("posts", "category", "news");import { getWidgetArea, getWidgetAreas } from "emdash";
// Get all widget areasconst areas = await getWidgetAreas();
// Get specific widget area with widgetsconst sidebar = await getWidgetArea("sidebar");
if (sidebar) { sidebar.widgets.forEach(widget => { console.log(widget.type, widget.title); });}import { getSection, getSections, getSectionCategories } from "emdash";
// Get all sectionsconst sections = await getSections();
// Filter sectionsconst heroes = await getSections({ category: "hero" });const themeSections = await getSections({ source: "theme" });const results = await getSections({ search: "newsletter" });
// Get single sectionconst cta = await getSection("newsletter-cta");
// Get categoriesconst categories = await getSectionCategories();import { search, searchCollection } from "emdash";
// Global search across collectionsconst results = await search("hello world", { collections: ["posts", "pages"], status: "published", limit: 20,});
// Results include snippets with highlightsresults.forEach(result => { console.log(result.title); console.log(result.snippet); // Contains <mark> tags console.log(result.score);});
// Collection-specific searchconst posts = await searchCollection("posts", "typescript", { limit: 10,});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); }}