|
| 1 | +# Cloudflare Workers Migration Report |
| 2 | + |
| 3 | +TanStack.com is configured as a Cloudflare Workers deployment for this branch. Netlify is no longer part of this site's hosting configuration, but Netlify remains available in builder/deploy-provider UX for users who choose it. |
| 4 | + |
| 5 | +## Files Changed |
| 6 | + |
| 7 | +- `package.json`, `pnpm-lock.yaml`: Cloudflare build/preview/deploy scripts, `@cloudflare/vite-plugin`, `wrangler`, `@tanstack/create@0.68.4`, and removal of Netlify hosting packages. |
| 8 | +- `vite.config.ts`: Cloudflare Vite plugin, Worker build constants, opt-in image transformation flag, and server builder-generation enabled with the Worker-safe create API. |
| 9 | +- `wrangler.jsonc`: Worker name/account, assets binding, `nodejs_compat`, CPU limit, cron triggers, and production `SITE_URL`. |
| 10 | +- `src/server.ts`, `src/server/scheduled.server.ts`: Worker `fetch` and `scheduled` entrypoints, replacing former Netlify scheduled functions. |
| 11 | +- `src/server/runtime/host.server.ts`, `src/utils/hosting-cache.server.ts`: host cache purge adapter using Cloudflare cache-tag purge. |
| 12 | +- `src/components/OptimizedImage.tsx`, `src/utils/optimizedImage.ts`: host-neutral optimized image helper with Cloudflare image transformations behind an explicit build flag. |
| 13 | +- `src/builder/api/create-worker.ts`: host-neutral adapter around `@tanstack/create/worker` with local Worker manifest loaders for supported frameworks and add-ons. |
| 14 | +- `src/builder/api/*`, `src/routes/api/builder/*`: builder feature catalog, compile, download, validate, suggest, and GitHub deploy paths use the Worker-safe create adapter. |
| 15 | +- `src/routes/*`, `src/utils/*`, `src/server/*`: CDN cache headers moved from Netlify-specific headers to Cloudflare Workers Cache headers / `Cache-Tag`. |
| 16 | +- `src/utils/markdown/processor.ts`: site-side compatibility guard for escaped angle brackets in generated TypeDoc markdown until `@tanstack/markdown` handles `\<...\>` as escaped text. |
| 17 | +- `package.json`, `pnpm-lock.yaml`: `@tanstack/markdown@0.0.5` for compact table delimiters, footnotes, and legacy single-tilde strike headings without the temporary pnpm patch. |
| 18 | +- Removed hosting-only Netlify files: `netlify.toml`, `netlify/functions/*`, `scripts/run-built-server.mjs`. |
| 19 | + |
| 20 | +## Commands Used |
| 21 | + |
| 22 | +```bash |
| 23 | +pnpm install --lockfile-only |
| 24 | +pnpm add @tanstack/create@^0.68.4 |
| 25 | +pnpm run test:tsc |
| 26 | +pnpm exec tsc --noEmit |
| 27 | +pnpm run build:cloudflare |
| 28 | +pnpm test |
| 29 | +pnpm run dev:cloudflare |
| 30 | +pnpm run deploy:cloudflare |
| 31 | +pnpm run preview:cloudflare -- --host 127.0.0.1 --port 3001 |
| 32 | +pnpm install |
| 33 | +``` |
| 34 | + |
| 35 | +Additional checks used `curl`, Node fetch scripts, Wrangler tail, and Playwright with system Chrome against the Workers preview URL. |
| 36 | + |
| 37 | +## Deploy Cache Purge |
| 38 | + |
| 39 | +`pnpm run deploy:cloudflare` builds, deploys the Worker, then runs `pnpm run purge:cloudflare`. The purge step clears the Cloudflare zone cache with `purge_everything` so stale HTML documents cannot keep pointing at removed route chunks after a deploy. |
| 40 | + |
| 41 | +Required environment: |
| 42 | + |
| 43 | +- `CLOUDFLARE_ZONE_ID`: the active `tanstack.com` zone ID. |
| 44 | +- `CLOUDFLARE_CACHE_PURGE_TOKEN`: Cloudflare API token with `Cache Purge` permission for that zone. |
| 45 | + |
| 46 | +`CLOUDFLARE_API_TOKEN` / `CF_API_TOKEN` and `CF_ZONE_ID` are accepted as aliases. If `CLOUDFLARE_ZONE_ID` is missing, the script can discover it only when the token also has zone read access. |
| 47 | + |
| 48 | +## Worker |
| 49 | + |
| 50 | +- Account: `8da95258a9c70b54c3e2b374a0079106` |
| 51 | +- Worker: `tanstack-com` |
| 52 | +- URL: `https://tanstack-com.thetanstack.workers.dev` |
| 53 | +- Current version: `ff011a60-320b-43b7-9a03-bd650a41bc7b` |
| 54 | +- Upload size: `17748.26 KiB` raw, `6413.10 KiB` gzip |
| 55 | +- Startup time: `33 ms` |
| 56 | +- Note: the secret-bearing `tanstack-com-staging` Worker was renamed to `tanstack-com`, and the older empty `tanstack-com` Worker was removed. |
| 57 | + |
| 58 | +## Passed |
| 59 | + |
| 60 | +- `pnpm run build:cloudflare` passed. |
| 61 | +- `pnpm run test:tsc` passed. |
| 62 | +- `pnpm test` passed with 10 existing oxlint warnings. |
| 63 | +- Cloudflare deploy passed. |
| 64 | +- Local Cloudflare preview started and returned 200 for `/` and `/builder`. |
| 65 | +- `/` returned 200 HTML on the Worker. |
| 66 | +- `/start/latest` returned 200 HTML on the Worker. |
| 67 | +- Browser SPA navigation from `/` to `/start/latest` did not reproduce the `npm-recent-downloads ... data is undefined` error. |
| 68 | +- `/builder` returned 200 HTML with COOP/COEP headers and loaded the client builder/integration surface. |
| 69 | +- Primary homepage images loaded in browser on the Worker. |
| 70 | +- `/api/og/query.png?title=Query&description=Smoke` returned a valid 1200x630 PNG. |
| 71 | +- `/_a/gtag.js` returned Google's JavaScript. |
| 72 | +- `/_a/g/collect` returned 204. |
| 73 | +- `/auth/github/start?returnTo=/account` redirected to GitHub with secure state/return cookies. |
| 74 | +- `/.well-known/oauth-authorization-server` returned OAuth metadata. |
| 75 | +- `/api/mcp/` returned the expected unauthenticated JSON-RPC auth error instead of a runtime failure. |
| 76 | +- `POST /api/application-starter/resolve` returned a Start recipe. |
| 77 | +- `/api/builder/features?framework=react` returned the Worker-safe catalog, including `tanstack-query`, `cloudflare`, and `biome`. |
| 78 | +- `/api/builder/compile` generated representative React and Solid projects server-side on the Worker. |
| 79 | +- `/api/builder/download` returned a valid zip for a representative React builder project. |
| 80 | +- `https://tanstack.com/api/builder/features?framework=react` returned the same Worker-safe builder catalog. |
| 81 | +- `https://tanstack.com/builder` returned 200 HTML with COOP/COEP headers. |
| 82 | +- Worker output does not include `generated/create-manifest.js` or a `create-manifest` chunk. |
| 83 | +- Cloudflare dry-run/upload size stayed below the paid Worker 10 MiB gzip limit after excluding the heavy React `events` example implementation chunk. |
| 84 | +- `Link` response headers for static assets are emitted on SSR responses for Cloudflare Early Hints fallback. |
| 85 | +- Broad docs/blog audit generated 2,767 latest-doc/blog URLs from GitHub doc trees plus local blog posts and compared production vs Worker. |
| 86 | +- Escaped generic headings in TypeDoc markdown now render correctly, e.g. `Interface: AudioAdapter<TModel, TProviderOptions>` with the production-compatible `interface-audioadaptertmodel-tprovideroptions` anchor. |
| 87 | +- Local site parser checks now match the remaining markdown audit diffs: |
| 88 | + - `/ai/latest/docs/code-mode/code-mode` parses 4 content tables. |
| 89 | + - `/db/latest/docs/collections/powersync-collection` parses 24 content tables. |
| 90 | + - `/start/latest/docs/framework/react/migrate-from-next-js` headings render as `Server Actions Functions` and `Server Routes Handlers`. |
| 91 | + - `/blog/tanstack-router-signal-graph` renders footnotes with `data-footnotes` and production-compatible footnote anchors. |
| 92 | +- Live Worker rechecks against production passed for the same table, heading, and footnote signals after deploying version `586b99ec-4a2c-46d2-b3b3-eb775de13141`. |
| 93 | +- Three full-body rechecks of 43 URLs that intermittently returned Worker 500/timeout during the high-concurrency audit cleared; the only stable non-200s were `/hotkeys/latest/docs/reference` and `/pacer/latest/docs/reference`, both 404 on production and Worker. |
| 94 | + |
| 95 | +## Failed Or Not Proven |
| 96 | + |
| 97 | +- Full GitHub OAuth callback/account login was not completed. |
| 98 | +- End-to-end GitHub repository deploy was not completed with a logged-in account. |
| 99 | +- Cron trigger behavior was deployed but not manually invoked. |
| 100 | +- The React `events` example is not exposed in the Worker builder catalog because its implementation chunk pushes the Worker over Cloudflare's paid 10 MiB gzip upload limit. |
| 101 | +- High-concurrency audit runs can still produce transient Worker 500s with `{"status":500,"unhandled":true,"message":"HTTPError"}` on changing docs paths, but targeted full-body rechecks did not reproduce stable page failures. Treat this as a load/audit-cache risk, not a confirmed content regression. |
| 102 | + |
| 103 | +## Builder Generation Note |
| 104 | + |
| 105 | +`@tanstack/create@0.68.4` adds the lazy `@tanstack/create/worker` API. The site now imports that API through `src/builder/api/create-worker.ts` instead of importing `@tanstack/create/edge` from route logic. |
| 106 | + |
| 107 | +`/api/builder/features` remains catalog-only by using `create.getFrameworkById` and `create.getAllAddOns`. ZIP/project generation loads only the requested framework/add-on chunks, then calls `create.finalizeAddOns`, `create.populateAddOnOptionsDefaults`, `createMemoryEnvironment`, and `create.createApp`. |
| 108 | + |
| 109 | +The Worker build was checked for `generated/create-manifest.js` and `create-manifest` output, and no generated manifest bundle was present. Including the React `events` example implementation still pushed upload size to `11179.57 KiB` gzip, so the Worker loader intentionally omits that chunk. The deployed Worker is `6413.10 KiB` gzip. |
| 110 | + |
| 111 | +## Image Transformation Note |
| 112 | + |
| 113 | +Cloudflare image transformations are enabled for `build:cloudflare`. Transformed image URLs are emitted through the configured `SITE_URL` origin because `/cdn-cgi/image/*` works on the routed `tanstack.com` zone but still returns 404 on the `workers.dev` hostname. |
| 114 | + |
| 115 | +## Markdown Audit Note |
| 116 | + |
| 117 | +The new markdown renderer initially parsed escaped TypeDoc generics like `\<T\>` as inline HTML. The site now protects escaped `<` / `>` outside code fences and inline code before parsing, restores them into text nodes before render, and rebuilds headings so rendered content and ToC anchors stay aligned. |
| 118 | + |
| 119 | +Resolved markdown differences from the broad audit: |
| 120 | + |
| 121 | +- Escaped TypeDoc generics no longer become inline HTML. |
| 122 | +- Compact markdown table delimiters like `:--:` now parse as tables, matching the existing production renderer. |
| 123 | +- Blog footnotes now render with `data-footnotes`, `user-content-fn-*`, and `user-content-fnref-*` anchors. |
| 124 | +- Legacy single-tilde strike headings now render without visible `~` markers. |
| 125 | + |
| 126 | +Remaining markdown differences observed during audit: |
| 127 | + |
| 128 | +- Production duplicates light/dark code blocks; the Worker branch renders one theme-aware code block. This explains large HTML-size and `<pre>` count differences. |
| 129 | +- The temporary `@tanstack/markdown@0.0.4` pnpm patch has been removed after upgrading to `@tanstack/markdown@0.0.5`. |
| 130 | + |
| 131 | +## Readiness |
| 132 | + |
| 133 | +Core marketing SSR, docs/start navigation, security headers, static assets, analytics proxying, GitHub auth start, MCP auth rejection, application-starter API, scheduled Worker registration, Cloudflare preview, deploy, dynamic OG image generation, and representative Worker-side builder generation are working on Cloudflare Workers. |
| 134 | + |
| 135 | +Production migration is close, but not fully safe until logged-in OAuth/account flows pass, cron jobs are verified, and an authenticated builder GitHub deploy is completed. The main remaining builder gap is the omitted React `events` example chunk. |
0 commit comments