Skip to content

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. The RSC server bundle deploys as the Worker script and the client output (including SSG-prerendered pages) deploys as static assets.

Requires the @distilled.cloud/waku package to be installed in your project (alongside waku itself). 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.

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",
});

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,
},
});

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.

src/worker-entry.ts
// 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",
}),
},
});

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"],
},
});

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;