LangENKO

Edge

`@mandujs/edge` ships edge-runtime adapters for Cloudflare Workers (Phase 15.1, shipped). Deno Deploy, Vercel Edge, and Netlify Edge adapters are scaffolded and land in Phase 15.2+.

since v0.24
On this page

Edge

@mandujs/edge is the Mandu package that lets you deploy the same app to V8-isolate edge runtimes — Cloudflare Workers, Deno Deploy, Vercel Edge, Netlify Edge. One codebase, multiple deploy targets.

Shipping status. Phase 15.1 ships the Cloudflare Workers adapter. Deno Deploy, Vercel Edge, and Netlify Edge are scaffolded stubs landing in Phase 15.2 / 15.3.

# Install
bun add @mandujs/edge

# Wrangler is a peer dep — install separately
bun add -D wrangler

# Build for Workers
mandu build --target=workers

# Dev + deploy via wrangler
wrangler dev
wrangler deploy

Pages

Polyfill mapping

Mandu's runtime paths are already 90% Web Fetch standard. The remaining Bun-specific APIs map to Workers equivalents as follows:

Bun API Workers equivalent Status (15.1)
Bun.serve export default { fetch } Done
Bun.CookieMap LegacyCookieCodec (WebCrypto) Done
Bun.CSRF crypto.subtle HMAC-SHA256 Done
Bun.password @noble/hashes/argon2 (planned) Coming soon
Bun.sql Neon serverless driver / D1 Coming soon
Bun.s3 aws4fetch / R2 binding Coming soon
Bun.file KV binding / build-time inlining Coming soon
Bun.cron Workers Cron Triggers Coming soon
SMTP Permanent skip (use Resend) Not planned

Calling an unsupported Bun API from inside a Worker returns a structured 500 response:

{
  "error": "BunApiUnsupportedOnEdge",
  "api": "Bun.s3",
  "correlationId": "019291f0-4a1c-7f2e-8c9a-...",
  "message": "Bun.s3 is not yet polyfilled for the Workers runtime.",
  "migration_guide": "/docs/edge#unsupported-apis",
  "runtime": "workers"
}

No crash, no silent wrong behavior — a clear, machine-readable refusal pointing at the migration guide.

Why a separate package?

@mandujs/core is Bun-native by default — it ships with Bun.serve, Bun.CookieMap, Bun.password, etc. as the first-class path. @mandujs/edge is opt-in so projects that never touch edge don't pay the polyfill overhead.

Typical package.json:

{
  "dependencies": {
    "@mandujs/core": "^0.24.0"
  }
}

Once you add an edge target:

{
  "dependencies": {
    "@mandujs/core": "^0.24.0",
    "@mandujs/edge": "^0.3.0"
  },
  "devDependencies": {
    "wrangler": "^4.0.0"
  }
}

Edge vs Pages Functions vs Node serverless

Choose the right runtime for the shape of your app:

Runtime Cold start CPU cap (free) Use when
Cloudflare Workers (@mandujs/edge/workers) 1–5ms 10ms Global latency matters; mostly-dynamic
Cloudflare Pages + Functions (mandu deploy --target=cf-pages) 1–5ms 10ms Mostly-static site with a sprinkle of dynamic
Vercel Functions (mandu deploy --target=vercel) ~900ms first 60s Rich Node ecosystem, generous timeouts
Fly.io machines (mandu deploy --target=fly) ~850ms n/a Long-lived processes, stateful

Mandu's runtime-agnostic API surface (contracts, fillings, middleware) works across all four — the adapter is the only thing that changes.

Accessing edge runtime primitives

Each adapter exposes typed accessors for the runtime's native context:

// Cloudflare Workers
import { getWorkersEnv, getWorkersCtx } from "@mandujs/edge/workers";

export async function POST(req: Request) {
  const env = getWorkersEnv();
  const ctx = getWorkersCtx();
  ctx?.waitUntil(env?.ANALYTICS_QUEUE.send({ at: Date.now() }));
  return Response.json({ ok: true });
}

The accessor pattern is consistent across runtimes — Deno Deploy's equivalent will be getDenoServeCtx() etc.

