Repository navigation
computer: Add an experimental dynamic JavaScript backend - #250
Draft
mattzcarey wants to merge 3 commits into
Draft
mattzcarey wants to merge 3 commits into
mattzcarey wants to merge 3 commits into
Conversation
馃 Changeset detectedLatest commit: 2552a45 The changes in this PR will be included in the next version bump. This PR includes changesets to release 4 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
commit: |
mattzcarey
force-pushed
the
computer/dynamic-javascript-backend
branch
from
October 19, 2026 13:54
feddf43 to
2c7956d
Compare
WorkerJavaScriptBackend checked its options and built its description in its constructor, and its handle was private to the module. A second backend that runs code the same way had to copy all of it. The option checks and defaults move into resolveJavaScriptBackendOptions, which names the calling backend in its errors, and the description into describeJavaScriptBackend, which takes extra paragraphs. The handle is exported inside the package and takes an optional extension: host functions the bridge serves without caller source being able to import them by specifier, generated modules every execution's bundle holds, and a hook that replaces a Workspace file in the module graph with generated source. The module graph gains those as internalModules and replaceModule, and runtimeModules for runtime modules code may import as written, such as cloudflare:workers. WorkerJavaScriptBackend uses none of them and behaves as before.
Code run by the JavaScript backend starts from nothing each time. To keep state between runs, a model had to write it to files and read it back. Cloudflare OS has a better shape for this: the agent writes a Durable Object class, the host runs it as a facet of itself, and one-off code calls methods on it. DynamicJavaScriptBackend runs code exactly as WorkerJavaScriptBackend does, through the same handle, and adds apps. An app is a Workspace file whose default export extends DurableObject from cloudflare:workers. When code imports one, the module graph swaps in ctx.facets for that app, with one facet per instance name. The runtime doesn't let code construct a DurableObjectFacets, so the import copies its shape: get(name) returns an instance whose method calls cross the bridge to the host, and abort(name, reason), delete(name) and clone(source, destination) act on instances. The host runs each instance as a facet named after the backend, the app's path and the instance's name. It loads the app's module graph in a Dynamic Worker whose main module subclasses the app with one dispatcher method. The dispatcher installs a bridge for that call, so node:fs and host modules work inside the app's methods under the backend's limits, refuses private and inherited methods, and checks the result is plain data. Every call hashes the app's graph and aborts the facet when the code has changed, so the next call runs the new code against the same storage. An instance runs one call at a time, and a clone waits for both instances. A caller whose call passes its deadline stops waiting, and the instance keeps running. A read-only backend refuses app calls, since they write storage. The workerd tests cover state across runs, separate instances, code changes, abort, delete and clone, Workspace access from an app, the method guard, plain-data checks, the facets object, read-only backends, deadlines, and two backends sharing one host.
mattzcarey
force-pushed
the
computer/dynamic-javascript-backend
branch
from
October 19, 2026 14:04
2c7956d to
dc57eb4
Compare
Add a page for DynamicJavaScriptBackend: how to set it up, what makes a Workspace file an app, how code gets an instance and calls it, when an instance restarts and what survives, and the limits on calls. Link it from the execution and isolate JavaScript pages, and list the backend as experimental in the package README. The page also records the planned design for app interfaces, so later changes can follow it: a host-side ctx.facets whose instances are RpcTargets, served to an agents SDK client over its Cap'n Web transport and on into a sandboxed iframe through a MessagePort.
mattzcarey
force-pushed
the
computer/dynamic-javascript-backend
branch
from
October 19, 2026 14:54
dc57eb4 to
2552a45
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds
DynamicJavaScriptBackendat@cloudflare/computer/backends/dynamic-javascript, marked experimental. It runs code likeWorkerJavaScriptBackend, and it also runs a Workspace file whose default export extendsDurableObjectas a long-lived app: code imports the file and calls methods on an instance, and each instance runs as a facet of the host Durable Object with its own SQLite storage.Code run by the JavaScript backend starts from nothing on every run. Cloudflare OS has a shape for state that lasts: the agent writes a Durable Object class, the host runs it as a facet, and one-off code calls it. This copies that shape. It is separate from durable replay in #238 and changes no existing backend.
ctx.facetsfor it, one facet per instance name. The runtime doesn't let code construct aDurableObjectFacets, so it copies the shape:get,abort,deleteandcloneon a prototype namedDurableObjectFacets.gettakes no startup options, and each method call on an instance crosses the existing bridge as plain datanode:fs, source modules andws:*host modules work inside its methods, under the backend's limitsNew workerd tests in
vitest.config.dynamic-javascript.ts, plus unit tests for detecting an app.npm run check, the package build, andnpm test --workspace @cloudflare/computerpass. Docs are indocs/20_dynamic_javascript.md.Follow-up, designed but not built (the "Planned: app interfaces" section of the docs page): app interfaces over RPC, as Cloudflare OS does it.
backend.facets(path)whose instances areRpcTargets using the same dispatcher, bridge and queue as calls from code.openAppcallable on its Cap'n Web transport, behind ashow_apptool.MessagePort, so the user and the agent share one live instance.