* fix(tests): remove explicit beforeAll timeouts that override global hookTimeout Integration tests passed 60s timeouts to beforeAll, overriding the 120s hookTimeout in vitest.smoke.config.ts. On CI the dev server startup can consume the full 60s, leaving no time for setup + seeding. Also bumps createTestServer's default waitForServer timeout from 60s to 90s, leaving 30s margin within the 120s hook budget. * fix(tests): don't remove shared node_modules symlink during cleanup Multiple integration test suites run concurrently and share the fixture/node_modules symlink. When the suite that created it finishes first, its cleanup deletes the symlink, causing other suites to fail with MODULE_NOT_FOUND when their server process tries to resolve astro. The symlink is gitignored so it's safe to leave in place.
emdash
The core EmDash CMS package - an Astro-native, agent-portable reimplementation of WordPress.
Installation
npm install emdash
Features
- Content Management - Collections, fields, Live Collections integration
- Media Library - Upload via signed URLs, S3-compatible storage
- Full-Text Search - FTS5 with Porter stemming, per-collection config
- Navigation Menus - Hierarchical menus with URL resolution
- Taxonomies - Categories, tags, custom taxonomies
- Widget Areas - Content, menu, and component widgets
- Sections - Reusable content blocks
- Plugin System - Hooks, storage, settings, admin pages
- WordPress Import - WXR, REST API, WordPress.com
Quick Start
// astro.config.mjs
import { defineConfig } from "astro/config";
import emdash, { local } from "emdash/astro";
import { sqlite } from "emdash/db";
export default defineConfig({
integrations: [
emdash({
database: sqlite({ url: "file:./data.db" }),
storage: local({
directory: "./uploads",
baseUrl: "/_emdash/api/media/file",
}),
}),
],
});
// src/live.config.ts
import { defineLiveCollection } from "astro:content";
import { emdashLoader } from "emdash/runtime";
export const collections = {
_emdash: defineLiveCollection({ loader: emdashLoader() }),
};
API
import {
getEmDashCollection,
getEmDashEntry,
getSiteSettings,
getMenu,
getTaxonomyTerms,
getWidgetArea,
search,
} from "emdash";
// Content
const { entries } = await getEmDashCollection("posts");
const { entry } = await getEmDashEntry("posts", "hello-world");
// Site settings
const settings = await getSiteSettings();
// Navigation
const menu = await getMenu("primary");
// Taxonomies
const categories = await getTaxonomyTerms("categories");
// Widgets
const sidebar = await getWidgetArea("sidebar");
// Search
const results = await search("hello world", { collections: ["posts"] });
Documentation
See the documentation site for guides, API reference, and plugin development.