CellB

Workers

A Worker is a project’s main module: its fetch handler answers the requests the runtime receives. CellB loads a project from its wrangler configuration (wrangler.jsonc plus its main) unchanged: loadNodeProject in Node bundles main with esbuild, as celld deploy does, and browserProjectRuntime runs it in a browser worker. A project with assets and no main is a static site.

{ "name": "hello", "main": "index.js", "compatibility_date": "2026-01-01" }
export default {
  async fetch(request, env) {
    return new Response("Hello from cells! url=" + request.url, {
      status: 200,
    });
  },
};

This is celld’s hello example (conformance/celld-examples/hello), copied unchanged. The conformance suite runs it on CellB in Node and in Chromium and compares the answers with celld’s recording (conformance.md).

A Worker gets the answers celld gives

  • An uncaught error answers 500, content-type: application/json, with the body Worker failed: rejected: <name>: <message>. A handler that returns something other than a Response is sent as its text with status 200.
  • An inbound request has request.cf set to {}.
  • A promise the Worker leaves rejected is the Worker’s affair. On Node, where the Worker shares the host’s process, CellB sends it to onUnhandledRejection (by default, one line on stderr), not to the process’s unhandledRejection listeners.
  • new Response("text") carries text/plain;charset=UTF-8, as the Fetch standard adds it; celld sends no Content-Type.
  • The Cache API (caches) always misses, as on celld, which has no shared edge cache: put() consumes the response and stores nothing, match() resolves to undefined.
  • HTMLRewriter is lol-html compiled to WASM (Miniflare’s html-rewriter-wasm), loaded on first use.

Node and browsers give a Worker different globals

  • In Node the project’s Worker shares the host’s realm and process. process.env is the host’s, and code that writes to globalThis affects the whole runtime (security.md).
  • In a browser, process.env holds the project’s text vars under nodejs_compat. node:fs and node:os load with no host behind them (existsSync is false, os.cpus() is empty); other Node.js modules without a browser polyfill load as stubs that throw when called.
  • Firefox has no streaming request body. CellB gives Worker code a request.body that reads the whole body first; forwarding it as another request’s body sends the text [object ReadableStream] in Firefox (gaps.md).

How it is checked

celld’s hello, webapi, router and body examples, and CellB’s worker-details, runtime-api and node-compat projects, on Node and in Chromium.

What breaks

  • Configuration CellB does not support is reported, not ignored, in createProjectRuntime(...).unsupported: service bindings to other scripts, durable_objects.bindings[].script_name, migrations.renamed_classes, containers, compatibility flags CellB does not know. Browser tabs log it as cellb: project … shard …: unsupported: ….
  • Python Workers do not run: CellB runs no Pyodide.