Heximon Logo
Deploy

Cloudflare

CloudflareWorkersStrategy, CloudflarePlugin, wrangler.json as a build artifact, dist/worker.js, Durable Object migrations, the Miniflare vp dev host.

Cloudflare is the one platform with a full local emulator — vp dev boots real workerd via Miniflare, so every dev request runs the exact edge path the deploy will, Durable Objects included. It's also the only platform where wrangler.json itself is generated: a compiler plugin collects every binding, migration, and cron trigger your app declares and writes one config, regardless of which bundler built it.

Configure the strategy

heximon.config.ts
import { defineHeximonConfig } from "@heximon/build";
import { CloudflareWorkersStrategy } from "@heximon/cloudflare";
import { HttpPlugin } from "@heximon/http/compiler";

export default defineHeximonConfig({
  platform: new CloudflareWorkersStrategy(),
  plugins: [new HttpPlugin()],
});

CloudflareWorkersStrategy is an edge-class strategy with platform name "cloudflare". Setting it injects two things for you: the CloudflareWorkerPlugin worker-entry host and the CloudflarePlugin wrangler.json emitter — you only list your own concept plugins (HttpPlugin, DurablePlugin, SchedulePlugin, …).

What vp build emits

terminal
vp build                                    # → dist/worker.js + dist/wrangler.json
wrangler deploy --config dist/wrangler.json

vp build writes a complete, deploy-shaped dist/wrangler.json alongside dist/worker.js. It auto-discovers a committed root wrangler.{jsonc,json,toml} as a base, folds every Heximon-owned section over it, and fills in the deploy-only fields it can derive — mainworker.js, a default compatibility_date, and name from package.json — each only when the base omits it. Commit the root file for what Heximon cannot know: account_id, vars, a pinned compatibility_date.

The compiler ALSO emits a partial wrangler.json into the gitignored .heximon/ output during a normal compile (DO bindings + migrations only, no main) — the bundler-agnostic artifact any host produces identically. vp build's complete dist/wrangler.json supersedes it.

The merge rules

CloudflarePlugin collects every cloudflare-targeted contribution other plugins publish and merges them over your optional base config, deterministically and additively:

  • Durable Object bindings — deduped by name; the base entry wins on a collision.
  • Migrations — deduped by tag; the base entry wins on a collision.
  • Cron triggers (triggers.crons) — deduped by expression; the base entry wins.
  • Everything else in the base passes through untouched.

So your committed account_id, vars, and any external bindings survive every rebuild, while the sections Heximon owns refresh automatically.

heximon.config.ts
import { defineHeximonConfig } from "@heximon/build";
import { CloudflareWorkersStrategy } from "@heximon/cloudflare";
import { HttpPlugin } from "@heximon/http/compiler";
import { DurablePlugin } from "@heximon/durable/compiler";
import { SchedulePlugin } from "@heximon/schedule/compiler";

export default defineHeximonConfig({
  platform: new CloudflareWorkersStrategy(),
  plugins: [new HttpPlugin(), new DurablePlugin(), new SchedulePlugin()],
  cloudflare: {
    queues: [{ queue: "ticketing-outbox", binding: "OUTBOX_QUEUE" }],
  },
});

cloudflare.kvNamespaces, cloudflare.queues, and cloudflare.hyperdrive in heximon.config.ts declare the deploy settings the compiler can't derive from source alone — each becomes its matching wrangler.json section (kv_namespaces, queues.producers/.consumers, hyperdrive).

The two generated modules

CloudflareWorkerPlugin emits two files, not one:

  • host-boot.js — the fully-bound boot recipe: bootHost(env, bindings?, options?) captures env, supplies every host binding the app graph needs (the in-process InternalClient loop-back, a materialized CoreConfig), boots the app, and mounts the WebSocket/SSE upgrade routes. It's importable on its own because a Durable Object may run in a different isolate than the fetch handler — the DO bridge boots through this recipe rather than a bare createApp() call.
  • worker-entry.js — the lean export default { fetch[, scheduled][, queue] } the platform actually loads, driven by a per-isolate memoized bootOnce(env, ctx) over the same boot recipe.

Durable Objects

The generated worker entry bridges every listed DO to workerd's RPC requirements automatically — there's nothing to write for it.

DurablePlugin discovers your DO classes (plus any shipped from a composed library boundary), emits their durable_objects bindings and an ordered migrations ledger (grouped by an optional migration: "v2" config member; malformed tags are a build diagnostic), and folds them into the wrangler.json the CloudflarePlugin writes. Listing the worker host without CloudflarePlugin compiles a warning (cloudflare.wrangler-emitter-missing) — the Worker still builds, but Durable Objects fail under both vp dev and a real deploy with no bindings emitted.

Feature support

Cloudflare is the widest-supported edge target: Durable Objects, workflows (native Cloudflare Workflows binding), cron (native scheduled trigger, always UTC — the platform evaluates every cron trigger in UTC regardless of a declared timezone), queues (native Cloudflare Queues, push-triggered), cross-service fan-out (either the per-service-queue split or the opt-in DO-router broadcast leg), and saga timeouts (a Durable Object alarm room).

Dev on real workerd

Set the strategy and vp dev serves the app in Miniflare — actual workerd, not a Node shim:

vite.config.ts
import heximon from "@heximon/build/vite";
import { defaultServerConditions, defineConfig } from "vite-plus";

export default defineConfig({
  plugins: [heximon()],
  resolve: { conditions: ["development", ...defaultServerConditions] },
});

The dev host bundles the emitted worker-entry.js, boots workerd with every declared binding provisioned from the merged wrangler config, and splices raw ws:// upgrades directly onto workerd's socket — so WebSocket routes, Durable Object rooms included, connect under vp dev exactly as they would on a real deploy.

Ship it

wrangler deploy --config dist/wrangler.json

CloudflareWorkersStrategy also implements the @heximon/deploy capability, so heximon deploy runs that same command for you (plus a wrangler whoami preflight, and --provision for Cloudflare Queues and runtime secrets via wrangler secret put). Teardown: wrangler delete.

See also

  • Durable Objects — the DO concept, config, and DI wiring.
  • Schedule — cron handlers and the ScheduledHandler base.
  • Deploy overview — strategies, vp build, and heximon deploy across all platforms.
  • heximon deploy — the CLI's Cloudflare-specific preflight and provisioning flags.
Copyright © 2026