Getting Started
This guide walks through adding xript to an application from scratch. By the end, you will have a working sandboxed expression evaluator that users can extend safely.
Install the Runtime
Section titled “Install the Runtime”The universal runtime uses QuickJS compiled to WebAssembly: it works in browsers, Node.js, Deno, and more.
npm install @xriptjs/runtimeWrite a Manifest
Section titled “Write a Manifest”Create a manifest.json describing what your application exposes to scripts. Start with a few safe bindings:
{ "$schema": "https://xript.dev/schema/manifest/v0.1.json", "xript": "0.1", "name": "my-app", "version": "1.0.0", "bindings": { "greet": { "description": "Returns a greeting for the given name.", "params": [{ "name": "name", "type": "string" }], "returns": "string" }, "add": { "description": "Adds two numbers.", "params": [ { "name": "a", "type": "number" }, { "name": "b", "type": "number" } ], "returns": "number" } }}Only xript and name are required. Everything else is optional and layered on as needed.
Provide Host Bindings
Section titled “Provide Host Bindings”Each binding in the manifest needs a host-side implementation. These are regular JavaScript functions:
const hostBindings = { greet: (name) => `Hello, ${name}!`, add: (a, b) => a + b,};Create a Runtime
Section titled “Create a Runtime”Initialize the WASM sandbox, then wire the manifest and bindings together:
import { initXript } from "@xriptjs/runtime";
const xript = await initXript();const runtime = xript.createRuntime(manifest, { hostBindings, console: { log: console.log, warn: console.warn, error: console.error },});initXript() loads the QuickJS WASM module once. After that, createRuntime() is synchronous: create as many runtimes as you need.
Execute Scripts
Section titled “Execute Scripts”Now you can safely evaluate user expressions:
runtime.execute('greet("World")'); // { value: "Hello, World!", duration_ms: ... }runtime.execute('add(2, 3)'); // { value: 5, duration_ms: ... }runtime.execute('add(1, add(2, 3))'); // { value: 6, duration_ms: ... }Scripts can compose your bindings with standard JavaScript:
runtime.execute('[1, 2, 3].map(n => add(n, 10))'); // { value: [11, 12, 13], ... }Clean Up
Section titled “Clean Up”When you’re done with a runtime, free its WASM resources:
runtime.dispose();See the Sandbox in Action
Section titled “See the Sandbox in Action”Anything not declared in the manifest is inaccessible:
runtime.execute('process.exit(1)'); // Error: process is not definedruntime.execute('require("fs")'); // Error: require is not definedruntime.execute('eval("1 + 1")'); // Error: eval() is not permittedruntime.execute('fetch("https://x")'); // Error: fetch is not definedThe sandbox guarantees that user scripts cannot escape the boundaries you define.
Next Steps
Section titled “Next Steps”- Scaffold a new project with
npx @xriptjs/init. See Init CLI. - Add capabilities to gate sensitive operations. See the Capabilities spec.
- Add namespaces to organize related bindings. See the Manifest spec.
- Generate TypeScript definitions from your manifest with
xript-typegen. See Type Generator. - Generate documentation from your manifest with
xript-docgen. See Doc Generator. - Use the Node.js runtime for file-based workflows and native V8 performance. See Node.js Runtime.
- Explore the runtime API in depth. See JS/WASM Runtime.
- Run the full example in the repository:
examples/expression-evaluator/.