Skip to content

alchemy@2.0.0-beta.40

v2.0.0-beta.40 is headlined by a real Vite dev server for Cloudflare Workers — Cloudflare.Vite now boots a single process that serves your client, SSR, and Worker through Vite, with HMR. The rest of the release is fixes that quiet noisy diffs, tighten error responses, and harden codegen against odd file paths.

bun alchemy dev for a Cloudflare.Vite resource previously bundled the Worker and served the built output through the sidecar. That worked, but it skipped the part of the Vite toolchain people actually want during development: HMR, on-demand SSR, the client/SSR/worker environments wired together.

beta.40 replaces that with vite.createServer driven through @distilled.cloud/cloudflare-vite-plugin. The sidecar now hosts a Vite dev server that serves your client and SSR, while the Worker runs in the same process against the live module graph:

const web = yield* Cloudflare.Vite("Web", {
main: "./src/worker.ts",
env: { VITE_API_URL: api.url.as<string>() },
});
  • HMR works in dev — edit a route component, the browser hot-reloads; edit the worker, the Worker reloads.
  • props.env keeps the beta.39 semantics: VITE_-prefixed keys are inlined into the client/SSR bundle via import.meta.env.*, everything else is a runtime Worker binding.
  • Vite is resolved from the project’s own node_modules first (via createRequire) so you control the version.

Cloudflare.AiGateway was reporting update on every bun alchemy deploy, even when nothing had changed. Two things were going on:

  1. The provider was diffing against news instead of observed cloud state.
  2. The provider’s defaults didn’t match what the Cloudflare API actually returns, so the first post-create diff always showed drift.

Both are fixed. Defaults now match the API’s response shape (0, false, null where the API returns those), and Diff.ts gained the helpers needed to compare observed-vs-desired correctly. Deploys are quiet again unless something genuinely changed.

Fixes #332 and #334.

Worker HTTP: log full Cause server-side, return generic 500

Section titled “Worker HTTP: log full Cause server-side, return generic 500”

The Cloudflare Worker HTTP adapter was leaking internal error detail into 500 responses. The fix flips that: the full Cause (stack, annotations, defects) is logged on the server, and the client receives a generic Internal Server Error 500. Same behavior as Effect’s own HttpServer.serve defaults — just now consistent under Cloudflare.Worker.

This pairs with the beta.39 lifecycle adapter fix and finishes the cleanup of the Worker HTTP path.

Fixes #336.

Sanitize entrypoint URLs in generated code

Section titled “Sanitize entrypoint URLs in generated code”

Codegen for Lambda, ECS, EC2-hosted, Cloudflare Worker, and Cloudflare Container entrypoints was interpolating importPath directly into a template string:

import entrypoint from "${importPath}";
import entrypoint from ${JSON.stringify(importPath)};

On macOS/Linux this never bit anyone. On Windows, paths containing backslashes (or any path with embedded quotes, unicode, etc.) generated invalid TypeScript. JSON.stringify emits a guaranteed-valid JS string literal across every codegen site.

  • Cloudflare.start forwards startup optionsstart’s options were silently dropped before they reached the container runtime. Thanks Christopher Yovanovitch (#341).
  • bun alchemy dev lockfile moves to @alchemy.run/node-utils and the legacy Sidecar/Lock.ts is gone (#345).
  • Cloudflare state store version bumped to 5 — clients pinned to older versions will refuse to load mismatched state and prompt you to upgrade.
  • scopeTransferToStream reverted — the optimization was causing 415 responses under workerd; reverting restores the previous (correct) HTTP semantics.
  • Axiom smartfilter chart subtype — used by the internal otel dashboard; available now if you build your own Axiom charts via Axiom.Chart (#339).

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