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 asimport.meta.env.<key>(matches Vite’s defaultenvPrefix). - Non-
VITE_entries stay out of the bundle but are still attached to the deployed Worker as runtime bindings. Redactedvalues are unwrapped when they’reVITE_-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-ip→remoteAddress- the original Cloudflare
Requestservice - patched
request.rawaccess - 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.
SendEmail binding type inference
Section titled “SendEmail binding type inference”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.
Contributors
Section titled “Contributors”Big thank-you to everyone who shipped code in this beta:
- Will King — Cloudflare Worker HTTP effect lifecycle (#328)
- Gerben Mulder —
SendEmailWorker binding type and meta (#326)