Skip to content

fix: delegate HEIC media to configured image services#2175

Open
masonjames wants to merge 6 commits into
emdash-cms:mainfrom
masonjames:codex/heic-media-support
Open

fix: delegate HEIC media to configured image services#2175
masonjames wants to merge 6 commits into
emdash-cms:mainfrom
masonjames:codex/heic-media-support

Conversation

@masonjames

@masonjames masonjames commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Removes the browser-side HEIC/WASM conversion and makes the storage-backed /_image endpoint use the configured image infrastructure instead.

On Node, local Astro image services continue transforming storage bytes directly, while external Astro services receive the storage adapter's public URL through their validateOptions/getURL contract. External services opt into HEIC input by listing heic or heif in image.service.config.supportedInputFormats; EmDash also requires a rewritten transform URL and a genuinely public storage source. On Cloudflare Workers, the existing Images binding remains the transformer and is detected automatically.

When no capable path is configured, direct, signed, and MCP HEIC uploads return 415 UNSUPPORTED_IMAGE_FORMAT before a broken library item is stored. Signed uploads that become unsupported before confirmation are deleted on a best-effort basis and marked failed. Existing storage-backed media with public R2/S3 URLs now builds admin thumbnails through /_image using its storage key.

Related issue/discussion: none; this follows the maintainer review on this PR.

Type of change

  • Bug fix
  • Feature (requires maintainer-approved Discussion)
  • Refactor (no behavior change)
  • Translation
  • Documentation
  • Performance improvement
  • Tests
  • Chore (dependencies, CI, tooling)

Checklist

  • I have read CONTRIBUTING.md
  • pnpm typecheck passes
  • pnpm lint passes
  • pnpm test passes (or targeted tests for my change)
  • pnpm format has been run
  • I have added/updated tests for my changes (if applicable)
  • User-visible strings in the admin UI are wrapped for translation (if applicable). No admin UI strings or catalog files were added.
  • I have added a changeset (if this PR changes a published package)
  • New features link to an approved Discussion: n/a — this remains a bug fix for an already-accepted media type.

AI-generated code disclosure

  • This PR includes AI-generated code — model/tool: OpenAI Codex (GPT-5)

Screenshots / test output

Production verification on masonjames.com (Astro Node + S3-compatible R2 storage + Cloudflare image resizing):

  • EmDash /_image redirects the existing HEIC storage object to the configured external service and returns a 400×300 WebP (44,796 bytes).
  • The same path returns a 400×223 WebP (22,170 bytes) for an existing JPEG.
  • Both redirect responses use Cache-Control: public, max-age=31536000, immutable.
  • The exact vendored revision passed the site's typecheck, build, startup/route smoke tests, and production Docker build; the immutable deployment converged and both public health URLs returned 200.

Repository validation:

  • pnpm format:check
  • pnpm lint:json | jq '.diagnostics | length'0
  • pnpm lint
  • pnpm typecheck — 31 package projects
  • TMPDIR=/private/tmp pnpm --filter emdash test — 386 files passed, 1 skipped; 4,996 tests passed, 3 skipped
  • pnpm --filter @emdash-cms/cloudflare test — 17 files, 232 tests passed
  • pnpm --filter @emdash-cms/admin exec vitest run tests/lib/media-thumbnail.test.ts — 8 tests passed
  • pnpm --filter emdash build
  • pnpm --filter @emdash-cms/cloudflare build
  • pnpm --filter @emdash-cms/admin build

The canonical TMPDIR is used because the repository's sandbox watcher assertion otherwise compares macOS's /var/folders spelling with its /private/var/folders realpath; no product test fails under the canonical path.

@changeset-bot

changeset-bot Bot commented Jul 21, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 7ef2d0e

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 17 packages
Name Type
@emdash-cms/cloudflare Patch
emdash Patch
@emdash-cms/fixture-perf-site Patch
@emdash-cms/perf-demo-site Patch
@emdash-cms/cache-demo-site Patch
@emdash-cms/do-demo-site Patch
@emdash-cms/do-solo-demo-site Patch
@emdash-cms/sandbox-workerd Patch
@emdash-cms/plugin-mcp-smoke Patch
@emdash-cms/admin Patch
@emdash-cms/auth Patch
@emdash-cms/blocks Patch
@emdash-cms/gutenberg-to-portable-text Patch
@emdash-cms/x402 Patch
create-emdash Patch
@emdash-cms/auth-atproto Patch
@emdash-cms/plugin-embeds Patch

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

@pkg-pr-new

