Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
634ff5b
feat(cloudflare): Durable Objects SQL database driver with read repli…
ascorbic Jun 15, 2026
c438479
fix(cloudflare): don't cache DO stub in driver; resolve per acquire
ascorbic Jun 15, 2026
313f2cc
feat(perf-monitor): add DO read-replica demo to measured sites
ascorbic Jun 15, 2026
71c5328
fix(perf-monitor): measure a Single Post slug present in the repo seed
ascorbic Jun 15, 2026
4d1f6fe
chore(do-demo): pin the SESSION KV namespace id
ascorbic Jun 15, 2026
6d04f36
fix(cloudflare): close read-your-writes gaps in DO SQL driver (advers…
ascorbic Jun 15, 2026
48432fe
feat(infra): add do-solo-demo perf fixture (DO single primary, no rep…
ascorbic Jun 15, 2026
d7caab9
feat(perf-monitor): register do-solo-demo (DO single primary) as a me…
ascorbic Jun 15, 2026
86ec83b
feat(cloudflare): coalesce same-turn reads into one RPC (DO SQL)
ascorbic Jun 15, 2026
410411c
feat: report DB round trips via rpc.count Server-Timing metric
ascorbic Jun 15, 2026
2d8c206
feat(perf-monitor): add South America (sae) and Oceania (oce) probes
ascorbic Jun 15, 2026
63fea7d
style: format
emdashbot[bot] Jun 15, 2026
ebc3acf
fix(cloudflare): use the current DO replication API (storage.primary/…
ascorbic Jun 15, 2026
16a76a4
chore: update lockfile for do-solo-demo fixture
ascorbic Jun 15, 2026
0f1c0d0
Merge origin/feat/do-sql-driver (bot format)
ascorbic Jun 15, 2026
f2bf630
build: exclude generated worker-configuration.d.ts from oxfmt
ascorbic Jun 15, 2026
91c7ceb
fix(cloudflare): address review on DO SQL driver (#1492)
ascorbic Jun 16, 2026
6bae578
Merge remote-tracking branch 'origin/main' into feat/do-sql-driver
ascorbic Jun 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/bookmark-commit-finally.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"emdash": patch
---

Fixes a read-your-writes gap with database read replication: the session bookmark cookie is now persisted even when page rendering throws after a successful write, so an immediately-following request can't read pre-write state from a lagging replica.
5 changes: 5 additions & 0 deletions .changeset/do-sql-driver.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@emdash-cms/cloudflare": minor
---

Adds a `durableObjects()` database adapter that stores the whole CMS in a single Durable Object's SQLite. With `session: "auto"` (plus the `experimental` and `replica_routing` compatibility flags) reads route to the nearest read replica and writes proxy to the primary, cutting read latency for globally distributed traffic. Register the exported `EmDashDB` class in your worker and add a `new_sqlite_classes` migration.
5 changes: 5 additions & 0 deletions .changeset/rpc-count-metric.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"emdash": patch
---

Adds an `rpc.count` Server-Timing metric reporting physical database round trips, distinct from `db.count` (logical queries). Backends that batch (the new Durable Objects SQL driver coalesces same-turn reads into one round trip) can now surface how many round trips a request actually made.
1 change: 1 addition & 0 deletions .oxfmtrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"**/*.mdx",
"**/package.json",
"**/emdash-env.d.ts",
"**/worker-configuration.d.ts",
"packages/registry-lexicons/src/generated/**",
"packages/plugin-cli/schemas/**"
]
Expand Down
5 changes: 5 additions & 0 deletions infra/do-demo/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
node_modules
dist
.astro
uploads
data.db
38 changes: 38 additions & 0 deletions infra/do-demo/AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
This is an EmDash site -- a CMS built on Astro with a full admin UI.

## Commands

```bash
npx emdash dev # Start dev server (runs migrations, seeds, generates types)
npx emdash types # Regenerate TypeScript types from schema
npx emdash seed seed/seed.json --validate # Validate seed file
```

The admin UI is at `http://localhost:4321/_emdash/admin`.

## Key Files

| File | Purpose |
| ------------------------ | ---------------------------------------------------------------------------------- |
| `astro.config.mjs` | Astro config with `emdash()` integration, database, and storage |
| `src/live.config.ts` | EmDash loader registration (boilerplate -- don't modify) |
| `seed/seed.json` | Schema definition + demo content (collections, fields, taxonomies, menus, widgets) |
| `emdash-env.d.ts` | Generated types for collections (auto-regenerated on dev server start) |
| `src/layouts/Base.astro` | Base layout with EmDash wiring (menus, search, page contributions) |
| `src/pages/` | Astro pages -- all server-rendered |

## Skills

Agent skills are in `.agents/skills/`. Load them when working on specific tasks:

- **building-emdash-site** -- Querying content, rendering Portable Text, schema design, seed files, site features (menus, widgets, search, SEO, comments, bylines). Start here.
- **creating-plugins** -- Building EmDash plugins with hooks, storage, admin UI, API routes, and Portable Text block types.
- **emdash-cli** -- CLI commands for content management, seeding, type generation, and visual editing flow.

## Rules

- All content pages must be server-rendered (`output: "server"`). No `getStaticPaths()` for CMS content.
- Image fields are objects (`{ src, alt }`), not strings. Use `<Image image={...} />` from `"emdash/ui"`.
- `entry.id` is the slug (for URLs). `entry.data.id` is the database ULID (for API calls like `getEntryTerms`).
- Always call `Astro.cache.set(cacheHint)` on pages that query content.
- Taxonomy names in queries must match the seed's `"name"` field exactly (e.g., `"category"` not `"categories"`).
46 changes: 46 additions & 0 deletions infra/do-demo/astro.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import cloudflare from "@astrojs/cloudflare";
import react from "@astrojs/react";
import { durableObjects, r2, sandbox } from "@emdash-cms/cloudflare";
import { formsPlugin } from "@emdash-cms/plugin-forms";
import webhookNotifier from "@emdash-cms/plugin-webhook-notifier";
import { defineConfig, fontProviders } from "astro/config";
import emdash from "emdash/astro";

export default defineConfig({
output: "server",
adapter: cloudflare(),
image: {
layout: "constrained",
responsiveStyles: true,
},
integrations: [
react(),
emdash({
database: durableObjects({ binding: "DB_DO", session: "auto" }),
storage: r2({ binding: "MEDIA" }),
plugins: [formsPlugin()],
sandboxed: [webhookNotifier],
sandboxRunner: sandbox(),
experimental: {
registry: "https://registry.emdashcms.com",
},
}),
],
fonts: [
{
provider: fontProviders.google(),
name: "Inter",
cssVariable: "--font-sans",
weights: [400, 500, 600, 700],
fallbacks: ["sans-serif"],
},
{
provider: fontProviders.google(),
name: "JetBrains Mono",
cssVariable: "--font-mono",
weights: [400, 500],
fallbacks: ["monospace"],
},
],
devToolbar: { enabled: false },
});
41 changes: 41 additions & 0 deletions infra/do-demo/emdash-env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Generated by EmDash on dev server start
// Do not edit manually

/// <reference types="emdash/locals" />

import type { ContentBylineCredit, TaxonomyTerm, PortableTextBlock } from "emdash";

export interface Page {
id: string;
slug: string | null;
status: string;
title: string;
content?: PortableTextBlock[];
createdAt: Date;
updatedAt: Date;
publishedAt: Date | null;
bylines?: ContentBylineCredit[];
terms?: Record<string, TaxonomyTerm[]>;
}

export interface Post {
id: string;
slug: string | null;
status: string;
title: string;
featured_image?: { id: string; src?: string; alt?: string; width?: number; height?: number; provider?: string; previewUrl?: string; meta?: Record<string, unknown> };
content?: PortableTextBlock[];
excerpt?: string;
createdAt: Date;
updatedAt: Date;
publishedAt: Date | null;
bylines?: ContentBylineCredit[];
terms?: Record<string, TaxonomyTerm[]>;
}

declare module "emdash" {
interface EmDashCollections {
pages: Page;
posts: Post;
}
}
32 changes: 32 additions & 0 deletions infra/do-demo/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"name": "@emdash-cms/do-demo-site",
"version": "0.0.18",
"private": true,
"type": "module",
"scripts": {
"dev": "astro dev",
"build": "astro build",
"preview": "astro preview",
"deploy": "astro build && wrangler deploy",
"typecheck": "astro check",
"bootstrap": "emdash init && emdash seed",
"seed": "emdash seed"
},
"dependencies": {
"@astrojs/cloudflare": "catalog:",
"@astrojs/react": "catalog:",
"@emdash-cms/cloudflare": "workspace:*",
"@emdash-cms/plugin-forms": "workspace:*",
"@emdash-cms/plugin-webhook-notifier": "workspace:*",
"@emdash-cms/plugin-cli": "workspace:*",
"astro": "catalog:",
"emdash": "workspace:*",
"react": "catalog:",
"react-dom": "catalog:"
},
"devDependencies": {
"@astrojs/check": "catalog:",
"@cloudflare/workers-types": "catalog:",
"wrangler": "catalog:"
}
}
Loading
Loading