Skip to content

Enable page:metadata hooks for anonymous visitors on public pages#169

Open
jdevalk wants to merge 1 commit intoemdash-cms:mainfrom
jdevalk:fix/page-metadata-anonymous-visitors
Open

Enable page:metadata hooks for anonymous visitors on public pages#169
jdevalk wants to merge 1 commit intoemdash-cms:mainfrom
jdevalk:fix/page-metadata-anonymous-visitors

Conversation

@jdevalk
Copy link
Copy Markdown

@jdevalk jdevalk commented Apr 3, 2026

Summary

  • Initialize the runtime for anonymous visitors on public pages so page:metadata and page:fragments plugin hooks fire for all visitors
  • Only attaches the two page contribution methods to locals.emdash (not the full admin object), preserving the existing performance optimization
  • The runtime is a cached singleton — after first init, getRuntime() is just a null-check, so the per-request cost is negligible

Problem

The middleware skips runtime initialization for anonymous visitors on public pages (lines 200–228). This means EmDashHead's getPageRuntime() returns undefined, and it falls back to base-only SEO contributions. Plugin page:metadata hooks never fire for search engine crawlers or regular visitors.

Fix

After the existing setup check, initialize the cached runtime and attach just collectPageMetadata and collectPageFragments to locals.emdash. This satisfies the structural check in getPageRuntime() so EmDashHead runs plugin hooks for all visitors.

Test plan

  • Verify anonymous visitors see plugin-contributed meta tags in <head>
  • Verify logged-in editors still see the full admin toolbar and all existing functionality
  • Verify performance is not impacted (runtime is cached singleton after first request)
  • Verify EmDashBodyStart/EmDashBodyEnd work correctly with the minimal locals object

Fixes #166

🤖 Generated with Claude Code

The middleware previously skipped runtime initialization entirely for
anonymous visitors on public pages, which meant page:metadata plugin
hooks never fired for the audience that matters most — search engine
crawlers and regular visitors.

The runtime is a cached singleton (created once, reused across all
requests), so the per-request cost of calling getRuntime() is just a
null-check. This change attaches only the two page contribution methods
(collectPageMetadata and collectPageFragments) to locals.emdash for
anonymous visitors, keeping the optimization of not constructing the
full admin locals object.

This enables SEO plugins using the page:metadata hook to contribute
meta tags, canonical URLs, robots directives, and JSON-LD schema
markup for all visitors.

Fixes emdash-cms#166

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Apr 3, 2026

⚠️ No Changeset found

Latest commit: 2ea1fc0

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 3, 2026

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

@jdevalk
Copy link
Copy Markdown
Author

jdevalk commented Apr 3, 2026

I have read the CLA Document and I hereby sign the CLA

github-actions bot added a commit that referenced this pull request Apr 3, 2026
@blmyr
Copy link
Copy Markdown

blmyr commented Apr 4, 2026

Hey @jdevalk! Just a heads up — I have an open PR (#119) that fixes the same issue (#118, which is a duplicate of #166). My PR takes a slightly different approach by restructuring the middleware to run the full runtime init for anonymous visitors (skipping only the manifest query), and includes tests and a changeset.

@ascorbic — two independent PRs for the same bug here. Would be great if you could take a look and decide which approach you'd prefer, or if there's a combination of both that works best. Happy to adjust ours either way!

blmyr added a commit to blmyr/emdash that referenced this pull request Apr 5, 2026
Rework the middleware approach based on CI feedback and PR emdash-cms#169's
strategy: keep the anonymous fast-path (avoiding D1 session handling
for public pages) but initialize the runtime inside it for page hooks.

Changes:
- Restore the anonymous fast-path from main (fixes Cloudflare Workers
  cross-request promise issue that caused smoke test failure)
- Initialize runtime inside the fast-path with try-catch for graceful
  degradation — EmDashHead falls back to base SEO if init fails
- Only bind collectPageMetadata/collectPageFragments for anonymous
  requests (not the full admin handler object)
- Add page contribution methods to doInit() path for admin/editor
  routes too
- Fix test DB handle leak (close better-sqlite3 handle in afterEach)
- Pass { db } consistently to HookPipeline in all test cases

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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.

page:metadata plugin hooks don't run for anonymous visitors on public pages

2 participants