Skip to content

Commit 5712f46

Browse files
committed
Enable Worker-safe builder generation
1 parent 824af06 commit 5712f46

20 files changed

Lines changed: 661 additions & 152 deletions

docs/cloudflare-workers-migration.md

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@ TanStack.com is configured as a Cloudflare Workers deployment for this branch. N
44

55
## Files Changed
66

7-
- `package.json`, `pnpm-lock.yaml`: Cloudflare build/preview/deploy scripts, `@cloudflare/vite-plugin`, `wrangler`, 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 disabled for Worker size.
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.
99
- `wrangler.jsonc`: Worker name/account, assets binding, `nodejs_compat`, CPU limit, cron triggers, and production `SITE_URL`.
1010
- `src/server.ts`, `src/server/scheduled.server.ts`: Worker `fetch` and `scheduled` entrypoints, replacing former Netlify scheduled functions.
1111
- `src/server/runtime/host.server.ts`, `src/utils/hosting-cache.server.ts`: host cache purge adapter using Cloudflare cache-tag purge.
1212
- `src/components/OptimizedImage.tsx`, `src/utils/optimizedImage.ts`: host-neutral optimized image helper with Cloudflare image transformations behind an explicit build flag.
13-
- `src/routes/api/builder/*`, `src/components/builder/*`: builder deploy/download path uses browser-generated files; direct server-generation endpoints return explicit 501 on Workers.
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.
1415
- `src/routes/*`, `src/utils/*`, `src/server/*`: CDN cache headers moved from Netlify-specific headers to portable `CDN-Cache-Control` / `Cache-Tag`.
1516
- `src/utils/markdown/processor.ts`: site-side compatibility guard for escaped angle brackets in generated TypeDoc markdown until `@tanstack/markdown` handles `\<...\>` as escaped text.
1617
- `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.
@@ -20,9 +21,12 @@ TanStack.com is configured as a Cloudflare Workers deployment for this branch. N
2021

2122
```bash
2223
pnpm install --lockfile-only
24+
pnpm add @tanstack/create@^0.68.4
2325
pnpm run test:tsc
26+
pnpm exec tsc --noEmit
2427
pnpm run build:cloudflare
2528
pnpm test
29+
pnpm run dev:cloudflare
2630
pnpm run deploy:cloudflare
2731
pnpm run preview:cloudflare -- --host 127.0.0.1 --port 3001
2832
pnpm install
@@ -35,9 +39,9 @@ Additional checks used `curl`, Node fetch scripts, Wrangler tail, and Playwright
3539
- Account: `8da95258a9c70b54c3e2b374a0079106`
3640
- Worker: `tanstack-com`
3741
- URL: `https://tanstack-com.thetanstack.workers.dev`
38-
- Current version: `586b99ec-4a2c-46d2-b3b3-eb775de13141`
39-
- Upload size: `14609.48 KiB` raw, `4736.36 KiB` gzip
40-
- Startup time: `29 ms`
42+
- Current version: `ff011a60-320b-43b7-9a03-bd650a41bc7b`
43+
- Upload size: `17748.26 KiB` raw, `6413.10 KiB` gzip
44+
- Startup time: `33 ms`
4145
- Note: the secret-bearing `tanstack-com-staging` Worker was renamed to `tanstack-com`, and the older empty `tanstack-com` Worker was removed.
4246

4347
## Passed
@@ -59,6 +63,13 @@ Additional checks used `curl`, Node fetch scripts, Wrangler tail, and Playwright
5963
- `/.well-known/oauth-authorization-server` returned OAuth metadata.
6064
- `/api/mcp/` returned the expected unauthenticated JSON-RPC auth error instead of a runtime failure.
6165
- `POST /api/application-starter/resolve` returned a Start recipe.
66+
- `/api/builder/features?framework=react` returned the Worker-safe catalog, including `tanstack-query`, `cloudflare`, and `biome`.
67+
- `/api/builder/compile` generated representative React and Solid projects server-side on the Worker.
68+
- `/api/builder/download` returned a valid zip for a representative React builder project.
69+
- `https://tanstack.com/api/builder/features?framework=react` returned the same Worker-safe builder catalog.
70+
- `https://tanstack.com/builder` returned 200 HTML with COOP/COEP headers.
71+
- Worker output does not include `generated/create-manifest.js` or a `create-manifest` chunk.
72+
- Cloudflare dry-run/upload size stayed below the paid Worker 10 MiB gzip limit after excluding the heavy React `events` example implementation chunk.
6273
- `Link` response headers for static assets are emitted on SSR responses for Cloudflare Early Hints fallback.
6374
- Broad docs/blog audit generated 2,767 latest-doc/blog URLs from GitHub doc trees plus local blog posts and compared production vs Worker.
6475
- Escaped generic headings in TypeDoc markdown now render correctly, e.g. `Interface: AudioAdapter<TModel, TProviderOptions>` with the production-compatible `interface-audioadaptertmodel-tprovideroptions` anchor.
@@ -72,23 +83,19 @@ Additional checks used `curl`, Node fetch scripts, Wrangler tail, and Playwright
7283

