1---
2title: 2.0.0-beta.40 - Vite Dev Server
3date: 2026-05-15T22:00:00Z
4category: release
5excerpt: Cloudflare local dev gains a real Vite dev server (HMR, SSR, client + worker in one process), AiGateway stops reporting spurious updates on every deploy, the Worker HTTP server returns generic 500s while logging the full Cause server-side, and a handful of fixes around entrypoint generation, container startup, and the dev lockfile.
6---
7
8:::caution
9**Upgrade immediately if you're on beta.39.** This release
10reverts a critical bug introduced in beta.39 where
11`scopeTransferToStream` caused **415** responses from
12Workers and **500** errors against the Cloudflare state
13store. The state store version has also been bumped to
14**v5** — older clients will refuse to load mismatched
15state and prompt you to upgrade.
16:::
17
18`v2.0.0-beta.40` is headlined by a real Vite dev server
19for Cloudflare Workers — `Cloudflare.Website.Vite` now boots a
20single process that serves your client, SSR, and Worker
21through Vite, with HMR. The rest of the release is fixes
22that quiet noisy diffs, tighten error responses, and
23harden codegen against odd file paths.
24
25## `Cloudflare.Website.Vite` — real Vite dev server
26
27`bun alchemy dev` for a `Cloudflare.Website.Vite` resource
28previously bundled the Worker and served the built output
29through the sidecar. That worked, but it skipped the part
30of the Vite toolchain people actually want during
31development: HMR, on-demand SSR, the client/SSR/worker
32environments wired together.
33
34beta.40 replaces that with `vite.createServer` driven
35through `@distilled.cloud/cloudflare-vite-plugin`. The
36sidecar now hosts a Vite dev server that serves your
37client and SSR, while the Worker runs in the same process
38against the live module graph:
39
40```typescript
41const web = yield* Cloudflare.Website.Vite("Web", {
42 main: "./src/worker.ts",
43 env: { VITE_API_URL: api.url.as<string>() },
44});
45```
46
47- HMR works in dev — edit a route component, the browser
48 hot-reloads; edit the worker, the Worker reloads.
49- `props.env` keeps the beta.39 semantics: `VITE_`-prefixed
50 keys are inlined into the client/SSR bundle via
51 `import.meta.env.*`, everything else is a runtime
52 Worker binding.
53- Vite is resolved from the project's own `node_modules`
54 first (via `createRequire`) so you control the version.
55
56## AiGateway: no more update-every-deploy
57
58`Cloudflare.AI.Gateway` was reporting `update` on every
59`bun alchemy deploy`, even when nothing had changed. Two
60things were going on:
61
621. The provider was diffing against `news` instead of
63 observed cloud state.
642. The provider's defaults didn't match what the
65 Cloudflare API actually returns, so the first
66 post-create diff always showed drift.
67
68Both are fixed. Defaults now match the API's response
69shape (`0`, `false`, `null` where the API returns those),
70and `Diff.ts` gained the helpers needed to compare
71observed-vs-desired correctly. Deploys are quiet again
72unless something genuinely changed.
73
74Fixes [#332](https://github.com/alchemy-run/alchemy/issues/332)
75and [#334](https://github.com/alchemy-run/alchemy/issues/334).
76
77## Worker HTTP: log full Cause server-side, return generic 500
78
79The Cloudflare Worker HTTP adapter was leaking internal
80error detail into 500 responses. The fix flips that: the
81full `Cause` (stack, annotations, defects) is logged on
82the server, and the client receives a generic
83`Internal Server Error` 500. Same behavior as Effect's
84own `HttpServer.serve` defaults — just now consistent
85under `Cloudflare.Worker`.
86
87This pairs with the beta.39 lifecycle adapter fix and
88finishes the cleanup of the Worker HTTP path.
89
90Fixes [#336](https://github.com/alchemy-run/alchemy/pull/336).
91
92## Sanitize entrypoint URLs in generated code
93
94Codegen for Lambda, ECS, EC2-hosted, Cloudflare Worker,
95and Cloudflare Container entrypoints was interpolating
96`importPath` directly into a template string:
97
98```diff lang="typescript"
99-import entrypoint from "${importPath}";
100+import entrypoint from ${JSON.stringify(importPath)};
101```
102
103On macOS/Linux this never bit anyone. On Windows, paths
104containing backslashes (or any path with embedded quotes,
105unicode, etc.) generated invalid TypeScript. `JSON.stringify`
106emits a guaranteed-valid JS string literal across every
107codegen site.
108
109## Other fixes
110
111- **Cloudflare.start forwards startup options** — `start`'s
112 options were silently dropped before they reached the
113 container runtime. Thanks
114 **[Christopher Yovanovitch](https://github.com/yovanoc)**
115 ([#341](https://github.com/alchemy-run/alchemy/pull/341)).
116- **`bun alchemy dev` lockfile** moves to
117 `@alchemy.run/node-utils` and the legacy `Sidecar/Lock.ts`
118 is gone ([#345](https://github.com/alchemy-run/alchemy/pull/345)).
119- **Cloudflare state store version bumped to 5** — clients
120 pinned to older versions will refuse to load mismatched
121 state and prompt you to upgrade.
122- **`scopeTransferToStream` reverted** — the optimization
123 was causing 415 responses under workerd; reverting
124 restores the previous (correct) HTTP semantics.
125- **Axiom `smartfilter` chart subtype** — used by the
126 internal otel dashboard; available now if you build
127 your own Axiom charts via `Axiom.Chart`
128 ([#339](https://github.com/alchemy-run/alchemy/pull/339)).
129
130## Contributors
131
132Big thank-you to everyone who shipped code in this beta:
133
134- **[Christopher Yovanovitch](https://github.com/yovanoc)** — `Cloudflare.start` options forwarding ([#341](https://github.com/alchemy-run/alchemy/pull/341))
135
136## Where to go next
137
138- [CHANGELOG](https://github.com/alchemy-run/alchemy/blob/main/CHANGELOG.md#v200-beta40)
139- [Compare v2.0.0-beta.39 → v2.0.0-beta.40](https://github.com/alchemy-run/alchemy/compare/v2.0.0-beta.39...v2.0.0-beta.40)