Repository navigation
fix: preserve custom TanStack Worker entries and prepare native Cloudflare output - #444
shrek-kurata wants to merge 6 commits into
Conversation
… output API
A migrated TanStack config keeps `main: "@tanstack/react-start/server-entry"`,
which was resolved as a file under the app root and broke dev and
build. A package-style `main` is now left for Vite to resolve, and
TanStack's own entry keeps the default, matching how Cloudflare reads
`main`.
`void deploy` wrapped the built Worker again and kept only its default
export, so the Durable Object and Workflow classes the custom entry
exports never reached Cloudflare. The deployment wrapper now
re-exports the original module's named exports.
`prepareCfBuildOutput` from `void/deploy` now takes
`{ accountId, mode }`, returns only `directory`, `assetsDirectory` and
`dispose`, and is documented as experimental because it follows
Cloudflare's draft Build Output format. It names the missing input or
module instead of failing with ENOENT, and refuses legacy asset
symlinks that point outside the asset directory.
The TanStack guide says where to bind exported classes, and the
reference covers local container images for both output formats.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
c6ac99a to
03e1309
Compare
|
@codex review |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 03e1309713
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…out regex The deployment wrapper rebuilt the default export from fetch and Void's own cron and queue handlers, so a custom entry's scheduled, email or tail handler was dropped on deploy. It now spreads the original default export first; Void's handlers still take precedence. A package subpath such as `@acme/worker/entry.js` was read as a file under the app root because it ends in `.js`. A bare `cloudflare.main` now counts as a file only when it exists in the app; `./`, `../` and absolute paths are always files. The entry, bundle-path and test checks use plain string checks instead of regular expressions. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 36e6f64802
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
The extension list that replaced the entry regex left out `.cjs` and `.cts`, so `cloudflare.main: "src/worker.cjs"` was sent to Vite as a package. A bare `main` now counts as a file whenever it is a file in the app, with no extension list to keep in step. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 109a01bdcc
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| } | ||
| } | ||
| if (worker.assetsDir) { | ||
| cpSync(worker.assetsDir, getWorkerAssetsDir(directory, name), { recursive: true }); |
There was a problem hiding this comment.
Reject escaping symlinks in native asset trees
When native .cloudflare/output/v0 assets contain a symlink, this cpSync neither calls assertAssetSymlinksInside nor dereferences the link; Node's default copy preserves it and resolves its target back into the original source tree. The returned artifact can therefore stop being self-contained when the source moves, and an escaping link can cause a later asset upload to read a file outside the asset directory, even though the public API promises that such links throw. Validate and materialize native asset links just as the legacy branch does.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Thanks, but this cannot happen: prepareNativeBuildOutput first calls readBuildOutput(source), which refuses any symlink in the native output, assets included, before cpSync runs ("Build Output Specification: symlink found at …/assets/…. Symlinks are not permitted because the build output must be portable and self-contained."). A link inside the asset directory is refused too, so there is nothing to validate or materialize at the copy. 499b5a2 adds a test that locks this in: rejects symlinked native assets in cf-build-output.test.ts. Leaving this thread open for the maintainer.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
TanStack Start applications which configure
cloudflare.maincurrently still use the default framework entry, and the generated wrapper drops named exports such as Durable Object and Workflow classes. Resolve the custom entry from the application root in development and build hooks, preserve its named exports and default handlers, and reject empty or recursively generated entries. The regression tests load real generated wrappers through Vite and verify fetch behavior and class identity.Expose the existing
prepareCfBuildOutputconverter andCfBuildOutputtype through the publicvoid/deployentry. It now also accepts a project root containing native.cloudflare/output/v0output from Cloudflare Vite plugin 2. Preparation uses the standard reader to complete partial module manifests, copies only declared modules plus assets, preserves auxiliary Workers and already-built Container declarations, and leaves source files unchanged. Native output keeps its original build mode; explicitly conflicting accounts or modes returnnull. Legacy Worker output and its production-mode default remain supported.Native module paths must remain inside the portable bundle, and the main ES module must be declared. Original source maps and binary bytes are retained. The result is a disposable private artifact; preparation performs no publication or resource provisioning. The public API and framework workflows are documented.
Related integration work: #445. This PR prepares native artifacts but does not change Void's internal Vite plugin dependency or claim complete Vite plugin 2 migration.
Validation on base
134a2ee5052458242e15273c0db6aecaa5760249, including the original custom-entry commit and the native artifact extension:@cloudflare/vite-plugin@2.0.0-beta.v.0with Vite 8.3.4 built native output which the public converter consumed. Realcf workers versions create --prebuilt --dry-runsucceeded. The probe retained account, compliance region, staging mode, assets, and esm/sourcemap/text/data/wasm bytes, and verified original files were unchanged. A separate case retained a root Container declaration; it did not test live Container execution or export-to-Container behavior.vpr test:all: formatting, lint, complete builds, framework artifact validation, type-aware platform lint, typechecking, and 711 test files / 13,554 tests passed; one file and 31 tests skipped. Credentialed live tests were not run.void/deploynative-output runtime probe and an actual consumer TypeScript check, including an assertion that the type-probe file was included. Private loopback package registries were used; no package was published externally.The native extension source commit is
c6ac99a7c2ff8503acff663d415822a9ee06efca. Its source tree exactly matches the final verified input. No local dependency installation, live deployment, or platform-resource operation was performed.Maintainer follow-up
Rebased onto main, with one commit on top:
cloudflare.main(TanStack's own@tanstack/react-start/server-entry, which a migratedwrangler.jsonckeeps) is no longer resolved as a file under the app root. File paths still resolve from the app root.void deployre-wraps the built Worker. That wrapper now re-exports the original module's named exports, so Durable Object and Workflow classes from a custom entry reach Cloudflare.prepareCfBuildOutput(dir, { accountId?, mode? })is the publicvoid/deployshape. It returns onlydirectory,assetsDirectoryanddispose, and its reference section is marked experimental because it follows Cloudflare's draft Build Output format.🤖 Generated with Claude Code