7384
## Failed Or Not Proven
7485

75-
- Direct server-side builder generation endpoints return 501 on Workers:
76-
- `/api/builder/features`
77-
- `/api/builder/compile`
78-
- `/api/builder/compile-attributed`
79-
- `/api/builder/download`
80-
- `/api/builder/validate`
81-
- `/api/builder/feature-artifacts`
8286
- Full GitHub OAuth callback/account login was not completed.
8387
- End-to-end GitHub repository deploy was not completed with a logged-in account.
8488
- Cron trigger behavior was deployed but not manually invoked.
89+
- 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.
8590
- 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.
8691

8792
## Builder Generation Note
8893

89-
The released `@tanstack/create@0.68.3` `edge` import is Worker-runtime compatible, but it still statically imports the generated create manifest. That manifest made the Worker upload `11222.23 KiB` gzip, over the paid 10 MiB Worker script limit.
94+
`@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.
9095

91-
The deployable compromise in this branch keeps dynamic generation in the browser for the builder UI, downloads, and GitHub deploy handoff, and excludes server-side create generation from the Worker bundle. After that change, the Worker upload is `4804.51 KiB` gzip.
96+
`/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`.
97+
98+
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.
9299

93100
## Image Transformation Note
94101

@@ -112,6 +119,6 @@ Remaining markdown differences observed during audit:
112119

113120
## Readiness
114121

115-
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, and dynamic OG image generation are working on Cloudflare Workers.
122+
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.
116123

117-
Production migration is close, but not fully safe until logged-in OAuth/account flows, cron jobs, and an authenticated builder GitHub deploy are verified. The biggest remaining product parity decision is whether direct server-side builder generation APIs must be supported on the Worker; supporting them requires a smaller create manifest/runtime from `@tanstack/create` or a separate generation service.
124+
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.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@
5959
"@tanstack/ai-anthropic": "^0.10.1",
6060
"@tanstack/ai-client": "^0.11.3",
6161
"@tanstack/ai-openai": "^0.9.5",
62-
"@tanstack/create": "^0.68.3",
62+
"@tanstack/create": "^0.68.4",
6363
"@tanstack/highlight": "^0.0.2",
6464
"@tanstack/markdown": "^0.0.5",
6565
"@tanstack/pacer": "^0.21.1",

pnpm-lock.yaml

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/builder/api/compile.ts

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
import {
2-
createApp,
2+
create,
33
createMemoryEnvironment,
4-
finalizeAddOns,
5-
populateAddOnOptionsDefaults,
64
type AddOn,
75
type Framework,
86
type Starter,
9-
} from '@tanstack/create/edge'
7+
} from './create-worker'
108

119
type AddOnType = 'add-on' | 'example' | 'starter' | 'toolchain' | 'deployment'
1210
type AddOnPhase = 'setup' | 'add-on' | 'example'
@@ -62,7 +60,7 @@ export interface ProjectDefinition {
6260
packageManager?: 'bun' | 'npm' | 'pnpm' | 'yarn'
6361
tailwind?: boolean
6462
features: Array<string>
65-
featureOptions: Record<string, Record<string, unknown>>
63+
featureOptions?: Record<string, Record<string, unknown>>
6664
selectedExample?: string
6765
customIntegrations?: Array<AddOnCompiled>
6866
customTemplate?: StarterCompiled | null
@@ -102,14 +100,14 @@ async function resolveAddOns(
102100
customAddOns: Array<AddOnCompiled>,
103101
frameworkId: FrameworkId = 'react',
104102
): Promise<Array<AddOn>> {
105-
const framework = getFramework(frameworkId)
103+
const framework = await getFramework(frameworkId)
106104
const allFrameworkAddOns = framework.getAddOns()
107105

108106
const customIds = new Set(customAddOns.map((a: AddOnCompiled) => a.id))
109107

110108
const frameworkFeatureIds = featureIds.filter((id) => !customIds.has(id))
111109

112-
const resolvedFramework = await finalizeAddOns(
110+
const resolvedFramework = await create.finalizeAddOns(
113111
framework,
114112
DEFAULT_MODE,
115113
[...DEFAULT_REQUIRED_ADDONS, ...frameworkFeatureIds],
@@ -186,7 +184,7 @@ function mergeOptionsWithDefaults(
186184
chosenAddOns: Array<AddOn>,
187185
userOptions: Record<string, Record<string, unknown>>,
188186
): Record<string, Record<string, unknown>> {
189-
const defaults = populateAddOnOptionsDefaults(chosenAddOns)
187+
const defaults = create.populateAddOnOptionsDefaults(chosenAddOns)
190188
const merged: Record<string, Record<string, unknown>> = { ...defaults }
191189

192190
for (const [addonId, options] of Object.entries(userOptions)) {
@@ -201,7 +199,8 @@ export async function compileHandler(
201199
options: CompileHandlerOptions = {},
202200
): Promise<CompileResponse> {
203201
const frameworkId = definition.framework ?? 'react'
204-
const framework = getFramework(frameworkId)
202+
const framework = await getFramework(frameworkId)
203+
const featureOptions = definition.featureOptions ?? {}
205204

206205
// Merge selectedExample into features (CTA treats examples as add-ons)
207206
const allFeatures = definition.selectedExample
@@ -216,7 +215,7 @@ export async function compileHandler(
216215
)
217216

218217
// Custom starters disabled until stable launch
219-
await createApp(environment, {
218+
await create.createApp(environment, {
220219
projectName: definition.name,
221220
targetDir: `/project/${definition.name}`,
222221
framework,
@@ -228,7 +227,7 @@ export async function compileHandler(
228227
install: false,
229228
intent: false,
230229
chosenAddOns,
231-
addOnOptions: mergeOptionsWithDefaults(chosenAddOns, definition.featureOptions),
230+
addOnOptions: mergeOptionsWithDefaults(chosenAddOns, featureOptions),
232231
})
233232

234233
const packageJson = output.files['package.json']
@@ -594,7 +593,8 @@ export async function compileWithAttributionHandler(
594593
definition: ProjectDefinition,
595594
): Promise<AttributedCompileOutput> {
596595
const frameworkId = definition.framework ?? 'react'
597-
const framework = getFramework(frameworkId)
596+
const framework = await getFramework(frameworkId)
597+
const featureOptions = definition.featureOptions ?? {}
598598

599599
// Merge selectedExample into features (CTA treats examples as add-ons)
600600
const allFeatures = definition.selectedExample
@@ -609,7 +609,7 @@ export async function compileWithAttributionHandler(
609609
)
610610

611611
// Custom starters disabled until stable launch
612-
await createApp(environment, {
612+
await create.createApp(environment, {
613613
projectName: definition.name,
614614
targetDir: `/project/${definition.name}`,
615615
framework,
@@ -621,7 +621,7 @@ export async function compileWithAttributionHandler(
621621
install: false,
622622
intent: false,
623623
chosenAddOns,
624-
addOnOptions: mergeOptionsWithDefaults(chosenAddOns, definition.featureOptions),
624+
addOnOptions: mergeOptionsWithDefaults(chosenAddOns, featureOptions),
625625
})
626626

627627
const packageJson = output.files['package.json']

src/builder/api/config.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
import { getFrameworkById } from '@tanstack/create/edge'
21
import { normalizeFrameworkId, type FrameworkId } from '../frameworks'
2+
import { create } from './create-worker'
33

44
export { type FrameworkId, FRAMEWORKS, normalizeFrameworkId } from '../frameworks'
55

6-
export function getFramework(id: FrameworkId | string = 'react') {
7-
const framework = getFrameworkById(normalizeFrameworkId(id))
6+
export async function getFramework(id: FrameworkId | string = 'react') {
7+
const frameworkId = normalizeFrameworkId(id)
8+
const framework = await create.getFrameworkById(frameworkId)
89
if (!framework) {
910
throw new Error(`${id} framework not found`)
1011
}

0 commit comments

Comments
 (0)