Files
next-ai-draw-io/docs/en/cloudflare-deploy.md
T
Dayuan Jiang 60105ddb51 refactor: remove dead code and over-engineering found by two audits (#986)
## Problem

Two audits of the repository (a workflow of finder, dedup and per-file verifier agents, run on 2026-10-11 against 1f4141e and again against 07f3e3c after a first round of refactoring) found dead code and features that were more machinery than the app needs: an Electron configuration-preset system with its own settings window, three copies of the about page, a hand-written `.env` parser, an "extended" system prompt nobody selected, two data migrations for formats from 2025-12 and 2026-01, a server-side example cache, type-to-confirm provider deletion, 81 unused dictionary keys, wrapper components around single call sites, and a long tail of unused exports, props, options and tests.

## Change

Every item below was either approved one by one or covered by "fix all of the others".

- **Electron:** the configuration-preset system is gone (settings window, preload, IPC, menu entry, dev watcher); `config-manager.ts` keeps only `userLocale`; `.env` files are parsed with `dotenv`. Entitlements file and a duplicate CI workflow removed; `test.yml` gets a type check and concurrency.
- **Server:** no extended prompt, no example cache, no `ENABLE_HISTORY_XML_REPLACE`, no message metadata or `hasConfig`; the connection test is one shared helper; EdgeOne loses its mock branch and model aliases; `DEBUG_LLM_PAYLOAD` logs message structure instead of payloads; Langfuse scoring helper; `apiKeyEnv` arrays kept and documented in `env.example` and the provider docs.
- **Pages:** one about page per language from a single component (old `/cn` and `/ja` URLs redirect with 308); layout uses `hasLocale`; the GitHub icon is a local component instead of `react-icons`; Cloudflare deploys without an R2 cache by default.
- **Chat and canvas:** the legacy `diagramHistory` conversion, the localStorage chat migration and the templates storage are gone; `chat-engine` reads the diagram through the context's refs; the diagram context has one tagged export path (`requestTaggedExport` / `fetchDiagramXml`); the screenshot check uses fetch + JSON and `generateText` with `Output.object`; `load-diagram` accepts `<svg:svg>` and strips the XML prolog.
- **Settings UI:** `model-selector` inlines its Dialog, Command and Tooltip; `ai-elements/model-selector`, `button-with-tooltip`, `error-toast` removed; no type-to-confirm, no footer, no `bedrockApiKey`; `use-model-config` loses the old-format migration; one reasoning component; the shimmer is CSS (no `motion`, no `@radix-ui/react-use-controllable-state`); 81 dictionary keys removed in all four languages.
- **MCP server:** `registerTool` / `ok` / `fail` replace per-tool try/catch; `syncFromBrowser`, `commitDocument`, `describePages`; the hand-written XML syntax check, unused result fields, `pickPageSelector`, `expandStyles` and the `host` attribute handling are gone; tests get a shared `setup.ts` and drive the server through the SDK client.
- **Prompt:** the web app's style rules come from the MCP drawing guide (`styleRules`), byte-identical to before.
- **Tests:** shared e2e helpers (`openApp`, `sse`, `toolCallEvents`, `chunkedReplies`); placeholder specs dropped; `settings-review-fixes` split by topic; unit tests use `vi.stubEnv`.
- **Dependencies removed:** `@xmldom/xmldom`, `remark-gfm`, `@anthropic-ai/tokenizer`, `@testing-library/user-event`, `concurrently`, `wait-on`, `eslint`, `eslint-config-next`, `@vitest/coverage-v8`, `shx`, `motion`, `@radix-ui/react-use-controllable-state`, `react-icons`.

Source lines: 74,983 before, 67,418 after (excluding lockfiles).

The last commit merges #984 (the canvas shell). Where the shell, the classic page or their tests depend on a wire format or signature this branch had changed, main's version wins (export fields of the poll response, `addHistory` signature); where main only extended code this branch restructured, main's change is carried into the new structure (`start_session`, `get_selection`, `prepareNewDiagram`, the diagram context's `requestExport`).

## Testing

- `tsc --noEmit` for the web app, `packages/mcp-server`, its shell and `electron`: clean
- `biome ci`: 0 errors (37 pre-existing warnings)
- Root `vitest run`: 719 passed; `packages/mcp-server` `vitest run`: 372 passed
- `next build`: clean
- Playwright, whole web suite on the dev server (4 workers): 280 tests, 279 passed; `language.spec.ts` "a stale offer to switch without saving does nothing once a message is being sent" failed under parallel load and passed alone, as it does on main
2026-10-12 12:35:55 +09:00

5.5 KiB

Deploy on Cloudflare Workers

This project can be deployed as a Cloudflare Worker using the OpenNext adapter, giving you:

  • Global edge deployment
  • Very low latency
  • Free workers.dev hosting
  • Full Next.js ISR support via R2 (optional)

Important Windows Note: OpenNext and Wrangler are not fully reliable on native Windows. Recommended options:

  • Use GitHub Codespaces (works perfectly)
  • OR use WSL (Linux)

Pure Windows builds may fail due to WASM file path issues.


Prerequisites

  1. A Cloudflare account (free tier works for basic deployment)
  2. Node.js 18+
  3. Wrangler CLI installed (dev dependency is fine):
npm install -D wrangler
  1. Cloudflare login:
npx wrangler login

Note: A payment method is only required if you want to enable R2 for ISR caching. Basic Workers deployment is free.


Step 1 — Install dependencies

npm install

Step 2 — Configure environment variables

Cloudflare uses a different file for local testing.

1) Create .dev.vars (for Cloudflare local + deploy)

