Files
moreminimore-redesign/packages/core
Benjamin Price 8c693b582d fix: prevent media upload OOM on Workers for large images (#262)
* fix: prevent media upload OOM on Workers via client thumbnails + server safety net

Large image uploads (5MB+) crash Cloudflare Workers (128MB limit) because
generatePlaceholder() decodes entire images to raw RGBA pixels. A 4000x3000
JPEG becomes ~48MB RGBA, exceeding the isolate memory budget.

Two-layer fix:
- Client-side: browser generates a 64px canvas thumbnail for oversized images
  and sends it alongside the upload. Server generates blurhash from the
  thumbnail (~16KB RGBA) instead of decoding the full image.
- Server-side: reads dimensions from image headers via image-size and skips
  placeholder generation when estimated decoded size exceeds 32MB. This covers
  API/CLI uploads that don't provide thumbnails.

* chore: add changeset for media upload OOM fix

* fix: clamp upload thumbnail to 64x64 box for extreme aspect ratios

Naive sizing (thumbW=64, thumbH=(h/w)*64) could produce an enormous canvas
for very tall or very wide images — e.g. a 100x840000 image would allocate
a 64x537600 canvas client-side, reintroducing the memory blowup this feature
exists to prevent.

Extract computeThumbnailSize() that fits the image within a 64x64 box by
scaling against max(width, height), wrap canvas allocation and drawImage
in try/catch with a no-thumbnail fallback, and add unit tests covering
extreme aspect ratios.

---------

Co-authored-by: Matt Kane <mkane@cloudflare.com>
2026-04-06 07:14:39 +00:00
..
2026-04-01 15:02:06 +00:00
2026-04-01 10:58:32 +01:00
2026-04-01 10:46:10 +01:00
2026-04-01 10:44:22 +01:00
2026-04-01 10:44:22 +01:00
2026-04-01 10:44:22 +01:00

emdash

The core EmDash CMS package - an Astro-native, agent-portable reimplementation of WordPress.

Installation

npm install emdash

Features

  • Content Management - Collections, fields, Live Collections integration
  • Media Library - Upload via signed URLs, S3-compatible storage
  • Full-Text Search - FTS5 with Porter stemming, per-collection config
  • Navigation Menus - Hierarchical menus with URL resolution
  • Taxonomies - Categories, tags, custom taxonomies
  • Widget Areas - Content, menu, and component widgets
  • Sections - Reusable content blocks
  • Plugin System - Hooks, storage, settings, admin pages
  • WordPress Import - WXR, REST API, WordPress.com

Quick Start

// astro.config.mjs
import { defineConfig } from "astro/config";
import emdash, { local } from "emdash/astro";
import { sqlite } from "emdash/db";

export default defineConfig({
	integrations: [
		emdash({
			database: sqlite({ url: "file:./data.db" }),
			storage: local({
				directory: "./uploads",
				baseUrl: "/_emdash/api/media/file",
			}),
		}),
	],
});
// src/live.config.ts
import { defineLiveCollection } from "astro:content";
import { emdashLoader } from "emdash/runtime";

export const collections = {
	_emdash: defineLiveCollection({ loader: emdashLoader() }),
};

API

import {
	getEmDashCollection,
	getEmDashEntry,
	getSiteSettings,
	getMenu,
	getTaxonomyTerms,
	getWidgetArea,
	search,
} from "emdash";

// Content
const { entries } = await getEmDashCollection("posts");
const { entry } = await getEmDashEntry("posts", "hello-world");

// Site settings
const settings = await getSiteSettings();

// Navigation
const menu = await getMenu("primary");

// Taxonomies
const categories = await getTaxonomyTerms("categories");

// Widgets
const sidebar = await getWidgetArea("sidebar");

// Search
const results = await search("hello world", { collections: ["posts"] });

Documentation

See the documentation site for guides, API reference, and plugin development.