Important
PREVIEW ONLY This package is provided as a preview for feedback only. APIs are unstable and the design is subject to change.
A Cloudflare Worker + Durable Object that runs a Workspace whose
runtime evaluates ECMAScript modules in a Dynamic Worker
loaded through env.LOADER. It mirrors
examples/worker-shell beat for beat — same DO
class, same routes, same R2 mount — but exec runs a JavaScript
module instead of a just-bash command.
client ─► Worker /c/<name>/{file,exec}
│ (DO RPC calls)
â–¼
DO (ContainerExample) ──► Workspace ──► WorkerJavaScriptBackend
│
│ env.LOADER.load(...)
â–¼
Dynamic Worker
(module runner)
│
│ evaluate(input, bridge)
â–¼
host capability bridge
(fs, git, artifacts)
- The DO constructs a
WorkerJavaScriptBackendfrom@cloudflare/computer/backends/worker-javascript, passing only the Loader binding. Unlike the shell backend there is noWorkspaceServiceProxyloopback: the backend is self-contained and reaches the host through theWorkspaceRuntimeBridgeit builds internally. - Each
exec(source, { backend: "worker-javascript", input })builds a module graph from the source, mints a fresh Dynamic Worker throughenv.LOADER.load(...), and calls the runner'sevaluate(input, bridge). The module's default export receives the structuredinputand its return value becomes the result'svalue. - Filesystem, Git, and artifacts operations from inside the module round-trip through the bridge to the host DO's own store, so storage handles stay valid and one workspace per DO is the natural boundary.
BackendHandle.syncis"none". There's a single authoritative store (the DO's SQLite); push and pull short-circuit.pushed/pulledare always zero.
The DO is a thin host; the Dynamic Worker lifecycle is the loader's problem. A run keeps advancing while its event stream is consumed, so the request that drains the handle holds the object resident until the run finishes.
PUT /c/<name>/file/workspace/hello.txt writes
/workspace/hello.txt, and
GET /c/<name>/file/workspace/r2/x reads /workspace/r2/x —
the URL and the on-disk path always match. Any URL outside
/workspace returns 400.
exec runs with cwd defaulting to /workspace. The source is
an ECMAScript module; its default export may be a function that
receives the request's input value and returns a
JSON-compatible result. The Dynamic Worker has
globalOutbound: null, so the module can't reach the public
internet on its own.
Identical to the worker-shell example. Seed once with:
npm run seed:r2:local --workspace @example/computer-worker-javascript
# or after deploy
npm run seed:r2 --workspace @example/computer-worker-javascriptPUT /c/<name>/file/workspace/<path> raw body → writeFile at /workspace/<path>
GET /c/<name>/file/workspace/<path> octet-stream of /workspace/<path>
(any path outside /workspace returns 400)
POST /c/<name>/exec { source, input?, cwd?, env?, stdin? }
cwd defaults to /workspace
→ JSON { status, exitCode, stdout, stderr, value }
No Docker, no extra build step. The module runner ships inside
@cloudflare/computer/backends/worker-javascript;
WorkerJavaScriptBackend mints the Dynamic Worker through the
Loader binding internally so the DO constructor stays a one-line
backend invocation.
npm run dev --workspace @example/computer-worker-javascriptSmoke test:
curl http://127.0.0.1:8787/
echo 'hello' | curl -X PUT --data-binary @- \
http://127.0.0.1:8787/c/demo/file/workspace/hello.txt
curl http://127.0.0.1:8787/c/demo/file/workspace/hello.txt
curl -X POST http://127.0.0.1:8787/c/demo/exec \
-H 'content-type: application/json' \
-d '{"source":"import { readFile } from \"node:fs/promises\";\nexport default async () => (await readFile(\"/workspace/hello.txt\", \"utf8\")).trim();"}'
curl -X POST http://127.0.0.1:8787/c/demo/exec \
-H 'content-type: application/json' \
-d '{"source":"export default (input) => input.n * 2;","input":{"n":21}}'
curl -X POST http://127.0.0.1:8787/c/demo/exec \
-H 'content-type: application/json' \
-d '{"source":"export default async () => { let s = \"\"; for await (const c of process.stdin) s += new TextDecoder().decode(c); return process.env.WHO + \":\" + s; };","env":{"WHO":"demo"},"stdin":"piped"}'env populates process.env (only the values you pass; the host
environment is never exposed), and stdin is readable through
process.stdin. console.log / console.error and
process.stdout / process.stderr writes come back as the result's
stdout and stderr.
examples/worker-javascript/
wrangler.jsonc Worker + DO + worker_loaders binding
src/index.ts Worker handler + DO (ContainerExample)
Nothing else. The Dynamic Worker module runner ships from
@cloudflare/computer/backends/worker-javascript.
- Exec is run-and-collect. The handler awaits
handle.result()and emits one JSON response. - One execution at a time by default. The backend admits a single execution per workspace unless configured otherwise, and bounds completed-execution retention by time and count.