### Start the development server Source: https://github.com/emdash-cms/emdash/blob/main/docs/src/content/docs/existing-project.mdx Run this command in the project root to start the local development server. After it starts, open http://localhost:4321/_emdash/admin/ to access the admin panel and begin the setup wizard. ```bash npm run dev ``` -------------------------------- ### Start generated site Source: https://github.com/emdash-cms/emdash/blob/main/docs/src/content/docs/themes/overview.mdx After scaffolding, install dependencies and start the generated site. Open http://localhost:4321/_emdash/admin to access the admin. ```bash cd my-site npm install npm run dev ``` -------------------------------- ### Run the Demo with pnpm Commands Source: https://github.com/emdash-cms/emdash/blob/main/demos/plugins-demo/README.md Install dependencies, seed sample content, start the development server, and open the browser. Use these commands in order to run the demo locally. ```bash # Install dependencies pnpm install # Seed sample content pnpm seed # Start development server pnpm dev # Open browser open http://localhost:4321 ``` -------------------------------- ### Clone, install, and build the workspace Source: https://github.com/emdash-cms/emdash/blob/main/docs/src/content/docs/contributing/index.mdx Run these commands from a terminal to clone the repository, install dependencies with pnpm, and build the workspace. This is the first step in the local setup. ```bash git clone https://github.com/emdash-cms/emdash.git cd emdash pnpm install pnpm build ``` -------------------------------- ### Database Setup: Start Site and Export Seed Source: https://github.com/emdash-cms/emdash/blob/main/skills/emdash-cli/SKILL.md Start the site with its package script. The first request runs pending migrations and applies the bundled seed's schema and structure once. Export an existing database as a seed file; the runtime auto-discovers .emdash/seed.json on first boot, so create the directory first. ```bash pnpm dev ``` ```bash mkdir -p .emdash npx emdash export-seed > .emdash/seed.json npx emdash export-seed --with-content=all > .emdash/seed.json ``` -------------------------------- ### Start EmDash blog development server Source: https://github.com/emdash-cms/emdash/blob/main/docs/src/content/docs/guides/create-a-blog.mdx Starts the local development server for the scaffolded blog. After running, open the printed local URL and navigate to /_emdash/admin to complete the first-run setup. ```bash cd my-blog pnpm dev ``` -------------------------------- ### Quick Start Commands for EmDash Template Source: https://github.com/emdash-cms/emdash/blob/main/TEMPLATES.md Commands to copy a template, install dependencies, initialize the database with demo content, and start the dev server. After running, the site is available at http://localhost:4321 and the CMS at http://localhost:4321/_emdash/admin. ```bash # Copy the template you want cp -r templates/blog my-site cd my-site # Install dependencies pnpm install # Initialize the database and seed demo content pnpm bootstrap # Start the dev server pnpm dev ``` -------------------------------- ### Start a release with the package-tag trigger Source: https://github.com/emdash-cms/emdash/blob/main/docs/src/content/docs/plugins/creating-plugins/delegated-releases.mdx Creates and pushes a version tag to start a release. The example starts a 1.2.3 release for the gallery package. ```sh git tag gallery@1.2.3 git push origin gallery@1.2.3 ``` -------------------------------- ### Fetch plugin package and latest release with DiscoveryClient Source: https://github.com/emdash-cms/emdash/blob/main/docs/src/content/docs/plugins/registry-client.mdx Example of using DiscoveryClient to fetch a package by DID and slug, then get the latest release. Requires installation of @emdash-cms/registry-client/discovery. ```typescript import { DiscoveryClient } from "@emdash-cms/registry-client/discovery"; const discovery = new DiscoveryClient({ aggregatorUrl: "https://registry.emdashcms.com", }); export async function getPlugin(did: string, slug: string) { const pkg = await discovery.getPackage({ did, slug }); const latest = await discovery.getLatestRelease({ did: pkg.did, package: pkg.slug, }); return { pkg, latest }; } ``` -------------------------------- ### Create a demo site from a template Source: https://github.com/emdash-cms/emdash/blob/main/CONTRIBUTING.md Build your own site in the monorepo by copying a template into `demos/`, giving it a unique `name` in `package.json`, installing, and running. Your site uses `workspace:*` links, so core changes are reflected immediately. ```bash cp -r templates/blog demos/my-site # edit demos/my-site/package.json to set a unique name pnpm install cd demos/my-site && pnpm dev ``` -------------------------------- ### Install skills with the Codex CLI Source: https://github.com/emdash-cms/emdash/blob/main/docs/src/content/docs/agent-skills.mdx Add the EmDash marketplace and install its plugin with the Codex CLI. The plugin includes all five EmDash skills. Start a new Codex task after installing it. ```sh codex plugin marketplace add emdash-cms/skills codex plugin add emdash@emdash ``` -------------------------------- ### Install skills with Claude Code Source: https://github.com/emdash-cms/emdash/blob/main/docs/src/content/docs/agent-skills.mdx Add the repository as a plugin marketplace, then install the `emdash` plugin. Claude Code names the installed skills with an `emdash:` prefix, for example `emdash:building-emdash-site`. ```sh /plugin marketplace add emdash-cms/skills /plugin install emdash@emdash ``` -------------------------------- ### Start development server with npm Source: https://github.com/emdash-cms/emdash/blob/main/docs/src/content/docs/getting-started.mdx Enter the project directory and start the development server using npm. Keep the terminal open; Astro prints the local site URL, normally http://localhost:4321/. ```bash cd my-emdash-site npm run dev ``` -------------------------------- ### Start development server with bun Source: https://github.com/emdash-cms/emdash/blob/main/docs/src/content/docs/getting-started.mdx Enter the project directory and start the development server using bun. Keep the terminal open; Astro prints the local site URL, normally http://localhost:4321/. ```bash cd my-emdash-site bun dev ``` -------------------------------- ### plugin:install Source: https://github.com/emdash-cms/emdash/blob/main/docs/src/content/docs/reference/hooks.mdx Lifecycle hook that runs when a plugin is first installed. Use for initial setup, creating storage collections, or seeding data. ```APIDOC ## plugin:install ### Description Runs when a plugin is first installed. Use for initial setup, creating storage collections, or seeding data. ### Event Empty event object. ### Return Value None. ### Example ```ts hooks: { "plugin:install": async (event, ctx) => { // Initialize default settings await ctx.settings.set("enabled", true); await ctx.settings.set("threshold", 100); ctx.log.info("Plugin installed successfully"); }, } ``` ``` -------------------------------- ### Install globally and initialize Source: https://github.com/emdash-cms/emdash/blob/main/packages/plugin-cli/README.md Installs the CLI globally with npm, then runs the init command directly. This is an alternative to using pnpm dlx. ```sh npm install -g @emdash-cms/plugin-cli emdash-plugin init my-plugin ``` -------------------------------- ### Install @emdash-cms/plugin-test and vitest Source: https://github.com/emdash-cms/emdash/blob/main/docs/src/content/docs/plugins/creating-plugins/testing.mdx Install the test host and Vitest as development dependencies for an existing plugin project. Projects created by emdash-plugin init already include this setup. ```sh pnpm add -D @emdash-cms/plugin-test vitest ``` -------------------------------- ### SQLite file path examples Source: https://github.com/emdash-cms/emdash/blob/main/docs/src/content/docs/deployment/database.mdx These examples show how to specify the SQLite database file path using relative, absolute, and environment variable forms. The `url` must start with `file:`. ```js // Relative path database: sqlite({ url: "file:./data/emdash.db" }); // Absolute path database: sqlite({ url: "file:/var/data/emdash.db" }); // From environment variable database: sqlite({ url: `file:${process.env.DATABASE_PATH}` }); ``` -------------------------------- ### Start the primary development demo Source: https://github.com/emdash-cms/emdash/blob/main/docs/src/content/docs/contributing/index.mdx Starts the demo from the demos/simple directory. The demo runs on Node.js with SQLite at http://localhost:4321 and applies the schema from seed/seed.json on the first request. ```bash cd demos/simple pnpm dev ``` -------------------------------- ### plugin:install lifecycle hook Source: https://github.com/emdash-cms/emdash/blob/main/docs/src/content/docs/reference/hooks.mdx Runs when a plugin is first installed. Use for initial setup, creating storage collections, or seeding data. The hook receives an empty event object and has no return value. ```ts hooks: { "plugin:install": async (event, ctx) => { // Initialize default settings await ctx.settings.set("enabled", true); await ctx.settings.set("threshold", 100); ctx.log.info("Plugin installed successfully"); }, } ``` -------------------------------- ### Prepare another package for release Source: https://github.com/emdash-cms/emdash/blob/main/skills/creating-plugins/references/publishing.md Run profile setup for a different package directory, then push its tag. The profile setup confirms publication and prints the next commands. The package must be added to a changeset for Changesets users. ```sh pnpm exec emdash-plugin profile setup --dir packages/comments git tag comments@1.0.0 git push origin comments@1.0.0 ``` -------------------------------- ### GET /download Source: https://github.com/emdash-cms/emdash/blob/main/docs/src/content/docs/plugins/creating-plugins/api-routes.mdx Example of a raw response route that returns a CSV file with custom headers and status. ```APIDOC ## GET /download ### Description Returns a raw CSV file with a custom status code and safe response headers. The response is buffered up to 8 MiB and can be either text or bytes. The route is public and only accepts `GET` requests. ### Method GET ### Endpoint `/download` (route name: `download`) ### Parameters #### Request Body - **body** (none) - Required - The request must have no body. ### Request Example No request body. ### Response #### Success Response (200) - **status** (number) - HTTP status code (e.g., `200`). - **headers** (object) - Allowed headers: `Accept-Ranges`, `Content-Disposition`, `Content-Encoding`, `Content-Language`, `Content-Range`, `Content-Type`, `ETag`, `Last-Modified`, `Location`, `Retry-After`. Other headers are removed. - **body** (object) - Either `{ "kind": "text", "value": string }` or `{ "kind": "bytes", "value": Uint8Array }`. #### Response Example ```json { "status": 200, "headers": { "content-type": "text/csv; charset=utf-8", "content-disposition": "attachment; filename=\"report.csv\"" }, "body": { "kind": "text", "value": "name,count\nPublished,12\n" } } ``` ### Notes - The host adds `X-Content-Type-Options: nosniff`, a sandboxed document content security policy, and `Referrer-Policy: no-referrer`. - The route's `cacheControl` is applied only to successful public `GET` and `HEAD` responses. Other responses use `private, no-store`. - Raw routes cannot serve active same-origin content. EmDash rejects HTML, JavaScript, ECMAScript, XHTML, SVG, XML, CSS, WebAssembly, `multipart/related`, and `multipart/x-mixed-replace` media types. ``` -------------------------------- ### Initialize a plugin with @emdash-cms/plugin-cli init Source: https://github.com/emdash-cms/emdash/blob/main/docs/src/content/docs/plugins/creating-plugins/cli.mdx Scaffolds a new plugin project with emdash-plugin.jsonc, src/plugin.ts, package.json, tsconfig.json, vitest.config.ts, a workerd-backed test, README, AGENTS.md, a local creating-plugins skill, and package-manager configuration. Interactive setup asks for publisher, author, security contact, and source repository; required fields cannot be skipped. ```sh pnpm dlx @emdash-cms/plugin-cli init my-plugin ``` -------------------------------- ### Clone and build the EmDash monorepo Source: https://github.com/emdash-cms/emdash/blob/main/README.md Clone the repository and install dependencies with pnpm, then build the project. This is the initial setup for contributing. ```bash git clone https://github.com/emdash-cms/emdash.git && cd emdash pnpm install pnpm build ``` -------------------------------- ### First-time setup commands Source: https://github.com/emdash-cms/emdash/blob/main/infra/perf-monitor/README.md Commands to create the D1 database, apply schema, create queues, deploy workers, and subscribe to Workers Builds events. Requires copying the database_id into wrangler.jsonc and using the CF dashboard or API for queue subscription. ```bash # 1. Create the D1 database and apply the initial schema wrangler d1 create emdash_perf # copy the database_id into wrangler.jsonc wrangler d1 execute emdash_perf --remote --file=schema.sql pnpm db:migrations:apply # any incremental migrations on top # 2. Create the deploy events queue and DLQ wrangler queues create emdash-perf-deploy-events wrangler queues create emdash-perf-deploy-events-dlq # 3. Build and deploy all 5 Workers pnpm deploy # 4. Subscribe the queue to Workers Builds events. # (No wrangler command for this yet -- use the CF dashboard or API: # https://developers.cloudflare.com/queues/event-subscriptions/manage-event-subscriptions/) # Source: Workers Builds # Events: build.succeeded (at minimum) # Queue: emdash-perf-deploy-events # 5. (Optional, to enable manual triggers) Add a Cloudflare Access policy # on POST /api/trigger. See "Access setup" above. ``` -------------------------------- ### Start dev server and refresh types Source: https://github.com/emdash-cms/emdash/blob/main/skills/building-emdash-site/references/configuration.md Commands to start the Astro dev server and refresh types from the running site. The runtime runs pending migrations on the first request and applies the seed's schema and structure once before setup is completed. ```bash pnpm dev # Start the Astro dev server npx emdash types # Refresh types from the running site ``` -------------------------------- ### Compile and run the demo Source: https://github.com/emdash-cms/emdash/blob/main/docs/src/content/docs/contributing/translating.mdx Run these commands to compile locale catalogs, build the project, and start the demo dev server. Always test translations in the running admin UI to catch context issues. ```bash pnpm run locale:compile pnpm build pnpm --filter emdash-demo dev ``` -------------------------------- ### Start development server with pnpm Source: https://github.com/emdash-cms/emdash/blob/main/docs/src/content/docs/getting-started.mdx Enter the project directory and start the development server using pnpm. Keep the terminal open; Astro prints the local site URL, normally http://localhost:4321/. ```bash cd my-emdash-site pnpm dev ``` -------------------------------- ### Run the demo site (Node.js + SQLite) Source: https://github.com/emdash-cms/emdash/blob/main/README.md Seed and start the demo site locally without a Cloudflare account. The admin interface is available at http://localhost:4321/_emdash/admin. ```bash pnpm --filter emdash-demo seed pnpm --filter emdash-demo dev ``` -------------------------------- ### Build the documentation site Source: https://github.com/emdash-cms/emdash/blob/main/docs/README.md Builds the documentation site for production. ```bash pnpm build ``` -------------------------------- ### Configure Cloudflare sandbox runner in astro.config.mjs Source: https://github.com/emdash-cms/emdash/blob/main/docs/src/content/docs/plugins/registry.mdx Add the Cloudflare sandbox runner to the emdash() configuration in astro.config.mjs to enable registry installs. Requires prior plugin sandbox setup. ```typescript import { defineConfig } from "astro/config"; import { sandbox } from "@emdash-cms/cloudflare"; import emdash from "emdash/astro"; export default defineConfig({ integrations: [ emdash({ sandboxRunner: sandbox(), }), ], }); ``` -------------------------------- ### Install @x402/svm Source: https://github.com/emdash-cms/emdash/blob/main/docs/src/content/docs/guides/x402-payments.mdx Install the optional Solana support package @x402/svm. Solana support is opt-in. ```bash pnpm add @x402/svm ``` -------------------------------- ### Run tests: all, core only, watch, E2E Source: https://github.com/emdash-cms/emdash/blob/main/docs/src/content/docs/contributing/index.mdx Tests use real in-memory SQLite databases instead of mocks; each test gets a fresh database. The E2E command starts its own server. ```bash pnpm test ``` ```bash cd packages/core && pnpm test ``` ```bash cd packages/core && pnpm test --watch ``` ```bash pnpm test:e2e # starts its own server ``` -------------------------------- ### Update EmDash packages with pnpm Source: https://github.com/emdash-cms/emdash/blob/main/docs/src/content/docs/deployment/updating.mdx Commands to check installed versions, update packages to the latest release, build the site, and start the dev server. For a Node.js deployment, omit @emdash-cms/cloudflare from the first two commands. The build writes the migration manifest; pending core migrations run on the first request after starting the dev server. ```bash pnpm outdated emdash @emdash-cms/cloudflare ``` ```bash pnpm up --latest emdash @emdash-cms/cloudflare ``` ```bash pnpm build ``` ```bash pnpm dev ``` -------------------------------- ### Run emdash-plugin release setup Source: https://github.com/emdash-cms/emdash/blob/main/docs/src/content/docs/plugins/creating-plugins/cli.mdx Run from a plugin directory or pass --dir to prepare package profiles and create a shared .github/workflows/emdash-release.yml at the Git repository root. Accepts profile setup flags plus workflow options like --service-url, --action-ref, --trigger, and --force. The command never pushes the generated workflow. ```sh emdash-plugin release setup ``` -------------------------------- ### Use routeCtx.user for per-user logic Source: https://github.com/emdash-cms/emdash/blob/main/docs/src/content/docs/plugins/creating-plugins/api-routes.mdx On private routes, routeCtx.user is the authenticated user. Never read the acting user from the request body. The example stores a connection start time in KV using the caller's ID. ```typescript routes: { "connect/start": { handler: async (routeCtx, ctx) => { // Never read the acting user from the request body — any authenticated // session could impersonate another user that way. Use routeCtx.user. const caller = routeCtx.user; if (!caller) throw new Error("No caller bound"); await ctx.kv.set(`user:${caller.id}:connection`, { startedAt: Date.now() }); return { userId: caller.id }; }, }, }, ``` -------------------------------- ### Start disposable site with pnpm ux:site:start Source: https://github.com/emdash-cms/emdash/blob/main/skills/ux-acceptance-coordinator/SKILL.md Starts a disposable EmDash site for the UX acceptance journey. Use the --target node flag unless the Cloudflare runtime is required. The command output provides the run ID, start URL, and run-data path; keep these private and never send the run-data file or API token to the tester. ```console pnpm ux:site:start -- --target node --profile editorial-small ``` -------------------------------- ### Create and configure a dedicated schema for EmDash Source: https://github.com/emdash-cms/emdash/blob/main/docs/src/content/docs/deployment/database.mdx Use these SQL statements with an administrative connection to create a dedicated schema owned by emdash_app and set its search_path. This is optional and best done before first setup; it does not move an existing installation. ```sql GRANT CONNECT ON DATABASE app TO emdash_app; CREATE SCHEMA emdash AUTHORIZATION emdash_app; ALTER ROLE emdash_app IN DATABASE app SET search_path = emdash; ``` -------------------------------- ### Install Node blog template Source: https://github.com/emdash-cms/emdash/blob/main/docs/src/content/docs/themes/overview.mdx Scaffolds the Node blog template using create-astro. ```bash npm create astro@latest -- --template @emdash-cms/template-blog ``` -------------------------------- ### Start development server with yarn Source: https://github.com/emdash-cms/emdash/blob/main/docs/src/content/docs/getting-started.mdx Enter the project directory and start the development server using yarn. Keep the terminal open; Astro prints the local site URL, normally http://localhost:4321/. ```bash cd my-emdash-site yarn dev ``` -------------------------------- ### Run EmDash dev server with pnpm dev Source: https://github.com/emdash-cms/emdash/blob/main/skills/building-emdash-site/SKILL.md Starts the Astro dev server. The runtime runs pending migrations on the first request and applies the seed schema once before setup is complete. The admin UI is available at http://localhost:4321/_emdash/admin. ```bash pnpm dev # Start the Astro dev server ``` -------------------------------- ### Define content model in seed/seed.json Source: https://github.com/emdash-cms/emdash/blob/main/docs/src/content/docs/themes/creating-themes.mdx A reduced seed file that keeps the collections and structural data used by the examples in this guide. The content entry 'id' is a seed-local identifier used by references; its 'slug' becomes the route identifier exposed as 'entry.id' by the query API. ```json { "$schema": "https://emdashcms.com/seed.schema.json", "version": "1", "meta": { "name": "Publication", "description": "A publication with posts" }, "settings": { "title": "Publication", "tagline": "Latest articles" }, "collections": [ { "slug": "posts", "label": "Posts", "labelSingular": "Post", "supports": ["drafts", "revisions", "search", "seo"], "fields": [ { "slug": "title", "label": "Title", "type": "string", "required": true, "searchable": true }, { "slug": "excerpt", "label": "Excerpt", "type": "text" }, { "slug": "featured_image", "label": "Featured image", "type": "image" }, { "slug": "content", "label": "Content", "type": "portableText", "searchable": true } ] }, { "slug": "pages", "label": "Pages", "labelSingular": "Page", "supports": ["drafts", "revisions", "search"], "fields": [ { "slug": "title", "label": "Title", "type": "string", "required": true, "searchable": true }, { "slug": "content", "label": "Content", "type": "portableText", "searchable": true }, { "slug": "template", "label": "Page template", "type": "select", "defaultValue": "default", "validation": { "options": ["default", "full-width", "landing"] } } ] } ], "menus": [ { "name": "primary", "label": "Primary navigation", "items": [ { "type": "custom", "label": "Home", "url": "/" }, { "type": "custom", "label": "Posts", "url": "/posts" } ] } ], "widgetAreas": [ { "name": "sidebar", "label": "Sidebar", "widgets": [] } ], "content": { "posts": [ { "id": "post-welcome", "slug": "welcome", "status": "published", "data": { "title": "Welcome", "excerpt": "The first article", "content": [] } } ] } } ```