cp env.example .dev.vars

Fill in your API keys and configuration.

2) Make sure .env.local also exists (for regular Next.js dev)

cp env.example .env.local

Fill in the same values there.


Step 3 — Choose your deployment type

Option A: Deploy WITHOUT R2 (Default, Free)

This is how the repository is configured: open-next.config.ts is defineCloudflareConfig({}) and wrangler.jsonc has no r2_buckets. Nothing to change here. Skip to Step 4.


Option B: Deploy WITH R2 (Optional, ISR caching)

R2 gives OpenNext a place to store Incremental Static Regeneration (ISR) pages. This app currently has no ISR pages, so you only need this step if you add some. Requires a payment method on your Cloudflare account.

1. Create an R2 bucket in the Cloudflare Dashboard:

  • Go to Storage & Databases → R2
  • Click Create bucket
  • Name it: next-inc-cache

2. Configure open-next.config.ts:

import { defineCloudflareConfig } from "@opennextjs/cloudflare/config"
import r2IncrementalCache from "@opennextjs/cloudflare/overrides/incremental-cache/r2-incremental-cache"

export default defineCloudflareConfig({
  incrementalCache: r2IncrementalCache,
})

3. Add r2_buckets to wrangler.jsonc:

{
  "$schema": "node_modules/wrangler/config-schema.json",
  "main": ".open-next/worker.js",
  "name": "next-ai-draw-io-worker",
  "compatibility_date": "2025-12-08",
  "compatibility_flags": ["nodejs_compat", "global_fetch_strictly_public"],
  "assets": {
    "directory": ".open-next/assets",
    "binding": "ASSETS"
  },
  "r2_buckets": [
    {
      "binding": "NEXT_INC_CACHE_R2_BUCKET",
      "bucket_name": "next-inc-cache"
    }
  ],
  "services": [
    {
      "binding": "WORKER_SELF_REFERENCE",
      "service": "next-ai-draw-io-worker"
    }
  ]
}

Important: The bucket_name must exactly match the name you created in the Cloudflare dashboard.

Note: Time-based revalidation (revalidate on a page or fetch) also needs a queue in open-next.config.ts. See the OpenNext Cloudflare caching docs.


Step 4 — Register a workers.dev subdomain (first-time only)

Before your first deployment, you need a workers.dev subdomain.

Option 1: Via Cloudflare Dashboard (Recommended)

Visit: https://dash.cloudflare.com → Workers & Pages → Overview → Set up a subdomain

Option 2: During deploy

When you run npm run deploy, Wrangler may prompt:

Would you like to register a workers.dev subdomain? (Y/n)

Type Y and choose a subdomain name.

Note: In CI/CD or non-interactive environments, the prompt won't appear. Register via the dashboard first.


Step 5 — Deploy to Cloudflare

npm run deploy

What the script does:

  • Builds the Next.js app
  • Converts it to a Cloudflare Worker via OpenNext
  • Uploads static assets
  • Publishes the Worker

Your app will be available at:

https://<worker-name>.<your-subdomain>.workers.dev

Common issues & fixes

You need to register a workers.dev subdomain

Cause: No workers.dev subdomain registered for your account.

Fix: Go to https://dash.cloudflare.com → Workers & Pages → Set up a subdomain.


Please enable R2 through the Cloudflare Dashboard

Cause: R2 is configured in wrangler.jsonc but not enabled on your account.

Fix: Either enable R2 (requires payment method) or use Option A (deploy without R2).


No R2 binding "NEXT_INC_CACHE_R2_BUCKET" found

Cause: r2_buckets is missing from wrangler.jsonc.

Fix: Add the r2_buckets section or switch to Option A (without R2).


Can't set compatibility date in the future

Cause: compatibility_date in wrangler config is set to a future date.

Fix: Change compatibility_date to today or an earlier date.


Windows error: resvg.wasm?module (ENOENT)

Cause: Windows filenames cannot include ?, but a wasm asset uses ?module in its filename.

Fix: Build/deploy on Linux (WSL, Codespaces, or CI).


Optional: Preview locally

Preview the Worker locally before deploying:

npm run preview

Summary

Feature Without R2 With R2
Cost Free Requires payment method
ISR Caching No Yes
Static Pages Yes Yes
API Routes Yes Yes
Setup Complexity Simple Moderate

The repository defaults to without R2. Choose with R2 only if you add pages that need ISR caching.