Skip to content

Docs: unblock local docs build without AI Search#1107

Open
richardjoo wants to merge 5 commits into
emdash-cms:mainfrom
richardjoo:split/docs-cloudflare-build
Open

Docs: unblock local docs build without AI Search#1107
richardjoo wants to merge 5 commits into
emdash-cms:mainfrom
richardjoo:split/docs-cloudflare-build

Conversation

@richardjoo
Copy link
Copy Markdown

What does this PR do?

Fixes the docs app so local/built Worker preview and docs builds do not require a live Cloudflare AI Search binding.

Closes #

  • Adds a local-only Wrangler config for docs Worker preview.
  • Adds an MCP fallback response when AI_SEARCH is not configured.
  • Uses a custom Starlight 404 page and disables the injected route that was producing build-time lookup noise.
  • Switches the Cloudflare adapter config to avoid remote binding dependency during docs builds.
  • Clarifies the docs README for the built-worker preview flow.

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). Do not include messages.po changes except in translation PRs — a workflow extracts catalogs on merge to main.
  • I have added a changeset (if this PR changes a published package)
  • New features link to an approved Discussion: https://github.com/emdash-cms/emdash/discussions/...

AI-generated code disclosure

  • This PR includes AI-generated code — model/tool: GPT-5.4 / OpenCode

Screenshots / test output

  • pnpm --dir docs build
  • prettier --check on changed docs files

Copilot AI review requested due to automatic review settings May 19, 2026 19:11
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 19, 2026

⚠️ No Changeset found

Latest commit: f57600f

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

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR updates the docs app to build and run local/built Worker previews without requiring a live Cloudflare AI Search binding, while preserving the production AI Search-backed MCP behavior when configured.

Changes:

  • Add a local-only Wrangler config (wrangler.local.jsonc) that omits the ai_search binding for local/built preview use.
  • Add an MCP /mcp tool fallback in the docs Worker when AI_SEARCH is not configured.
  • Customize 404 handling by disabling Starlight’s injected 404 route and adding a prerendered src/pages/404.astro, and adjust the Cloudflare adapter config to avoid remote binding dependency during builds.
  • Clarify the docs README for the local built-worker preview flow vs. production-binding previews.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
