This release includes 3 breaking changes for platform teams planning a safe upgrade.
✓ No known CVEs patched in this version
Topics
Affected surfaces
Summary
AI summaryUpdates Minor Changes, Patch Changes, and https://github.com/ascorbic across a mixed release.
Changes in this release
| Type | Severity | Summary | CVE |
|---|---|---|---|
| Breaking | Medium |
POST /menus/:name/items no longer accepts unknown keys silently; returns 400 with unrecognized key errors. POST /menus/:name/items no longer accepts unknown keys silently; returns 400 with unrecognized key errors. Source: granite4.1:8b-q6_K@2026-05-19 Confidence: high |
— |
| Breaking | Medium |
Legacy query-string item writes removed; use PUT /menus/:name/items/:id and DELETE /menus/:name/items/:id instead. Legacy query-string item writes removed; use PUT /menus/:name/items/:id and DELETE /menus/:name/items/:id instead. Source: granite4.1:8b-q6_K@2026-05-19 Confidence: high |
— |
| Breaking | Medium |
Menu and menu-item API responses now camelCase, breaking clients expecting snake_case keys. Menu and menu-item API responses now camelCase, breaking clients expecting snake_case keys. Source: granite4.1:8b-q6_K@2026-05-19 Confidence: high |
— |
| Feature | Medium |
Adds experimental decentralized plugin registry support via `experimental.registry.aggregatorUrl` config. Adds experimental decentralized plugin registry support via `experimental.registry.aggregatorUrl` config. Source: granite4.1:8b-q6_K@2026-05-19 Confidence: high |
— |
| Feature | Medium |
Experimental config includes `policy.minimumReleaseAge` and `policy.minimumReleaseAgeExclude` for release gating. Experimental config includes `policy.minimumReleaseAge` and `policy.minimumReleaseAgeExclude` for release gating. Source: granite4.1:8b-q6_K@2026-05-19 Confidence: high |
— |
| Feature | Medium |
Adds Indonesian translations for SEO settings labels, replacing Adds Indonesian translations for SEO settings labels, replacing Source: granite4.1:8b-q6_K@2026-05-19 Confidence: low |
— |
| Bugfix | Medium |
Admin collection list pagination denominator now uses total filtered count, preventing incremental growth. Admin collection list pagination denominator now uses total filtered count, preventing incremental growth. Source: granite4.1:8b-q6_K@2026-05-19 Confidence: low |
— |
| Bugfix | Medium |
Image collapsing media picker adds onError handler and fallback placeholder for missing images. Image collapsing media picker adds onError handler and fallback placeholder for missing images. Source: granite4.1:8b-q6_K@2026-05-19 Confidence: low |
— |
| Bugfix | Medium |
Slash command menu initial selection no longer overridden by stationary pointer; fixes keyboard default handling. Slash command menu initial selection no longer overridden by stationary pointer; fixes keyboard default handling. Source: granite4.1:8b-q6_K@2026-05-19 Confidence: low |
— |
| Refactor | Medium |
Menus refactored to standard repository pattern with new MenuRepository. Menus refactored to standard repository pattern with new MenuRepository. Source: granite4.1:8b-q6_K@2026-05-19 Confidence: low |
— |
Full changelog
Minor Changes
-
#1052
0d5843fThanks @Rimander! - Fixes menu REST API consistency:POST /menus/:name/itemsno longer accepts unknown keys silently. Sendingcustom_url(snake_case) orurlused to return 201 withcustom_url: nullbecause Zod's default.strip()quietly dropped them. The schemas now use.strict()and return 400VALIDATION_ERRORwithUnrecognized key: "custom_url". The documented camelCase keys (customUrl,sortOrder,referenceCollection, etc.) are unchanged and persist as before. Thetypefield is now validated against the canonical enum ("custom" | "page" | "post" | "taxonomy" | "collection"); previously any string passed.- Moves per-item writes to
PUTandDELETE /menus/:name/items/:id(path-style). Every other EmDash resource (content,taxonomies,redirects,sections,widget-areas) addresses items by URL path; menus were the lone outlier requiring?id=<id>in the query string. The legacy query-string form is removed (it was undocumented and only used by the admin, which is updated in this PR). Callers should usePUT /menus/:name/items/:id/DELETE /menus/:name/items/:id. - Menu and menu-item API responses are now camelCase, aligning with the rest of EmDash's REST surface (
content,taxonomies,redirects, …).created_at→createdAt,updated_at→updatedAt,menu_id→menuId,parent_id→parentId,sort_order→sortOrder,reference_collection→referenceCollection,reference_id→referenceId,custom_url→customUrl,title_attr→titleAttr,css_classes→cssClasses,translation_group→translationGroup. Breaking for direct REST consumers that depend on snake_case keys in the response body. The admin UI is already updated. - Refactors menus to the standard repository pattern. Adds
MenuRepositorynext toContentRepository,TaxonomyRepository,RedirectRepository,MediaRepository,CommentRepository. Handlers become thin orchestrators; the repository is now the single place where snake_case rows become camelCase entities.
These changes do not touch any database schema or migration. Existing data is preserved.
-
#1011
dbaea9cThanks @ascorbic! - Adds experimental support for the decentralized plugin registry (see RFC #694). Configure withexperimental.registry.aggregatorUrlinastro.config.mjs; the admin UI then uses the registry instead of the centralized marketplace for browse and install. Marketplace behavior is unchanged when the option is not set.The experimental config accepts a
policy.minimumReleaseAgeduration (e.g."48h") that holds back releases below that age from install and update prompts, with apolicy.minimumReleaseAgeExcludeallowlist for trusted publishers or specific packages. The minimum-release-age check is enforced both client-side (for UX) and server-side (in the install endpoint), so stale browser tabs and deep links still hit the gate.
Patch Changes
-
#751
05440b1Thanks @edrpls! - Fix the admin collection list pagination denominator so it no longer grows in increments of 5 as the user pages forward.The
GET /_emdash/api/content/{collection}response now includes atotalfield with the full filtered row count (independent oflimit). The admin uses it as the pagination denominator, so a 143-entry collection reads1/8on page 1 instead of1/5 → 5/10 → 10/15 → …as successive API pages load.The
totalfield is optional; pre-upgrade clients that ignore it still work, and the admin falls back to the loaded-item count when an older server doesn't return it.Also handles the edge case where the current page exceeds
totalPagesafter filtering or deletion — the admin clamps the active page so the table doesn't render empty while waiting for a refetch. -
#1050
484e7abThanks @wojtekpiskorz! - Fixes broken image collapsing media picker container — addsonErrorhandler and fallback placeholder so Change/Remove buttons remain accessible when referenced image is missing from storage -
#1013
0cd8c6dThanks @ascorbic! - Fixes the slash command menu's initial selection getting overridden when the menu opens under a stationary pointer. The menu items previously reacted tomouseenterunconditionally, so an item rendered beneath the cursor would steal selection from the keyboard default before any user interaction. Mouse-hover-selects still works, but only after the user actually moves the pointer over the menu. -
#1020
d014b48Thanks @ahliweb! - Adds missing Indonesian (id) translations for SEO settings labels and replaces "Edit" with "Sunting" and "Tagline" with "Slogan" across the admin UI. -
Updated dependencies []:
- @emdash-cms/[email protected]
Breaking Changes
- `POST /menus/:name/items` no longer accepts unknown keys silently; now returns 400 `VALIDATION_ERROR` for unrecognized keys.
- Per‑item writes moved to path‑style endpoints: `PUT /menus/:name/items/:id` and `DELETE /menus/:name/items/:id`; legacy query‑string form removed.
- Menu and menu‑item API responses switched from snake_case to camelCase (e.g., `created_at` → `createdAt`).
Weekly OSS security release digest.
The CVE patches and breaking changes that affected production tools this week. One email, every Sunday.
No spam, unsubscribe anytime.
Share this release
About emdash
All releases →Related context
Earlier breaking changes
- [email protected] Schema migration adds `locale` and `translation_group` columns to `_emdash_bylines`.
- [email protected] Byline hydration now strictly per-locale, suppressing cross‑locale fallback.
- v@emdash-cms/[email protected] Changes `_emdash_content_bylines.byline_id` to store translation_group instead of row id, enforcing strict per-locale credit hydration.
- v@emdash-cms/[email protected] Registry install handler fails closed on non-conforming aggregator release records.
- v@emdash-cms/[email protected] Removes the `atprotoPlugin` named export and factory call shape.
Beta — feedback welcome: [email protected]