Skip to content

Node.js Runtime

The Node.js runtime (@xriptjs/runtime-node) executes user scripts inside a sandboxed Node.js vm context. It provides createRuntimeFromFile for loading manifests directly from disk. Use this runtime when your application runs exclusively on Node.js and you want file-based workflows.

For applications that need to run in browsers, Deno, Bun, or other environments, use the JS/WASM Runtime (@xriptjs/runtime) instead. For Rust host applications, see the Rust Runtime. For .NET applications, see the C# Runtime. For a comparison of all runtimes, see Choosing a Runtime.

Terminal window
npm install @xriptjs/runtime-node
import { createRuntime } from "@xriptjs/runtime-node";
const runtime = createRuntime(manifest, {
hostBindings: { greet: (name) => `Hello, ${name}!` },
});

createRuntime performs basic structural validation on the manifest (required fields, correct types) and returns a runtime immediately.

import { createRuntimeFromFile } from "@xriptjs/runtime-node";
const runtime = await createRuntimeFromFile("./manifest.json", {
hostBindings: { greet: (name) => `Hello, ${name}!` },
});

createRuntimeFromFile reads the manifest from disk, performs structural validation, and creates a runtime. For full JSON Schema validation, use @xriptjs/validate before creating the runtime.

createRuntime and createRuntimeFromFile accept the same options:

OptionTypeDefaultDescription
hostBindingsHostBindings(required)Map of binding names to host functions
capabilitiesstring[][]List of capabilities granted to this script
console{ log, warn, error }no-op functionsConsole output routing
const result = runtime.execute("2 + 2");
// { value: 4, duration_ms: 0.5 }
const result = await runtime.executeAsync("return await data.get('score');");
// { value: "42", duration_ms: 1.2 }

executeAsync wraps code in an async function. Use return and await as needed.

Call dispose() when you are done with a runtime:

runtime.dispose();

The Node.js runtime does not require explicit cleanup, but dispose() is provided for API parity with the universal runtime. Code written against one runtime works identically on the other.

The Node.js runtime exports the same error classes as the universal runtime:

ErrorWhenCatchable in script?
ManifestValidationErrorManifest fails structural or schema validationN/A (thrown at load time)
BindingErrorHost function throws or is missingYes
CapabilityDeniedErrorCalling a gated binding without the capabilityYes
ExecutionLimitErrorScript exceeds timeout or resource limitsNo
import {
BindingError,
CapabilityDeniedError,
ExecutionLimitError,
} from "@xriptjs/runtime-node";

The Node.js runtime creates a vm.Context with a restricted global environment:

  • Code generation disabled: vm.createContext is configured with codeGeneration: { strings: false, wasm: false }. This blocks eval() and new Function() at the V8 level.
  • Standard globals available: Math, JSON, Date, Number, String, Boolean, Array, Object, Map, Set, Promise, RegExp, Symbol, Proxy, Reflect, typed arrays, and standard error constructors
  • Blocked: process, require, import, fetch, setTimeout, setInterval, Buffer, __dirname, __filename
  • Frozen namespaces: Namespace objects are frozen with Object.freeze
  • Execution limits: Timeout enforced via vm.Script.runInContext timeout option

runtime.loadMod(modManifest, options?) validates a mod manifest against the app manifest, sanitizes any fragment HTML, and returns a ModInstance. If the mod manifest declares an entry script, that script runs during loading.

const mod = runtime.loadMod(modManifest, { sources: fragmentSources });
console.log(mod.name, mod.version);
console.log(mod.fragments.length);

fragmentSources is an object mapping fragment IDs to their raw HTML strings. The runtime sanitizes each source before attaching it to the mod.

runtime.fireFragmentHook(fragmentId, lifecycle, bindings?) fires a lifecycle hook registered by the active mod script and returns any command buffer operations the script issued. Supported lifecycles: mount, unmount, update, suspend, resume.

const ops = runtime.fireFragmentHook("health-bar", "update", { health: 75 });
// ops is an array of command arrays: [["setText", ".hp", "75"], ...]

Each entry in ops is a command array whose first element is the command name followed by its arguments. The host applies these operations to the rendered fragment.

runtime.processFragment(fragmentId, source, bindings) evaluates data-bind and data-if attributes in the fragment HTML against the provided binding data and returns the resolved output.

const { html, visibility } = runtime.processFragment("health-bar", source, {
health: 75,
maxHealth: 100,
});

html is the processed HTML string with data-bind values substituted. visibility is a map of element selectors to boolean values derived from data-if expressions.