docs/wrangler.local.jsonc Adds a Wrangler config for local preview that doesn’t require AI Search bindings.
docs/src/worker.ts Adds a safe MCP tool fallback when AI_SEARCH is missing; uses a local aiSearch reference for search calls.
docs/src/pages/404.astro Introduces a custom prerendered Starlight 404 page.
docs/README.md Documents the local-only Wrangler preview flow and notes MCP behavior without AI Search.
docs/astro.config.mjs Disables Starlight’s 404 route and configures the Cloudflare adapter to avoid remote bindings during builds.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread docs/src/worker.ts Outdated
Comment on lines +18 to +24
server.registerTool(
"search_docs",
{
title: "Search EmDash documentation",
description:
"Search the EmDash CMS documentation. This endpoint is not configured in this environment.",
inputSchema: {
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

This is a maintainability issue rather than a functional bug, but I agree that the duplicated tool metadata could drift over time. I can factor the shared search_docs tool definition into a single shared spec and only branch the handler behavior based on AI_SEARCH availability.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I refactored this so the search_docs tool definition is shared in one place and only the handler implementation varies based on AI_SEARCH availability.

That removes duplicate schema/metadata and prevents drift between the fallback and AI-enabled paths.

Validation I ran after the fix:

  • prettier --check docs/src/worker.ts
  • pnpm --dir docs build

@ascorbic
Copy link
Copy Markdown
Collaborator

/review

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented May 19, 2026

Open in StackBlitz

@emdash-cms/admin

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

@emdash-cms/auth

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

@emdash-cms/blocks

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

@emdash-cms/cloudflare

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

emdash

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

create-emdash

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

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

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

@emdash-cms/x402

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

@emdash-cms/plugin-ai-moderation

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

@emdash-cms/plugin-atproto

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

@emdash-cms/plugin-audit-log

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

@emdash-cms/plugin-color

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

@emdash-cms/plugin-embeds

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

@emdash-cms/plugin-forms

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

@emdash-cms/plugin-webhook-notifier

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

commit: f57600f

Copy link
Copy Markdown
Contributor

@ask-bonk ask-bonk Bot left a comment

Choose a reason for hiding this comment

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

Scoped, low-risk docs build fix. The intent is clear and the changes hang together: skip AI Search at build/prerender time and degrade gracefully at runtime when the binding is absent. Both disable404Route and Astro.locals.t("404.text") are valid Starlight APIs, and remoteBindings/prerenderEnvironment are valid @astrojs/cloudflare adapter options. The Copilot-recommended dedupe of searchDocsTool has already been applied.

A few things worth thinking about before merge:

  1. The adapter changes in astro.config.mjs affect production builds too, not just local. That's almost certainly fine here (docs are fully static; nothing prerendered touches env.AI_SEARCH), but it is a real behavior change for the production deploy pipeline -- worth confirming the next CI deploy still produces what you expect.
  2. Env.AI_SEARCH is typed as non-optional in worker-configuration.d.ts, so the if (!aiSearch) guard works at runtime but doesn't actually narrow at the type level. Functionally fine; cosmetically a bit fuzzy.
  3. wrangler.local.jsonc duplicates compatibility_date, compatibility_flags, name, main, assets, and observability from wrangler.jsonc. They will drift. Adding a brief comment that mirrors this restriction (or a README pointer) would help future-you.

No blockers.

Comment thread docs/astro.config.mjs
],

adapter: cloudflare(),
adapter: cloudflare({ remoteBindings: false, prerenderEnvironment: "node" }),
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.

These two options apply to every build (local, CI, and the production deploy), not just the local-only flow described in the README. That's almost certainly what you want -- the docs site is fully static and nothing prerendered touches env.AI_SEARCH -- but the PR description frames this as "avoid remote binding dependency during docs builds" which slightly understates the scope.

Worth verifying the next prod deploy still works as expected (especially that prerenderEnvironment: "node" doesn't regress any workerd-specific behavior the prior config relied on). If the goal was strictly local-only, the alternative would be conditional config via an env var, but that's probably overkill here.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

This does affect all docs builds, not just local preview, and that is intentional here. The docs site is fully static, and nothing in prerender depends on workerd-only behavior or env.AI_SEARCH, so using prerenderEnvironment: "node" and remoteBindings: false for the docs app is the desired behavior rather than a local-only toggle.

Comment thread docs/src/worker.ts
version: "1.0.0",
});
const aiSearch = env.AI_SEARCH;
if (!aiSearch) {
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.

Env.AI_SEARCH is typed as AiSearchInstance (non-optional) in worker-configuration.d.ts, so from TypeScript's perspective this guard is dead code -- aiSearch is always truthy. At runtime, however, the binding really is undefined when running under wrangler.local.jsonc, so the check does work; it's just not type-narrowed.

If you want the type system to reflect reality, you could type-narrow with an explicit cast or local declaration, e.g.:

Suggested change
if (!aiSearch) {
const aiSearch = (env as { AI_SEARCH?: typeof env.AI_SEARCH }).AI_SEARCH;
if (!aiSearch) {

Not a blocker -- behavior is correct as-is.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I updated this so the runtime fallback matches the type story more closely by reading AI_SEARCH through an explicit optional cast before the guard.

That keeps the fallback behavior the same at runtime while making it clearer that the binding may be absent in the local-only Wrangler config.

Comment thread docs/wrangler.local.jsonc
@@ -0,0 +1,14 @@
{
"$schema": "node_modules/wrangler/config-schema.json",
"compatibility_date": "2026-03-01",
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 config duplicates compatibility_date, compatibility_flags, name, main, assets, and observability from wrangler.jsonc. If any of those change in the primary config (especially compatibility_date and compatibility_flags), this file will silently drift and local previews will diverge from production behavior.

A short comment at the top noting "keep in sync with wrangler.jsonc" -- or a checklist line in docs/README.md -- would save a future debugging session. Not blocking.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Agreed. I added a short sync note at the top of wrangler.local.jsonc so it’s clearer that the shared fields should stay aligned with wrangler.jsonc.

Comment thread docs/src/pages/404.astro
editUrl: false,
pagefind: false,
hero: {
tagline: Astro.locals.t("404.text"),
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.

Confirmed Astro.locals.t("404.text") is the documented Starlight i18n helper (see @astrojs/starlight/locals.d.ts) and 404.text is one of the built-in translation keys, so this works even though the site doesn't configure custom i18n. Just flagging for posterity.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Thanks for confirming.

@ask-bonk
Copy link
Copy Markdown
Contributor

ask-bonk Bot commented May 19, 2026

Review posted successfully. Summary of the review:

  • Verdict: Scoped, low-risk docs build fix with no blockers.
  • Validated: disable404Route, Astro.locals.t("404.text"), and the Cloudflare adapter options (remoteBindings, prerenderEnvironment) are all real APIs; the dedupe of searchDocsTool Copilot asked for was already applied.
  • Flagged for awareness:
    1. astro.config.mjs changes apply to all builds including production deploys, not just local — worth confirming the next prod CI run still works.
    2. Env.AI_SEARCH is non-optional in the generated worker types, so the if (!aiSearch) guard works at runtime but doesn't type-narrow; included a suggestion.
    3. wrangler.local.jsonc duplicates fields from wrangler.jsonc and will silently drift — recommended a sync comment or README note.
    4. Confirmed the 404 i18n key resolves via Starlight's bundled translations.

github run

Copy link
Copy Markdown
Collaborator

@ascorbic ascorbic left a comment

Choose a reason for hiding this comment

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

Thanks!

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.

3 participants