5. phaze-cloudflare
1. phaze-tsplugin ← editor (TS Language Service)2. phaze-compile ← build-time AST rewriting ├── babel-plugin.ts ← the actual Babel plugin (all the visitors live here) └── vite-plugin.ts ← thin wrapper that adapts it for Vite3. phaze-vite ← island HMR + chunking helpers4. phaze-astro ← Astro integration (island model)5. phaze-cloudflare ← native Cloudflare Workers adapter (whole-page) ← you are here@madenowhere/phaze-cloudflare is the native Cloudflare Workers adapter — the alternative to phaze-astro (#4) when your deployment target is Workers and you don’t want an Astro layer. You install it instead of phaze-astro (you pick one host adapter per app); both are build-time only — neither grows the sub-3 KB phaze runtime chunk that ships to the browser.
Where phaze-astro is islands (<Component client:load/> mounts independently), phaze-cloudflare is whole-page: the entire page SSRs and hydrates as one reactive tree. The full feature surface — file-system routing (groups, slots, restricted +name/ folders, status-code pages, co-located layouts), transport, middleware, auth + restricted-route RBAC, caching/ISR, the client router, device variants, env, cookies, prefetch, … — lives in Phaze + Cloudflare; this page is the tooling view — what the package is and how it plugs into Vite.
One Vite plugin
Section titled “One Vite plugin”The consumer’s vite.config.ts needs a single import:
import cloudflare from '@madenowhere/phaze-cloudflare/vite'
export default defineConfig({ plugins: [cloudflare({ pages: 'src/pages', prerender: ['/', '/about'] })],})cloudflare() returns a Vite plugin array. For builds it is [phazeCompile(), main] — it bundles phaze-compile so you don’t wire it yourself. In serve mode it additionally composes @cloudflare/vite-plugin plus a cf-imports / cf-externals pair (see the dev modes below). You don’t add phazeVite(): its per-component replace() HMR is the island model, and phaze-cloudflare’s whole-page hydration has no per-island boundary to target (its dev HMR works differently — below). phazeChunks() is applied internally to both build environments.
The plugin owns: file-system routing under src/pages, the virtual server/client entries (__phaze/server / __phaze/client), ambient env/action type-gen (.phaze/types.d.ts), server-entry codegen, the dual-environment build, and the dev runtime.
The dual-environment build
Section titled “The dual-environment build”vite build --app runs two Vite 8 environments in one process:
client→dist/client/assets/*.js(hashed browser bundle) +.vite/manifest.json.ssr→dist/server/index.js— a single self-contained Worker (codeSplitting: false— Vite 8’s non-deprecated form of the oldinlineDynamicImports: true— everythingnoExternal), ready forwrangler deploy.
Optional prerender: [...] paths render to static HTML in closeBundle (production builds only).
Two dev modes — both real workerd
Section titled “Two dev modes — both real workerd”Unlike Astro’s single dev server, phaze-cloudflare offers two single-process dev loops, both serving real workerd with real bindings (D1 / KV / R2 / cloudflare:sockets, .dev.vars secrets, shared .wrangler/state/v3):
pnpm dev→vite build --app --watch+ in-process Miniflare.closeBundlestarts (ormf.setOptions-hot-swaps) an in-process Miniflare against the freshly built worker. Full reload per rebuild (~200–1500 ms), build-grade output (real CSS<link>s, shaped assets, the size report). Replaces the old two-processvite build --watch+wrangler dev.pnpm dev:hmr→ a Vite dev server. Serve mode composes@cloudflare/vite-plugin, so the worker runs inside Miniflare via Vite 8’s Environment-API module runner (needs@cloudflare/vite-plugin≥ 1.41, the Rolldown-aware runner) — real workerd and live module updates. The client gets true HMR (import.meta.hot); phaze adds Tier-1 page-level HMR by making the client entry the accept boundary (App-subtree edits re-render the root, page edits swap the active page reactively).
The transport layer’s Rust half in dev (rustApi)
Section titled “The transport layer’s Rust half in dev (rustApi)”Phaze Transport’s endpoints are canonically Rust — they live in a sibling worker (rust-api/, its own wrangler dev) that the app reaches by a public fetch to RUST_API, mirroring production. In dev that half has to be running, or every rust: proxy, lang: rust hoist, and /api/* call fails. cloudflare({ rustApi }) folds it into pnpm dev:hmr, so one command brings up both halves of the one transport:
cloudflare({ rustApi: 'rust-api' }) // string = the sibling worker's dir; { dir, port } to overrideOn dev-server start the plugin runs that dir’s own dev script (honouring its --port + --persist-to ../.wrangler/state, so D1 / KV / sessions are the same local store as the app worker), gates on /api/health, and kills it on server close — port-guarded, so an already-running instance is reused instead of double-spawned. Startup prints one line of the transport surface plus a codegen-drift check:
[transport] rust-api ↑ :8788 · 19 actions · 2 streams · 5 signed · 5 encrypted[transport] api.gen.ts in sync # or ⚠ drift → pnpm check:apiIt’s dev plumbing, not a new concept: rust-api stays the Rust half of the unified transport — consumed through phaze:transport / the generated api.gen.ts, never a separate API you fetch directly. Omit rustApi and you start the worker yourself (pnpm -C rust-api dev). Serve-mode only (dev:hmr); pnpm dev (build-watch) has no dev-server hook to attach to. It does not check key alignment across the two workers — the app and rust-api can legitimately run separate signing keypairs.
Why it’s separate from phaze-astro
Section titled “Why it’s separate from phaze-astro”Same reasoning as the rest of the stack: different host, different deps. phaze-astro plugs into Astro’s integration API and pulls in Astro’s types; phaze-cloudflare plugs straight into Vite and pulls in @cloudflare/vite-plugin, miniflare, and wrangler. An Astro app never installs miniflare/wrangler; a Cloudflare app never installs Astro. Keeping them as distinct packages (#4 and #5) means each app’s dependency tree carries only its target’s host glue — and, since both are build-time, neither affects the shipped runtime byte budget.
Where to go from here
Section titled “Where to go from here”- Phaze + Cloudflare — the full adapter surface: file-system routing (groups / slots / restricted
+name/folders / status pages / co-located layouts), transport, middleware, auth + RBAC, caching/ISR, the client router, device variants, env, cookies, prefetch, the two dev modes in depth, and the native-vs-Astro performance comparison. - phaze-cloudflare source on GitHub —
plugin.tsis the whole adapter (routing, codegen, build config, Miniflare dev runtime) in one file.