Skip to content

What's new in beta.39

v2.0.0-beta.39 is a fix-only release that lands three things people hit immediately after beta.38: SPAs reading import.meta.env.VITE_*, Effect RPC servers running under the Cloudflare Worker adapter, and the brand-new SendEmail binding showing up in Worker binding types.

Cloudflare.Vite inlines VITE_* env into the bundle

Section titled “Cloudflare.Vite inlines VITE_* env into the bundle”

Cloudflare.Vite("Site", { env: { ... } }) was forwarding env only as runtime Worker bindings — the values never made it into the client (or SSR) bundle. SPAs reading import.meta.env.VITE_API_URL fell back to whatever default the code hardcoded, breaking dynamic backend URLs at build time.

The build step now passes props.env through Vite’s define hook, emulating vite build’s env semantics:

const web = yield* Cloudflare.Vite("Web", {
env: { VITE_API_URL: worker.url.as<string>() },
});
// SPA: `import.meta.env.VITE_API_URL` now contains the deployed worker URL.

The rules match Vite itself:

  • Only VITE_-prefixed keys are inlined into the bundle as import.meta.env.<key> (matches Vite’s default envPrefix).
  • Non-VITE_ entries stay out of the bundle but are still attached to the deployed Worker as runtime bindings.
  • Redacted values are unwrapped when they’re VITE_-prefixed — opting them into the public bundle is an explicit choice you make by naming them that way.

The tutorial gains an Inject the Worker URL at build time section covering the pattern end-to-end.

Cloudflare Worker HTTP adapter — Effect’s handled lifecycle

Section titled “Cloudflare Worker HTTP adapter — Effect’s handled lifecycle”

The Worker HTTP adapter previously hand-rolled the HttpServerRequest / HttpServerResponse bridge. That worked for simple handlers but diverged from Effect’s HttpEffect.toHandled / toWebHandler lifecycle in ways that broke scoped HTTP apps under workerd — most visibly, RpcServer.toHttpEffect hung when served through Alchemy even though the same app worked under raw Wrangler.

The adapter now runs handlers through Effect’s standard HTTP lifecycle while preserving every Worker-specific behavior:

  • cf-connecting-ipremoteAddress
  • the original Cloudflare Request service
  • patched request.raw access
  • existing 500 response mapping from serveWebRequest

…plus the parts that were missing: stream-scope transfer, HEAD body suppression, and request-abort interruption.

The net effect: anything that worked under @effect/platform’s standard HTTP lifecycle now works under Cloudflare.Worker too. RpcServer.toHttpEffect, in particular, is unblocked.

Thanks to Will King (#328) for the fix. Fixes #327.

beta.38 added Cloudflare.SendEmail but two wiring sites in Worker.ts were missed — the binding type inference didn’t know about it, and the deploy-time binding metadata didn’t emit a send_email entry. The result: typing bindings: { EMAIL: Email } on a Worker errored, and deploys silently dropped the binding.

Both are wired up now — SendEmail is a first-class member of the Worker binding union, the env key carries the right runtime type, and Cloudflare receives the expected send_email binding metadata at deploy time.

Thanks to Gerben Mulder (#326) for catching this.

Big thank-you to everyone who shipped code in this beta: