2.0.0-beta.52 - Dev Servers & Browser Actions
beta.52 is a dev-loop release: alchemy dev can now run your static
site’s own framework dev server instead of rebuilding on every
change, backed by a new Build.DevServer resource that survives HMR
restarts. Browser Rendering quick actions become first-class typed
Effects, and Vite / StaticSite gain the same class form as
Worker.
Local dev servers for StaticSite
Section titled “Local dev servers for StaticSite”Cloudflare.StaticSite grows a dev prop. On deploy nothing
changes — the build command runs and the output directory ships as
Worker assets. Under alchemy dev, the build is skipped and
dev.command is spawned as your local server instead:
const site = yield* Cloudflare.StaticSite("Website", { command: "zola build", outdir: "public", dev: { command: "zola serve", }, assets: { notFoundHandling: "404-page", },});site.url is the deployed URL on deploy and the local dev server’s
URL in dev. The repo’s cloudflare-static-site example was rebuilt
around Zola to show the flow.
Under the hood this is a new Build.DevServer resource: a long-lived
shell process scoped to the stack instance, started during
alchemy dev, restarted when its inputs change, and a no-op on
deploy. It runs inside the dev sidecar so it survives user-code HMR
restarts, mirrors the child’s stdout/stderr to your terminal, and
scans output for the first http(s):// URL, exposed as its url
output:
const dev = yield* DevServer("Frontend", { command: "npm run dev",});console.log(dev.url); // e.g. "http://localhost:5173"For servers that don’t print a URL, StaticSite’s dev.url prop sets
it explicitly.
(#537)
One assets shape everywhere
Section titled “One assets shape everywhere”Worker, StaticSite, and Vite previously took three different
assets shapes (assets.config, assetsConfig, and
{ path, hash, config }). Now AssetsProps extends AssetsConfig, so
routing options like runWorkerFirst, htmlHandling, and
notFoundHandling sit directly on assets for all three:
const app = yield* Cloudflare.Vite("SolidStart", { compatibility: { flags: ["nodejs_compat"], }, assets: { config: { runWorkerFirst: true, }, runWorkerFirst: true, },});For StaticSite, rename the assetsConfig: key to assets:.
(#534)
Effect-native Browser quick actions
Section titled “Effect-native Browser quick actions”The BrowserClient returned by yield* Cloudflare.Browser(...) now
mirrors Cloudflare’s Browser Rendering quick actions as typed Effect
operations. JSON actions (content, markdown, links, scrape,
snapshot, json) resolve to their parsed success payloads and fail
with a typed BrowserError; binary actions (screenshot, pdf)
return a Stream of the response bytes. No more hand-rolling
Response.json() against the raw binding:
const browser = yield* Cloudflare.Browser({ name: "BROWSER" });const url = "https://example.com";
// JSON quick actions resolve to their parsed payload.const markdown = yield* browser.markdown({ url });const { result: links } = yield* browser.links({ url });
// Binary actions stream bytes — collect or pipe them.const png = yield* browser.screenshot({ url }).pipe(Stream.runCollect);Provide Cloudflare.BrowserBindingLive on the Worker’s init Effect.
raw and fetch remain as escape hatches for promise-based
libraries like @cloudflare/puppeteer.
(#525)
Class form for Vite and StaticSite
Section titled “Class form for Vite and StaticSite”Cloudflare.Vite and Cloudflare.StaticSite support the same
zero-arg class-constructor form as Cloudflare.Worker: calling with
no arguments returns a constructor you extend to declare the site
as a named class — both a yieldable Effect and a type other resources
can reference:
export class Website extends Cloudflare.Vite<Website>()("Website", { compatibility: { flags: ["nodejs_compat"], }, assets: { runWorkerFirst: true, },}) {}
export type WebsiteEnv = Cloudflare.InferEnv<typeof Website>;
// in the stack:const site = yield* Website;The cloudflare-tanstack example uses it.
(#527)
Also in this release
Section titled “Also in this release”- Version Metadata binding —
yield* Cloudflare.VersionMetadata()attaches Cloudflare’sversion_metadatabinding and returns an accessor for the deployed version’sid,tag, andtimestamp; also declarable onenv. Thanks Alex (#524). - Workflow runs get a per-invocation
ExecutionContext(scope + cache), so binding helpers likeDrizzle.postgresresolve inside workflow steps just like in a fetch or queue handler. Thanks Saatvik Arya (#515). retainremoval policy is honored on replace — the old generation of a replaced resource is retained, not just orphan deletes (#548).- Changing a
Redactedconfig/secret value now triggers an update — redacted wrappers are unwrapped during diffing (#549). - Local dev fixes — the assets binding is populated for Vite websites (#547), cross-script Durable Object bindings work (#540), and multi-worker dev is more reliable in bun (#535).
- Drizzle
Schemaexposesoutas a cwd-relative path so drizzle-kit resolves migrations regardless of where alchemy runs (#551). - The Vite SPA tutorial documents the local dev workflow (#526).
@distilled.cloud/*bumped to 0.23.1 (#539).