pkg-pr-new Bot commented Jul 21, 2026

Copy link
Copy Markdown

Open in StackBlitz

@emdash-cms/admin

npm i https://pkg.pr.new/@emdash-cms/admin@2175

@emdash-cms/auth

npm i https://pkg.pr.new/@emdash-cms/auth@2175

@emdash-cms/auth-atproto

npm i https://pkg.pr.new/@emdash-cms/auth-atproto@2175

@emdash-cms/blocks

npm i https://pkg.pr.new/@emdash-cms/blocks@2175

@emdash-cms/cloudflare

npm i https://pkg.pr.new/@emdash-cms/cloudflare@2175

@emdash-cms/contentful-to-portable-text

npm i https://pkg.pr.new/@emdash-cms/contentful-to-portable-text@2175

emdash

npm i https://pkg.pr.new/emdash@2175

create-emdash

npm i https://pkg.pr.new/create-emdash@2175

@emdash-cms/gutenberg-to-portable-text

npm i https://pkg.pr.new/@emdash-cms/gutenberg-to-portable-text@2175

@emdash-cms/plugin-cli

npm i https://pkg.pr.new/@emdash-cms/plugin-cli@2175

@emdash-cms/plugin-types

npm i https://pkg.pr.new/@emdash-cms/plugin-types@2175

@emdash-cms/registry-client

npm i https://pkg.pr.new/@emdash-cms/registry-client@2175

@emdash-cms/registry-lexicons

npm i https://pkg.pr.new/@emdash-cms/registry-lexicons@2175

@emdash-cms/registry-verification

npm i https://pkg.pr.new/@emdash-cms/registry-verification@2175

@emdash-cms/sandbox-workerd

npm i https://pkg.pr.new/@emdash-cms/sandbox-workerd@2175

@emdash-cms/x402

npm i https://pkg.pr.new/@emdash-cms/x402@2175

@emdash-cms/plugin-ai-moderation

npm i https://pkg.pr.new/@emdash-cms/plugin-ai-moderation@2175

@emdash-cms/plugin-atproto

npm i https://pkg.pr.new/@emdash-cms/plugin-atproto@2175

@emdash-cms/plugin-audit-log

npm i https://pkg.pr.new/@emdash-cms/plugin-audit-log@2175

@emdash-cms/plugin-color

npm i https://pkg.pr.new/@emdash-cms/plugin-color@2175

@emdash-cms/plugin-embeds

npm i https://pkg.pr.new/@emdash-cms/plugin-embeds@2175

@emdash-cms/plugin-field-kit

npm i https://pkg.pr.new/@emdash-cms/plugin-field-kit@2175

@emdash-cms/plugin-forms

npm i https://pkg.pr.new/@emdash-cms/plugin-forms@2175

@emdash-cms/plugin-webhook-notifier

npm i https://pkg.pr.new/@emdash-cms/plugin-webhook-notifier@2175

commit: 7ef2d0e

@masonjames
masonjames marked this pull request as ready for review July 21, 2026 13:58
@github-actions github-actions Bot added the review/needs-review No maintainer or bot review yet label Jul 21, 2026

@emdashbot emdashbot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a sensible, well-scoped bug fix: HEIC/HEIF files are undisplayable in browsers, so converting them client-side before they hit the existing local-media upload flow is the right layer to solve it. The change is additive (no break), the CSP-compatible heic-to/csp import is appropriate, the changeset is user-facing, and no user-visible strings were added. Tests cover conversion behavior, browser renderability, and end-to-end flow through the media API.

I read the changed files and traced the two call-sites (MediaLibrary and MediaPickerModal). The only meaningful gap is that the same HEIC fix is not applied to uploadToProvider, so external-provider uploads can still store an undisplayable original. The rest of the code looks solid.

Comment thread packages/admin/src/lib/api/media.ts Outdated
Comment thread packages/admin/src/lib/media-upload.ts Outdated
@github-actions github-actions Bot added review/needs-rereview Author pushed changes since the last review size/L and removed review/needs-review No maintainer or bot review yet size/M labels Jul 21, 2026
@ascorbic

Copy link
Copy Markdown
Collaborator

I'm a bit reluctant to add a big new WASM dependency for this. It seems like something the image server should be handling.

@masonjames masonjames changed the title fix(admin): convert HEIC uploads for browser display fix: delegate HEIC media to configured image services Jul 22, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Overlapping PRs

This PR modifies files that are also changed by other open PRs:

This may cause merge conflicts or duplicated work. A maintainer will coordinate.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants