Fix local media ID normalization#1100
Conversation
🦋 Changeset detectedLatest commit: ccda3a3 The changes in this PR will be included in the next version bump. This PR includes changesets to release 13 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
@emdash-cms/admin
@emdash-cms/auth
@emdash-cms/blocks
@emdash-cms/cloudflare
emdash
create-emdash
@emdash-cms/gutenberg-to-portable-text
@emdash-cms/x402
@emdash-cms/plugin-ai-moderation
@emdash-cms/plugin-atproto
@emdash-cms/plugin-audit-log
@emdash-cms/plugin-color
@emdash-cms/plugin-embeds
@emdash-cms/plugin-forms
@emdash-cms/plugin-webhook-notifier
commit: |
|
/review |
There was a problem hiding this comment.
Targeted, well-tested fix. The new resolveLocalId helper mirrors the existing resolveInternalUrl shape, and the fallback ordering (internal URL → http(s) external → local ID → external fallback) is sensible. A few minor observations below — none blocking.
| id: "", | ||
| src: url, | ||
| }); | ||
| }; |
There was a problem hiding this comment.
Minor: the existing test suite covers the happy path for a resolved local ID, but doesn't have a dedicated case for the new fallback — a bare non-URL string that the local provider doesn't recognize (or where no local provider is registered) still ending up as { provider: "external", id: "", src: "…" }. The behavior is correct, just untested. Worth adding an explicit case so a future refactor of resolveLocalId can't silently regress this.
| }; | ||
| } | ||
|
|
||
| async function resolveLocalId( |
There was a problem hiding this comment.
Heads-up (not a blocker for this PR): resolveInternalUrl above passes the URL suffix (storage key) to localProvider.get(...), while MediaRepository.findById queries by id. So for any internal URL where the storage key differs from the row id (e.g. 01ABC.jpg vs row id 01ABC), the lookup will return null and fall back to external — the test at line 76 of normalize.test.ts only passes because the mock returns the provider item regardless of the argument. resolveLocalId does the right thing (querying by id), but the existing internal-URL path looks suspect. Probably worth a follow-up issue.
| * - `null`/`undefined` → `null` | ||
| * - Bare URL string → `{ provider: "external", id: "", src: url }` | ||
| * - Bare internal media URL → resolved via local provider's `get()` | ||
| * - Bare local media ID → resolved via local provider's `get()` |
There was a problem hiding this comment.
Tiny nit: since the local-ID resolution only happens for strings that are neither an internal URL nor an http(s) URL, the JSDoc could be a hair more explicit about ordering — e.g. Bare local media ID (only when not a URL) → resolved via local provider's get(). Not blocking.
|
Review posted successfully. Summary:
|
Restores the two changesets removed in #1105 (which existed only to flip changesets/action onto the publish path for the already-versioned 0.13.0). With 0.13.0 now published, these re-enter the version path: the next Release run bumps 0.13.0 -> 0.13.1 and refreshes the changeset-release PR (#1103), whose merge publishes 0.13.1 with the #1100/#1101 fixes documented. Files are byte-identical to their pre-#1105 state.
#1110 reintroduced these changesets, so changelog-github credited both to #1110/@ascorbic, dropping @jcheese1's credit for #1100. Add per-changeset pr/commit/author overrides (read by @changesets/changelog-github) so the regenerated release PR attributes #1100 to @jcheese1 and #1101 to @ascorbic with the original commits. Editing #1103 directly wouldn't survive the next force-push of changeset-release/main.
What does this PR do?
Fixes media normalization so bare local media IDs are resolved through the local media provider before falling back to external URL behavior.
This matters for API/MCP-style writes that create media first, then use the returned media ID directly in an image field:
{ "featured_image": "01KRZKN0BK219P9HBMPYYGYRHY" }Previously, EmDash stored that as an external image value:
{ "provider": "external", "id": "", "src": "01KRZKN0BK219P9HBMPYYGYRHY" }That produced broken rendered image URLs. With this change, EmDash checks whether the bare string resolves as local media and stores it as a local media value. Unknown strings still keep the previous external fallback behavior.
Closes #
Type of change
Checklist
pnpm typecheckpassespnpm lintpassespnpm testpasses (or targeted tests for my change)pnpm formathas been runmessages.pochanges except in translation PRs — a workflow extracts catalogs on merge tomain.AI-generated code disclosure
Screenshots / test output
Targeted test passed:
Note: the pnpm wrapper currently exits before running tests with:
So I validated the touched test file directly with Vitest.