Waku
Source:
src/Cloudflare/Website/Waku.ts
A Cloudflare Worker deployed from a Waku project.
Waku builds the project programmatically — no waku.config.ts edits,
no Wrangler configuration, and no build command required. A project’s
waku.config.* loads natively (same as waku’s CLI) as the base config,
with srcDir/distDir/basePath from this resource winning per key;
unstable_adapter is owned by Alchemy and must not be set. Note that a
standalone vite.config.ts is NOT loaded (same as waku’s CLI) — Vite
config belongs in waku.config.*’s vite field. The RSC server
bundle deploys as the Worker script and the client output (including
SSG-prerendered pages) deploys as static assets.
Requires the @alchemy.run/cloudflare-frameworks package to be installed in
your project; the integration is loaded from its /waku export. Input files
are content-hashed
(respecting .gitignore by default) so unchanged projects skip the
build and deploy entirely.
Waku’s server runtime uses AsyncLocalStorage, so the nodejs_als
compatibility flag is enabled automatically when your compatibility
flags include neither nodejs_als nor nodejs_compat. SSG pages are
served at their extensionless URLs (/about) via the default
drop-trailing-slash asset handling.
Deploying a Waku Site
Section titled “Deploying a Waku Site”A single call builds the project and deploys the RSC server bundle plus the client assets — no configuration required.
Waku site
const site = yield* Cloudflare.Website.Waku("Site");Waku project in a subdirectory
const site = yield* Cloudflare.Website.Waku("Site", { rootDir: "apps/web",});Bindings
Section titled “Bindings”Pass resources through env like any other Worker. Server components
and API routes read them from the cloudflare:workers env at request
time. Prefer a guarded dynamic import in page modules — Waku’s SSG step
renders static pages in Node, where a top-level
import { env } from "cloudflare:workers" cannot resolve.
const bucket = yield* Cloudflare.R2.Bucket("Uploads");
const site = yield* Cloudflare.Website.Waku("Site", { env: { UPLOADS: bucket, },});Custom Worker Entry
Section titled “Custom Worker Entry”By default the deployed Worker entry is Waku’s own RSC server entry.
When the Worker must export more than Waku’s fetch handler — Durable
Object classes, additional handlers — point main at your own module
that wraps Waku’s handler (imported from virtual:waku/server-entry)
and re-exports the extras.
// import wakuHandler from "virtual:waku/server-entry";// export class Counter extends DurableObject { ... }// export default { fetch: (req, env, ctx) => wakuHandler.fetch(req, env, ctx) };
const site = yield* Cloudflare.Website.Waku("Site", { main: "src/worker-entry.ts", env: { COUNTER: Cloudflare.DurableObject("Counter", { className: "Counter", }), },});Custom Rebuild Scope
Section titled “Custom Rebuild Scope”By default, every non-gitignored file is hashed to decide whether a
rebuild is needed. Use memo to narrow the scope when your project has
large directories that don’t affect the build output.
const site = yield* Cloudflare.Website.Waku("Site", { memo: { include: ["src/**", "public/**", "package.json"], },});Class Form
Section titled “Class Form”Calling Waku with no arguments returns a constructor you can extend
to declare the Worker as a named class. The class is both an Effect
you can yield* to deploy and a type you can reference elsewhere —
useful when other resources need to bind to this Worker.
class Site extends Cloudflare.Website.Waku<Site>()("Site") {}
const site = yield* Site;