github emdash-cms/emdash emdash@0.8.0

latest release: @emdash-cms/gutenberg-to-portable-text@0.8.0
12 hours ago

Minor Changes

  • #679 493e317 Thanks @drudge! - Adds a repeater Block Kit element: array-of-objects with scalar sub-fields, drag-to-reorder, and collapsible item cards. Plugin block forms can now capture repeating data (FAQ rows, carousel slides, card grids) inline in the portable-text editor.

  • #779 e402890 Thanks @ascorbic! - Adds settings_get and settings_update MCP tools so agents can read and update site-wide settings (title, tagline, logo, favicon, URL, posts-per-page, date format, timezone, social, SEO). settings_get resolves media references (logo/favicon/seo.defaultOgImage) to URLs; settings_update is a partial update that preserves omitted fields. New settings:read (EDITOR+) and settings:manage (ADMIN) API token scopes back the tools, with matching options in the personal API token settings UI.

  • #777 3eca9d5 Thanks @ascorbic! - Behavior change — MCP taxonomy_list_terms now uses an opaque base64 keyset cursor over (label, id) instead of the previous raw term-id cursor. The new cursor is robust to concurrent term deletion: it encodes a position in sort space rather than a reference to a specific row. MCP clients that persisted page cursors across this upgrade should drop them and restart pagination — pre-upgrade cursors will return INVALID_CURSOR.

    Adds parent-chain validation to taxonomy_create_term (previously only taxonomy_update_term validated): rejects non-existent parents, cross-taxonomy parents, self-parent on update, cycles on update, and parent chains exceeding 100 ancestors. Existing taxonomies with chains over the depth limit continue to function but cannot accept new descendants until the chain is shortened.

  • #675 b6cb2e6 Thanks @eyupcanakman! - Renders local media through storage publicUrl when configured. EmDashImage and the Portable Text image block now call a new locals.emdash.getPublicMediaUrl() helper, so R2 and S3 deployments with a custom domain serve images from that domain. S3Storage.getPublicUrl now returns the /_emdash/api/media/file/{key} path when no publicUrl is set (previously {endpoint}/{bucket}/{key}).

  • #398 31333dc Thanks @simnaut! - Adds pluggable auth provider system with AT Protocol as the first plugin-based provider. Refactors GitHub and Google OAuth from hardcoded buttons into the same AuthProviderDescriptor interface. All auth methods (passkey, AT Protocol, GitHub, Google) are equal options on the login page and setup wizard.