Compatibility testing

mandu build --target=workers runs a compatibility guard during build:

  • Scans every module in the client + server bundles.
  • Flags imports of fs, child_process, net, tls, and any Bun-specific API not yet polyfilled.
  • Emits CLI_E213 as a warning (non-fatal in 15.1; will be upgradable to error via --strict-edge in 15.2).

Run it in CI even if you don't yet deploy to edge — it catches inadvertent drift early.

🤖 Agent Prompt

🤖 Agent Prompt — Edge
Apply the guidance from the Mandu docs page at https://mandujs.com/docs/edge/index to my project.

Summary of the page:
`@mandujs/edge` is a separate package from core — install with `bun add @mandujs/edge`. Polyfill mapping: Bun.CookieMap → LegacyCookieCodec (WebCrypto); Bun.CSRF → crypto.subtle HMAC-SHA256; Bun.serve → `export default { fetch }`. Unsupported APIs return `BunApiUnsupportedOnEdge` 500 JSON. Cloudflare Workers shipped; Deno/Vercel/Netlify coming.

Required invariants — must hold after your changes:
- `@mandujs/edge` is a separate package — not bundled with `@mandujs/core`
- Cloudflare Workers adapter is shipped (Phase 15.1); Deno/Vercel/Netlify Edge are stubs
- Calling an unsupported Bun API inside a Worker returns a structured 500 with `BunApiUnsupportedOnEdge` payload
- Wrangler is a peer dependency — install with `bun add -D wrangler`
- All edge adapters use Mandu's same `manifest.json` + registered handlers — zero app-code changes required

Then:
1. Make the change in my codebase consistent with the page.
2. Run `bun run guard` and `bun run check` to verify nothing
   in src/ or app/ breaks Mandu's invariants.
3. Show me the diff and any guard violations.

For Agents

{
  "schema": "mandu.edge/v0.24",
  "package": "@mandujs/edge",
  "shipped_adapters": ["workers"],
  "roadmap_adapters": ["deno", "vercel-edge", "netlify-edge"],
  "peer_dependency": "wrangler (for Workers)",
  "polyfill_mapping": {
    "Bun.serve": "export default { fetch }",
    "Bun.CookieMap": "LegacyCookieCodec (WebCrypto)",
    "Bun.CSRF": "crypto.subtle HMAC-SHA256",
    "Bun.password": "@noble/hashes/argon2 (planned)",
    "Bun.sql": "Neon / D1 (planned)",
    "Bun.s3": "aws4fetch / R2 binding (planned)",
    "Bun.file": "KV binding / inline (planned)",
    "Bun.cron": "Workers Cron Triggers (planned)",
    "SMTP": "skip permanently — use Resend"
  },
  "unsupported_api_response": {
    "status": 500,
    "error": "BunApiUnsupportedOnEdge"
  },
  "rules": [
    "@mandujs/edge is opt-in — not in core",
    "Use `mandu build --target=workers` — compatibility guard flags edge-unsafe imports",
    "Use `mandu deploy --target=cf-pages` for Pages+Functions (different deploy model)"
  ]
}

For Agents

AI hint

`@mandujs/edge` is a separate package from core — install with `bun add @mandujs/edge`. Polyfill mapping: Bun.CookieMap → LegacyCookieCodec (WebCrypto); Bun.CSRF → crypto.subtle HMAC-SHA256; Bun.serve → `export default { fetch }`. Unsupported APIs return `BunApiUnsupportedOnEdge` 500 JSON. Cloudflare Workers shipped; Deno/Vercel/Netlify coming.

Invariants
  • `@mandujs/edge` is a separate package — not bundled with `@mandujs/core`
  • Cloudflare Workers adapter is shipped (Phase 15.1); Deno/Vercel/Netlify Edge are stubs
  • Calling an unsupported Bun API inside a Worker returns a structured 500 with `BunApiUnsupportedOnEdge` payload
  • Wrangler is a peer dependency — install with `bun add -D wrangler`
  • All edge adapters use Mandu's same `manifest.json` + registered handlers — zero app-code changes required
Guard scope
edge