EmDash는 콘텐츠 조회, 미디어 관리, 데이터베이스 작업을 위한 함수를 export합니다.
Content Queries
EmDash의 쿼리 함수는 Astro의 live content collections 패턴을 따르며, 오류 처리를 위해 { 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);
}
Parameters
| Parameter | Type | Description |
|---|---|---|
collection | string | 컬렉션 slug |
options | CollectionFilter | 선택적 필터 옵션 |
Options
interface CollectionFilter {
status?: "draft" | "published" | "archived";
limit?: number;
where?: Record<string, string | string[]>; // Filter by field or taxonomy
}
Returns
interface CollectionResult<T> {
entries: ContentEntry<T>[]; // Empty array if error or none found
error?: Error; // Set if query failed
}
Examples
// 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");
}
Parameters
| Parameter | Type | Description |
|---|---|---|
collection | string | 컬렉션 slug |
slugOrId | string | 항목 slug 또는 ID |
미리보기 모드는 자동으로 처리됩니다. 미들웨어가 _preview 토큰을 감지하고 AsyncLocalStorage를 통해 초안 콘텐츠를 제공합니다. options 매개변수는 필요 없습니다.
Returns
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
}
Examples
// 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");
}
Content Types
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- 컬렉션 스키마에 정의된 모든 사용자 정의 필드
Database Functions
createDatabase()
데이터베이스 연결을 만듭니다.
import { createDatabase } from "emdash";
const db = createDatabase({ url: "file:./data.db" });
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()
마이그레이션 상태를 확인합니다.
import { createDatabase, getMigrationStatus } from "emdash";
const db = createDatabase({ url: "file:./data.db" });
const status = await getMigrationStatus(db);
// { applied: ["0001_core", ...], pending: [] }
Repositories
리포지토리를 통한 저수준 데이터 액세스.
ContentRepository
import { ContentRepository, createDatabase } from "emdash";
const db = createDatabase({ url: "file:./data.db" });
const repo = new ContentRepository(db);
// Find many
const { items, nextCursor } = await repo.findMany("posts", {
limit: 10,
where: { status: "published" },
});
// Find by ID
const post = await repo.findById("posts", "01HXK5MZSN...");
// Create
const newPost = await repo.create({
type: "posts",
slug: "new-post",
data: { title: "New Post", content: [] },
status: "draft",
});
// Update
const updated = await repo.update("posts", "01HXK5MZSN...", {
data: { title: "Updated Title" },
});
// Delete
await repo.delete("posts", "01HXK5MZSN...");
MediaRepository
import { MediaRepository, createDatabase } from "emdash";
const db = createDatabase({ url: "file:./data.db" });
const repo = new MediaRepository(db);
// List media
const { items } = await repo.findMany({ limit: 20 });
// Get by ID
const 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",
});
Schema Registry
프로그래밍 방식 스키마 관리.
import { SchemaRegistry, createDatabase } from "emdash";
const db = createDatabase({ url: "file:./data.db" });
const registry = new SchemaRegistry(db);
// List collections
const collections = await registry.listCollections();
// Get collection with fields
const postsSchema = await registry.getCollectionWithFields("posts");
// Create collection
await registry.createCollection({
slug: "products",
label: "Products",
labelSingular: "Product",
supports: ["drafts", "revisions"],
});
// Add field
await registry.createField("products", {
slug: "price",
label: "Price",
type: "number",
required: true,
});
Preview System
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.request)) {
const token = getPreviewToken(Astro.request);
// Verify and show preview content
}
Content Converters
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);
Site Settings
import { getSiteSettings, getSiteSetting } from "emdash";
// Get all settings
const settings = await getSiteSettings();
// Get single setting
const title = await getSiteSetting("siteTitle");
설정은 런타임 API에서 읽기 전용입니다. 업데이트는 admin API를 사용하세요.
Menus
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));
});
}
Taxonomies
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");
Widget Areas
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);
});
}
Sections
import { getSection, getSections, getSectionCategories } from "emdash";
// Get all sections
const sections = await getSections();
// Filter sections
const heroes = await getSections({ category: "hero" });
const themeSections = await getSections({ source: "theme" });
const results = await getSections({ search: "newsletter" });
// Get single section
const cta = await getSection("newsletter-cta");
// Get categories
const categories = await getSectionCategories();
Search
import { search, searchCollection } from "emdash";
// Global search across collections
const results = await search("hello world", {
collections: ["posts", "pages"],
status: "published",
limit: 20,
});
// Results include snippets with highlights
results.forEach(result => {
console.log(result.title);
console.log(result.snippet); // Contains <mark> tags
console.log(result.score);
});
// Collection-specific search
const posts = await searchCollection("posts", "typescript", {
limit: 10,
});
Error Handling
EmDash는 특정 실패를 위한 오류 클래스를 export합니다:
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);
}
}