Skip to content

Use process shim in WorkerJavaScriptBackend - #37

Open
aron-cf wants to merge 11 commits into
stack/2-worker-javascript-examplefrom
stack/3-process-shim
Open

Use process shim in WorkerJavaScriptBackend#37
aron-cf wants to merge 11 commits into
stack/2-worker-javascript-examplefrom
stack/3-process-shim

Conversation

@aron-cf

@aron-cf aron-cf commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Code running in the worker-javascript backend had no process. A module could not read an environment variable, consume standard input, or write to standard output and standard error the way ordinary Node-style code expects. This change installs a process shim in the runner and threads the pieces it needs through the execution path.

The caller supplies env and stdin on the exec options. These flow through the runtime router and into the runner, which installs globalThis.process before user code runs. Only the values the caller passes are visible through process.env; the host environment is never exposed. process.stdin reads back the caller's bytes as a non-interactive, always-ends stream, bounded by a new maxStdinBytes ceiling. process.cwd() returns the execution's working directory, and process.argv and process.platform carry inert defaults.

Output is captured with stream identity preserved. console.log and console.info and direct process.stdout writes route to standard output; console.warn, console.error, and process.stderr writes route to standard error. Each is byte-accurate: a raw write is not given a trailing newline it did not have, and a console line keeps the one it adds. The capture stays bounded by the existing per-stream byte and event ceilings.

The client-facing exec options also gained env and stdin, so callers reaching the runtime through a Workspace stub can pass them; both survive the capnweb boundary unchanged. The worker-javascript example forwards env and stdin from its exec route, and its README shows a recipe that pipes standard input and reads an environment value.

Verify with npm test --workspace @cloudflare/computer, which exercises the shim end to end against the real Worker Loader, or post a module to the example that reads process.env and process.stdin.

@pkg-pr-new

pkg-pr-new Bot commented Aug 1, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/cloudflare/computer/@cloudflare/computer@37

commit: 4e782d0

aron-cf added 11 commits August 1, 2026 16:53
Several documents still pointed at the pre-rename example directory and
source tree. Update the project layout tree to worker-shell and
worker-javascript, and repoint the worker backend doc and the package
README at examples/worker-shell.
Mirror the worker-shell example with a Durable Object whose Workspace
runs an ECMAScript module in a Dynamic Worker through the
WorkerJavaScriptBackend. The DO class, HTTP routes, and R2 mount match
worker-shell; the difference is the backend and what exec runs.

The backend takes only the Loader binding, needs no
WorkspaceServiceProxy loopback, and requires waitUntil, so the DO
passes ctx.waitUntil.bind(ctx) into the Workspace options. The exec
route accepts a module source plus an optional structured input and
returns the run's status, streams, and the module's structured value.

List the new example in the project layout doc.
The runtime rejected structured input for any command-routed backend,
conflating two independent questions: which downstream surface a
backend speaks, and whether it accepts a structured input value and
returns a structured result. A shell backend that coerced JSON to
argv and parsed stdout back to a value could not opt in.

Add an optional callable capability to the backend interfaces, default
false. The WorkerJavaScriptBackend declares it true. The runtime router
now gates input on a callableBackendIds set built from that capability,
independent of command-versus-module routing, and reports a clearer
"not callable" error.
Explain callable next to the id and type discussion in the package
README: a callable backend accepts a structured input value and returns
a structured value, the runtime rejects input for a non-callable
backend, and the capability is independent of the backend kind.
…cript backend

Executions now accept an env map that surfaces inside the module as
process.env. The runtime forwards env through the module exec path, the
backend passes it as evaluate context alongside the working directory,
and the runner installs a process object before importing user code.

Only caller-supplied variables are visible; the host process
environment is never exposed. The runner replaces globalThis.process,
falling back to defineProperty and then to reassigning process.env when
the nodejs_compat global resists replacement, so the override holds
whether or not the runtime pre-populated the global.
…ript backend

Executions accept a stdin value, passed as bytes or a string, that the
module reads through process.stdin. The runtime forwards it down the
module exec path, the backend normalizes it to bytes, bounds it by a
new maxStdinBytes option, and hands it to the runner as evaluate
context. The runner shims process.stdin as a non-TTY async iterable
that yields the supplied bytes once and then ends, so both for-await
and manual iteration read to completion.
…te streams

The runner captured console output into a single list tagged with
textual level prefixes, which the backend mapped back to stdout or
stderr. Replace that with stream-tagged capture: console.log and
console.info record to stdout, console.warn and console.error to
stderr, and process.stdout.write and process.stderr.write record their
raw bytes to the matching stream without an added newline.

Console lines carry their own trailing newline, so the backend now
emits each captured entry verbatim rather than appending one. The
per-stream byte and event ceilings and the truncation marker are
preserved.
Add an end-to-end case asserting process.cwd() tracks the exec cwd and
that argv and platform carry their inert defaults, pinning the shim
surface that the env, stdin, and stdout work left untested.
The host and stub exec paths gained env and stdin, but the
client-facing RuntimeExecOptions still omitted them, so callers reaching
the runtime through getWorkspace(stub) could not pass either. Add both
fields; the client already forwards the whole options object, and env
maps and byte stdin survive the capnweb boundary unchanged.
The exec handler now forwards env and stdin from the request body into
runtime.exec, so a posted module can read process.env and process.stdin.
Document the extended request shape and add a smoke-test recipe that
pipes stdin and reads an env value.
Add a section to the JavaScript runtime doc describing the process shim:
caller-supplied env exposed through process.env with the host
environment hidden, caller-supplied stdin as a non-interactive
async-iterable bounded by maxStdinBytes, stdout and stderr capture with
console routing, and inert argv, cwd, and platform. List maxStdinBytes
among the configurable byte limits.

Add a test asserting an oversized stdin fails the run before the loader
is invoked.
@aron-cf
aron-cf force-pushed the stack/3-process-shim branch from 4e782d0 to 9cc0f86 Compare August 1, 2026 17:31
function errorJSON(error: unknown, status: number): Response {
const message = error instanceof Error ? error.message : String(error);
const code = (error as { code?: string }).code;
return new Response(JSON.stringify({ error: message, code }), {
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants