Minor Changes
-
#1540
cf17c9fThanks @marcusbellamyshaw-cell! - Add comment reactionsVisitors can now react to approved comments (positive-only "like" by default,
extensible to other reaction types). Reactions are deduped per voter via IP hash.The
<Comments>component gains two opt-in props:reactions— render a like button per comment and attach live counts.sort="best"— order top-level comments by a Reddit-style Wilson score
lower bound (sort="oldest", the previous behavior, remains the default).
Posting is progressively enhanced (a tiny inline script, no framework island)
and emitted only whenreactionsis enabled, so pages that don't use reactions
ship zero additional JavaScript. Fully additive and backward-compatible: a new
table, a new route, and new optional props with behavior-preserving defaults. -
#1484
d46abfdThanks @swissky! - Forward declarativeportableTextBlocksandfieldWidgetsfrom standard and sandboxed (from-config) pluginsStandard- and sandboxed-format plugins could already declare admin pages and dashboard widgets, but their declarative Portable Text block types and field widgets were dropped during adaptation — only native-format plugins surfaced them. Since the admin editor reads these from the manifest, the slash-menu entries and Block Kit forms never appeared for non-native plugins.
adaptSandboxEntrynow forwards both for standard plugins and sandboxed plugins resolved from config (thevirtual-modules.tscodegen path), so those formats can contribute Portable Text blocks and field widgets. The site-side render component (componentsEntry) still requires native format, which is unchanged.Note: marketplace bundles loaded from R2 are not covered yet — the bundle manifest schema, both
extractManifest()implementations, and the bundler don't carry these fields, so this is scoped to standard/sandboxed-from-config. Full marketplace support is tracked as a follow-up. -
#1564
6a97bacThanks @ascorbic! - Adds byline management to the MCP server:byline_list,byline_get,byline_create,byline_update,byline_delete, andbyline_translationstools, plus abylinesargument oncontent_createso credits can be attached at creation time (previously onlycontent_updateaccepted them). -
#1378
640e60aThanks @scottbuscemi! - Add an optional distributed object cache for query results.Content reads (
getEmDashCollection,getEmDashEntry,resolveEmDashPath) and chrome reads (site settings, menus, taxonomies) can now be served from a fast key/value store instead of hitting the database on every request. This sits beneath the per-request cache and above the database, dramatically reducing read pressure on D1/SQLite — especially valuable on Cloudflare, where KV handles far more requests than D1.The cache is off by default and fully opt-in. Configure a backend in
astro.config.mjs:import { kvCache } from "@emdash-cms/cloudflare"; // Workers KV (distributed) import { memoryCache } from "emdash/astro"; // in-isolate (Node / local dev) emdash({ database: d1({ binding: "DB" }), objectCache: kvCache({ binding: "CACHE" }), });
with a matching KV binding in
wrangler.jsonc:Invalidation is epoch-based and automatic: content, byline, taxonomy, menu, and settings writes bump a per-namespace version, instantly orphaning stale entries (no key enumeration needed). Preview and visual-edit requests bypass the cache, so editors previewing see live content; other reads are served from the cache, which only ever stores published content. After an edit, anonymous visitors may see stale content until isolates pick up the bumped epoch — immediate on the in-isolate memory backend, and on KV bounded by KV's edge-cache propagation (eventually consistent, up to ~60s) plus the
revalidatewindow (default 1s, configurable).New public API:
cachedQuery,invalidateObjectCache,invalidateCollectionCache,contentNamespace/contentNamespaces,CacheNamespace, theObjectCache*types (fromemdash),memoryCache()(fromemdash/astro), andkvCache()(from@emdash-cms/cloudflare). Existing sites are unaffected until they opt in. -
#1549
a623c6bThanks @ascorbic! - Fixes responsive image optimization for storage-backed media on Cloudflare. EmDash now wraps Astro's image endpoint to read media bytes directly from your storage adapter instead of fetching them over HTTP, soImageand Portable Text images generate a real responsivesrcseteven when the site is behind Cloudflare Access (previously these 404'd and fell back to a full-size image). This is on by default and also removes an internal HTTP round-trip on Node. Setimages: falsein youremdash()config to leave Astro's image endpoint untouched.
Patch Changes
-
#1579
0bfab91Thanks @ascorbic! - Fixes a Cloudflare build failure where the barezodimport used by the type generator was not externalized, causing the bundler to fail. EmDash sites on Cloudflare now build correctly under Astro 7. -
#1584
707edeeThanks @ascorbic! - Fixes the dev setup-bypass endpoint accumulating duplicatedev-bypass-tokenaccess tokens. Re-running it (for example after a dev reset) now replaces the previous token with a fresh one instead of adding another row. -
#1581
a36b5f3Thanks @naota70! - Fix the setup probe baking a redirect to/_emdash/admin/setupinto prerendered pages. On a build whose database is empty (e.g. CI/first deploy), the anonymous setup probe saw a missing migrations table and returnedcontext.redirect("/_emdash/admin/setup"); a prerendered route serializes that into static HTML and ships the redirect to production. The probe is now skipped entirely whencontext.isPrerenderedis true — there is no live visitor to send to the wizard at build time, and the build database is legitimately empty. Live (SSR) requests are unaffected. -
#1509
ed921d8Thanks @ascorbic! - Speeds up public page loads on remote databases (D1, Durable Objects) by eagerly warming site-global layout data (menus, widget areas, taxonomy term lists, settings) at the start of the request, so the layout's per-component reads overlap into roughly one round trip instead of executing serially. Transparent to site code; no template changes needed. -
#1563
c219affThanks @ascorbic! - Lets the MCPcontent_createandcontent_updatetools accept a Markdown string for rich text (portableText) fields, converting it to Portable Text automatically — the same behaviour the EmDash client already has. Passing a Portable Text JSON array still works. Authoring rich text as a single Markdown string avoids the large nested JSON payloads that agents frequently emit as malformed JSON, causing the tool call to fail before it reaches the server.content_getandcontent_listgain an optionalmarkdownflag (default false) that returns rich text fields as Markdown instead of Portable Text arrays. -
#1580
ca47da4Thanks @ascorbic! - Fixes query instrumentation (EMDASH_QUERY_LOG=1) so the per-query NDJSON log captures queries issued while the page is still streaming, not just those that ran before the response headers were sent. The per-query log now matches the totals already reported by the[emdash-stream-end]summary line. -
#1538
cb1c689Thanks @swissky! - Fix logged-in pages hanging indefinitely on Cloudflare Workers (#1274). A request cancelled mid-session.get("user")could leave the session-store read as a promise that never settles, poisoning the isolate so every later session-bearing request hung (0-CPU, multi-minute,canceled) — reliably reproducible on fresh isolates right after a deploy. Every session read on the request path (the main middleware, the auth middleware, and the preview-snapshot route) now goes through a sharedresolveSessionUser()helper that anchors the read withafter()so a cancelled request still drives it to completion (preventing the isolate poisoning), with a fail-closed timeout backstop that degrades a still-stalled read to "unauthenticated for this request" rather than hanging. -
Updated dependencies [
f4925b1]:- @emdash-cms/admin@0.22.0
- @emdash-cms/auth@0.22.0
- @emdash-cms/gutenberg-to-portable-text@0.22.0
{ "kv_namespaces": [{ "binding": "CACHE", "id": "<namespace-id>" }] }