* fix(admin): avoid locally caught exception in PasskeyLogin
Why:
The passkey login flow threw an error when navigator.credentials.get()
returned no credential, but that exception was immediately caught by the
local catch block in the same function. This triggered a
"throw of exception caught locally" warning and made the failure path
less direct.
What:
- Handle the missing credential case inline instead of throwing locally
- Preserve the existing error message shown to the user
- Keep the onError callback behavior unchanged
* Add changeset
---------
Co-authored-by: Matt Kane <m@mk.gg>
Co-authored-by: Matt Kane <mkane@cloudflare.com>
* fix: allow public access to search and suggest API endpoints
The search and suggest endpoints required search:read permission,
blocking unauthenticated visitors from using the LiveSearch component.
This is safe because the search query layer defaults to status=published,
so public users only see published content.
Fixes#104
* fix: add changeset for public search API access
Patch bump for emdash - allows public access to search API endpoints.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Co-authored-by: Matt Kane <mkane@cloudflare.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Add Back navigation to Security and Domain settings pages
* changeset
---------
Co-authored-by: Matt Kane <mkane@cloudflare.com>
Co-authored-by: Matt Kane <m@mk.gg>
* 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>
* fix: passkeys behind TLS reverse proxy
Add passkeyPublicOrigin and wire it through passkey routes so origin/rpId match
the browser when dev runs behind nginx. Expose dev-only /_emdash/api/dev/passkey-url,
add admin messaging for insecure WebAuthn contexts, nginx repro under demos/simple,
and direct kysely dependency for the simple demo Node adapter bundle.
Made-with: Cursor
* docs: add passkeyPublicOrigin to configuration reference
Adds the new passkeyPublicOrigin option and reverse proxy guidance
to the public-facing configuration docs as requested in PR review.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* update tests and more docs
* fix: add missing refresh-server-pat fixture and restore docs heading
---------
Co-authored-by: Joseph Eftekhari <jdeftekhari@gmail.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(admin): use collection urlPattern for preview button fallback URL
The preview button hardcoded fallback URLs as /${collection}/${slug},
ignoring the collection's urlPattern setting. Collections with custom
URL patterns (e.g. urlPattern: "/biljke/{slug}" on a "biljka" collection)
would open a 404 instead of the correct page.
Thread urlPattern through the manifest and use it in the ContentEditor
preview fallback.
Fixes#167
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Filip Ilic <ilic.filip@gmail.com>
* chore: add changeset for preview URL pattern fix
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Filip Ilic <ilic.filip@gmail.com>
---------
Signed-off-by: Filip Ilic <ilic.filip@gmail.com>
Co-authored-by: Matt Kane <mkane@cloudflare.com>
* implement safe url validation for redirects
added validation for safe redirect urls to prevent xss attacks
* add changeset: fix dom xss in form redirects
(marked as patch version bump for @emdash-cms/plugin-forms)
---------
Co-authored-by: Matt Kane <mkane@cloudflare.com>
* docs: fix critical errors in import paths, types, and API references
- Fix Cloudflare adapter imports: d1/r2 come from @emdash-cms/cloudflare, not emdash/db or emdash/astro
- Fix PortableText import path: emdash/ui, not emdash/astro
- Replace set:html with PortableText component for Portable Text content
- Fix CLI binary alias: em, not ec
- Fix media upload API: POST multipart to /api/media, not JSON to /api/media/upload
- Fix MediaValue type: src not url, provider is optional, add previewUrl
- Fix EmDashMedia to Image component (actual export name)
- Fix Cloudflare Access auth config: use access() function, not nested object
- Fix REST API methods: content/media update is PUT not PATCH, settings is POST not PUT
- Fix contributing docs: Node.js 22+, pnpm 10+, correct E2E test command
- Fix WordPress migration: remove undocumented CLI import command
* docs: fix high-priority technical errors across docs
- Fix hook names: beforeSave/afterSave, not beforeCreate/afterCreate
- Fix status values: draft/published/scheduled, not archived
- Fix field type count: 14, not 15
- Fix MCP tool count: 33, not 28
- Fix Section.previewUrl type: string, not object
- Fix getSections examples to show { items } destructuring
- Add missing CollectionSupport values: search, seo
- Update reserved field slugs to match actual code
- Add MCP server enablement note (mcp: true required)
- Clarify getStaticPaths guidance: themes must be SSR, other sites can use static
- Delete orphaned duplicate migration/plugin-porting.mdx
* docs: fix medium-priority issues across docs
- Fix broken internal links: /guides/media/ -> /guides/media-library/, /guides/seeding/ -> /themes/seed-files/
- Standardize env var to EMDASH_PREVIEW_SECRET throughout preview guide
- Fix featuredImage -> featured_image in widgets guide
- Remove Discord social link (no Discord server exists)
- Fix formatting config reference: .oxfmtrc.json, not .prettierrc
- Add audienceEnvVar to Cloudflare Access config options
- Fix content model type declarations to show actual return types
* docs: document missing plugin hooks, capabilities, and context properties
- Add 10 missing hooks to reference: cron, email (beforeSend, deliver, afterSend),
comment (beforeCreate, moderate, afterCreate, afterModerate), page (metadata, fragments)
- Document all hook event types, handler signatures, and return values
- Add exclusive hook option to configuration tables
- Add 6 missing capabilities: network:fetch:any, read:users, email:send/provide/intercept, page:inject
- Add 6 missing context properties: ctx.site, ctx.url(), ctx.users, ctx.cron, ctx.email
- Update hooks reference tables in both plugins/hooks.mdx and reference/hooks.mdx
* Format
* perf(db): optimize D1 indexes to eliminate full table scans
- Add composite indexes to ec_* tables for common query patterns
- Replace single-column indexes with (deleted_at, updated_at, id) composite
- Add (deleted_at, status) index for count queries
- Add (deleted_at, created_at, id) index for chronological ordering
- Optimize comment counting with partial indexes per status
- Rewrite countByStatus() to use parallel WHERE queries instead of GROUP BY
Fixes#131
* chore: add changeset for D1 index optimization
* style: wrap changeset description for formatting compliance
Replace placeholder text branding ("— EmDash") with actual logo SVGs
from the brand assets. Adds Logo.tsx with LogoIcon (icon mark) and
LogoLockup (icon + wordmark) components.
- Sidebar: gradient icon mark replaces em dash text character
- Login, Signup, Setup: full lockup SVG with currentColor wordmark
- Welcome modal: logo icon replaces Sparkle placeholder
- Favicon: real gradient icon SVG replaces emoji
On a fresh CF deployment, if the first request hits a public page, the
middleware fast-path skips runtime init. Template helpers like
getSiteSettings() then query an empty database and crash with
'no such table: options'.
Add a one-time setup probe in the middleware fast-path: check if the
migrations table exists, and redirect to the setup wizard if not.
The check is cached for the worker lifetime after first success.
Also includes release workflow update to use GitHub App token and
admin branding changeset.