Patch Changes

  • #777 3eca9d5 Thanks @ascorbic! - Fixes MCP ownership checks failing with an internal error on content that has no authorId (seed-imported rows). Admins and editors can now edit, publish, unpublish, schedule, and restore such items; users with only own-content permissions get a clean permission error.

  • #777 3eca9d5 Thanks @ascorbic! - Fixes content create / update silently accepting invalid data: required fields are now enforced, select / multiSelect values must match the configured options, and reference fields must resolve to a real, non-trashed target. Errors surface with a structured VALIDATION_ERROR code and a message naming every offending field.

  • #670 37ada52 Thanks @segmentationfaulter! - Change text direction of input fields and tiptap editor depending upon the language entered

  • #688 0557b62 Thanks @corwinperdomo! - Fixes the Settings > Email admin page so active email:beforeSend / email:afterSend middleware plugins are listed (previously always empty). Adds HookPipeline.getHookProviders() for enumerating non-exclusive hook providers.

  • #673 5a581d9 Thanks @mvanhorn! - Fixes WordPress media import to emit relative /_emdash/api/media/file/... URLs instead of absolute ones, matching every other media endpoint. Imported media is now recognized by INTERNAL_MEDIA_PREFIX for enrichment, and no longer pins URLs to the origin that happened to serve the import request (breaking renders on a different port or behind a reverse proxy).

  • #750 0ecd3b4 Thanks @edrpls! - Make the admin collection list column headers sortable. Title, Status, Locale, and Date are now clickable buttons that toggle direction; the current sort state is exposed via aria-sort on the <th> so screen readers announce it correctly.

    The server's orderBy field whitelist now accepts status, locale, and name alongside the existing date fields — unchanged from a security standpoint, the repo still rejects unknown field names to prevent column enumeration.

    Callers of <ContentList> that don't pass onSortChange render the previous static-label headers, so legacy integrations (e.g. the content picker) are unaffected.

  • 3138432 Thanks @r2sake! - Fixes hydration of the inline PortableText editor on pnpm projects by aliasing use-sync-external-store/shim to the main use-sync-external-store package. The shim is a CJS-only React<18 polyfill imported transitively by @tiptap/react; under pnpm's virtual store Vite cannot pre-bundle it, and the browser receives raw module.exports which fails to load as ESM (SyntaxError: ... does not provide an export named 'useSyncExternalStore'). The aliases redirect to React's built-in useSyncExternalStore (peer-dep floor is React 18), so users no longer need to add the workaround themselves in astro.config.mjs.

  • #755 70924cd Thanks @mvanhorn! - Fixes the WordPress importer so collections created mid-import are visible to the subsequent execute phase.

    POST /_emdash/api/import/wordpress/prepare now calls emdash.invalidateManifest() when it creates new collections or fields. Without this, the DB-persisted manifest cache (emdash:manifest_cache in the options table) stays stale and the execute request reports Collection "<slug>" does not exist for every item destined for a freshly created collection — a bug that survived dev-server restarts and required manually deleting the cache row.

  • #757 1f0f6f2 Thanks @ascorbic! - Removes two redundant in-scope database queries from the FTS verify-and-repair path. The inner block re-fetched searchable fields and search config that were already loaded in the outer scope of the same method. No behavior change.

  • #777 3eca9d5 Thanks @ascorbic! - Fixes paginated list endpoints silently returning the first page when given a malformed cursor. Bad cursors now produce a structured INVALID_CURSOR error so client pagination bugs surface immediately.

    Note for plugin authors: the low-level decodeCursor export from emdash/database/repositories now throws InvalidCursorError on invalid input instead of returning null. Direct callers (rare — most code uses findMany-style helpers that handle this internally) should wrap the call in try/catch or migrate to the higher-level helpers.

  • #777 3eca9d5 Thanks @ascorbic! - Fixes schema_create_collection MCP tool to apply its documented default of ['drafts', 'revisions'] for supports when omitted.

  • #189 f5658f0 Thanks @Sayeem3051! - Add url and email plugin setting field types (Issue #175)

  • #777 3eca9d5 Thanks @ascorbic! - Preserves structured error codes through MCP tool responses. Errors returned by MCP tools now include a stable [CODE] prefix in the message text and a _meta.code field on the response envelope, so MCP clients can distinguish failure modes (e.g. NOT_FOUND, CONFLICT, VALIDATION_ERROR) instead of seeing only a generic message.

  • #777 3eca9d5 Thanks @ascorbic! - Fixes revision_restore for collections that support revisions: restore now creates a new draft revision from the source revision's data and updates draft_revision_id, leaving the live columns untouched. Previously, restore overwrote the live row directly and left any pending draft unchanged, opposite to the documented contract ("Replaces the current draft..."). The response is also hydrated so the returned data reflects the post-restore state.

    Behavior is unchanged for collections that do not support revisions.

  • #734 cf1edae Thanks @huckabarry! - Preserve clearer error logging and run sandboxed after() content hook tasks in parallel when deferred plugin hooks execute after save and publish.

  • #794 b352e88 Thanks @ascorbic! - Sanitises the snippet field returned by the search() API so it is safe to render with set:html / innerHTML. Previously SQLite's FTS5 snippet() function spliced literal <mark> tags around matched terms but left the surrounding text unescaped, meaning a post title like Hello <script>alert(1)</script> would render as live markup. Templates and components rendering snippets directly were exposed; the in-tree LiveSearch component already worked around this client-side. Snippets now contain only HTML-escaped source text plus literal <mark>...</mark> highlight tags, matching the documented contract.

  • #183 da3d065 Thanks @masonjames! - Fixes Astro dev to use the built admin package for external app installs while keeping source aliasing for local monorepo development.

  • #777 3eca9d5 Thanks @ascorbic! - Tightens conflict-error matchers in handleContentCreate and handleContentUpdate. Both paths now match specifically on "unique constraint failed" or "duplicate key" (avoiding false positives where the word "unique" appears in unrelated error text), and produce sanitized SLUG_CONFLICT / CONFLICT messages so raw database error text — including Postgres-internal index names — no longer leaks to API consumers. Clients that pattern-match the previous unsanitized messages will see normalized text instead.

  • #777 3eca9d5 Thanks @ascorbic! - Fixes taxonomy_list exposing collection slugs for collections that no longer exist. Orphaned slugs are filtered out so the response stays consistent with schema_list_collections.

  • #777 3eca9d5 Thanks @ascorbic! - Fixes content_unpublish so that publishedAt is cleared when an item is unpublished.

  • #608 47978b5 Thanks @drudge! - Fixes /_emdash/api/widget-areas/* endpoints returning raw DB rows (snake_case fields, content as a JSON string) instead of the transformed Widget shape. Admin UI expects content to already be a parsed PortableText array and componentId/componentProps/menuName in camelCase, so expanding a content widget in /_emdash/admin/widgets produced an empty editor. All four route handlers (GET /widget-areas, GET /widget-areas/:name, POST /widget-areas/:name/widgets, PUT /widget-areas/:name/widgets/:id) now run their results through rowToWidget, which was made module-exported.

  • #777 3eca9d5 Thanks @ascorbic! - Adds taxonomies:manage and menus:manage API token scopes for fine-grained control over taxonomy and menu mutations via MCP and REST. Existing tokens with content:write continue to work for those operations: content:write now implicitly grants menus:manage and taxonomies:manage so PATs issued before the split keep their effective permissions. The reverse implication does not hold — a token with only menus:manage cannot create or edit content.

  • Updated dependencies [86b26f6, 493e317, e998083, 37ada52, acab807, 0ecd3b4, 4c9f04d, e402890, ed4d880, 31333dc, 3eca9d5]:

    • @emdash-cms/admin@1.0.0
    • @emdash-cms/auth@1.0.0
    • @emdash-cms/auth-atproto@1.0.0
    • @emdash-cms/gutenberg-to-portable-text@1.0.0

Don't miss a new emdash release

NewReleases is sending notifications on new releases.