Skip to main content

Module rquickjs

Module rquickjs 

Source
Expand description

QuickJS-backed implementation of the workflow JavaScript runtime boundary.

Runtime overview:

  1. Create a restricted QuickJS context with only the intrinsics needed by the workflow sandbox (Promise, Proxy, JSON, collections, regexps, etc.). QuickJS’s eval intrinsic is still required for host-owned source/module evaluation, but user-visible dynamic evaluation is disabled before user workflow code runs. Node/browser/host globals are not provided and a second hardening pass replaces or hides known escape hatches such as eval, Function, Date, host IO globals, Date, and Math.random.

  2. Evaluate sandbox_prelude.js. The prelude intentionally contains only the small JS-native pieces that are easier to express in JavaScript: the readonly Proxy factory plus pure helper globals like parallel and pipeline. Rust captures the temporary __readonly helper and removes it before user workflow code runs.

  3. Install Rust-owned workflow globals (args, budget, agent, workflow, log, and phase). Rust exposes two different kinds of protection and both are required:

    • Global/property binding protection is done with define_readonly_data_property, which defines non-writable, non-configurable properties. Use this for public globals, hidden bootstrap helpers, disabled host globals, and host object properties like Math.random.
    • Object/value mutation protection is done with the captured readonly proxy. Use this for mutable-looking objects exposed from Rust, such as args and budget, so nested writes, new properties, deletes, and prototype changes throw instead of mutating the underlying object.

    A protected global binding alone is not enough for object values: it stops globalThis.args = ..., but not args.nested.value = .... Therefore any object/array exposed from Rust that should be immutable to workflow code must be wrapped with readonly_proxy before it is installed or passed to user code.

    agent(...) and workflow(...) do not perform provider work inside QuickJS; they create pending JS promises, enqueue Rust-side requests, and save the JS resolve/reject functions for later.

  4. Declare and evaluate the user source as a real ES module. Module evaluation is represented by a QuickJS promise so top-level await is naturally supported. Literal top-level return is not supported because the source is parsed as ESM.

  5. Once module evaluation resolves, Rust reads the module namespace and starts the default export. Function defaults are called as default(args, ctx), where ctx contains the workflow helpers. Value or promise defaults are used directly. Rust normalizes both forms into a single workflow promise.

  6. Polling drains the QuickJS job queue, emits queued calls/requests to the workflow core, and completes when the workflow promise resolves. The core later resolves requests through resolve_request, which resumes the saved JS promises by calling their captured resolve/reject functions.

Structs§

RQuickJSWorkflowRuntime
Workflow JavaScript runtime backed by QuickJS via rquickjs.