@gorules/quickjs
v0.4.0
Published
High-performance, sandboxed JavaScript runtime for Node.js and browsers, powered by QuickJS and Rust
Readme
@gorules/quickjs
A high-performance, sandboxed JavaScript runtime for Node.js and browsers, powered by QuickJS and built with Rust via napi-rs.
Features
- Sandboxed Execution - Run untrusted JavaScript code safely with isolated contexts
- Resource Constraints - Limit execution time, memory usage, and stack size
- HTTP Client - Built-in
httplibrary for making HTTP requests - Custom Modules - Define and import ES6 modules dynamically
- Async/Await - Full support for asynchronous JavaScript
- Cross-Platform - Works on macOS, Linux, Windows, and browsers (WASM)
Installation
npm install @gorules/quickjs
# or
yarn add @gorules/quickjsQuick Start
import { JavascriptRunner } from '@gorules/quickjs';
import { createFetchAdapter } from '@gorules/quickjs/fetch-adapter';
const runner = new JavascriptRunner({
fetch: createFetchAdapter(),
});
// Evaluate simple expressions
const result = await runner.evaluate('1 + 1');
console.log(result); // 2
// Evaluate complex scripts
const data = await runner.evaluate(`
const users = [
{ name: 'Alice', age: 30 },
{ name: 'Bob', age: 25 }
];
users.filter(u => u.age > 26).map(u => u.name)
`);
console.log(data); // ['Alice']
// Clean up when done
runner.dispose();API Reference
JavascriptRunner
The main class for evaluating JavaScript code.
interface JavascriptRunnerOptions {
fetch: (url: string, options?: FetchOptions) => Promise<FetchResponse>;
modules?: Array<JavascriptModule>;
maxPoolSize?: number;
memoryLimit?: number; // Memory limit in bytes
maxStackSize?: number; // Stack size limit in bytes
maxDuration?: number; // Execution timeout in milliseconds
}
const runner = new JavascriptRunner(options);Methods
evaluate(script: string): Promise<any>- Execute JavaScript code and return the resultdispose(): void- Clean up resources
JavascriptModule
Define custom modules that can be imported in evaluated scripts.
interface NewJavascriptModuleOptions {
name: string;
source: string;
}
const module = new JavascriptModule({ name: 'myModule', source: '...' });
// or create multiple at once
const modules = JavascriptModule.fromList([...]);Usage Examples
HTTP Requests
The runtime provides a built-in http object for making HTTP requests:
const runner = new JavascriptRunner({
fetch: createFetchAdapter(),
});
const result = await runner.evaluate(`
const response = await http.get("https://api.example.com/users");
response.data
`);Supported methods:
http.get(url, options?)- GET requesthttp.post(url, body?, options?)- POST requesthttp.put(url, body?, options?)- PUT requesthttp.delete(url, options?)- DELETE request
Request options:
{
headers: { "Authorization": "Bearer token" },
params: { "q": "search", "limit": "10" },
baseURL: "https://api.example.com"
}Response format:
{
status: 200,
statusText: "OK",
headers: { "content-type": "application/json" },
data: { ... } // Parsed JSON or string
}Custom Modules
Define reusable modules that scripts can import:
import { JavascriptRunner, JavascriptModule } from '@gorules/quickjs';
const modules = JavascriptModule.fromList([
{
name: 'utils',
source: `
export const double = x => x * 2;
export const capitalize = s => s.charAt(0).toUpperCase() + s.slice(1);
`
},
{
name: 'api',
source: `
export async function fetchUsers() {
const response = await http.get("https://api.example.com/users");
return response.data;
}
`
}
]);
const runner = new JavascriptRunner({
fetch: createFetchAdapter(),
modules,
});
const result = await runner.evaluate(`
const { double } = await import('utils');
const { fetchUsers } = await import('api');
const users = await fetchUsers();
users.map(u => ({ ...u, score: double(u.score) }))
`);Resource Constraints
Protect against runaway scripts with execution limits:
const runner = new JavascriptRunner({
fetch: createFetchAdapter(),
maxDuration: 1000, // 1 second timeout
memoryLimit: 10 * 1024 * 1024, // 10 MB memory limit
maxStackSize: 512 * 1024, // 512 KB stack size
});
// This will throw an error after 1 second
try {
await runner.evaluate('while(true) {}');
} catch (error) {
console.log('Script interrupted:', error.message);
}
// This will throw a memory error
try {
await runner.evaluate(`
const arr = [];
while(true) { arr.push("x".repeat(10000)); }
`);
} catch (error) {
console.log('Memory limit exceeded:', error.message);
}Async Operations
Full support for async/await and Promises:
const result = await runner.evaluate(`
async function processData() {
const [users, posts] = await Promise.all([
http.get("https://api.example.com/users"),
http.get("https://api.example.com/posts")
]);
return {
userCount: users.data.length,
postCount: posts.data.length
};
}
await processData()
`);A sleep function is also available:
await runner.evaluate(`
await sleep(100); // Sleep for 100ms
"done"
`);Browser Usage (WASM)
The package includes WASM support for browser environments:
import { JavascriptRunner } from '@gorules/quickjs/browser';
// The WASM module loads asynchronously
const runner = new JavascriptRunner({
fetch: async (url, options) => {
const res = await fetch(url, options);
return {
status: res.status,
statusText: res.statusText,
headers: Object.fromEntries(res.headers),
data: await res.text(),
};
},
});
const result = await runner.evaluate('2 + 2');Execution Isolation
Each call to evaluate() runs in an isolated context. Variables defined in one evaluation are not accessible in subsequent evaluations:
await runner.evaluate('var x = 100');
const result = await runner.evaluate('typeof x');
console.log(result); // 'undefined'Platform Support
| Platform | Architecture | Support | |----------|--------------|---------| | macOS | x64, ARM64 | Native | | Linux | x64, ARM64 (glibc) | Native | | Linux | x64, ARM64 (musl) | Native | | Windows | x64 | Native | | Browser | WASM | WASM |
Built with Rust
The core runtime is implemented in Rust using:
This provides excellent performance while maintaining memory safety.
License
MIT
