箱 (Hako) means "box" in Japanese
Embeddable, lightweight, secure, high-performance JavaScript engine for .NET
Hako is an embeddable JavaScript engine that brings modern ES2023+ JavaScript execution to your .NET applications. Built on 6over3's fork of QuickJS, Hako compiles QuickJS to WebAssembly and hosts it safely within your .NET process.
Hako supports the ES2023 specification and Phase 4 proposals including modules, asynchronous generators, top-level await, proxies, BigInt, and built-in TypeScript support.
Hako for .NET is the official .NET host implementation that provides:
- Secure Execution: JavaScript runs in a WebAssembly sandbox with configurable memory limits, execution timeouts, and resource controls
- High Performance: Powered by QuickJS compiled to WASM, with multiple backend options (Wasmtime, WACS)
- Modern JavaScript: Full ES2023+ support including async/await, modules, promises, classes, and more
- TypeScript Support: Built-in type stripping for TypeScript code (type annotations are removed at runtime)
- Top-Level Await: Use await at the module top level without wrapping in async functions
- Deep .NET Integration: Expose .NET functions to JavaScript, pass complex types bidirectionally, and marshal data seamlessly
- Lightweight: Minimal dependencies, small runtime footprint
- Developer Friendly: Source generators for automatic binding, rich extension methods
dotnet add package Hako
dotnet add package Hako.Backend.Wasmtimeusing Hako;
using Hako.Backend.Wasmtime;
using Hako.Extensions;
// Initialize the runtime
using var runtime = Hako.Initialize<WasmtimeEngine>();
using var realm = runtime.CreateRealm()
.WithGlobals(g => g.WithConsole());
// Execute JavaScript with type-safe results
var result = await realm.EvalAsync<int>(@"
const numbers = [1, 2, 3, 4, 5];
const sum = numbers.reduce((a, b) => a + b, 0);
console.log('Sum:', sum);
sum;
");
Console.WriteLine($"Result from JS: {result}"); // 15
await Hako.ShutdownAsync();Read the full technical documentation →
Use await directly at the module top level without wrapping in async functions:
var result = await realm.EvalAsync<string>(@"
const response = await fetch('https://api.example.com/data');
const data = await response.json();
data.message;
", new() { FileName = "app.js", Async = true });Hako automatically strips TypeScript type annotations when you use a .ts file extension:
var result = await realm.EvalAsync<int>(@"
interface User {
name: string;
age: number;
}
function greet(user: User): string {
return `${user.name} is ${user.age} years old`;
}
const alice: User = { name: 'Alice', age: 30 };
console.log(greet(alice));
alice.age + 12;
", new() { FileName = "app.ts" });You can also manually strip types:
var typescript = "const add = (a: number, b: number): number => a + b;";
var javascript = runtime.StripTypes(typescript);All JavaScript execution happens inside a WebAssembly sandbox. The WASM runtime provides memory isolation, preventing JavaScript from accessing host memory outside its allocated boundaries.
var runtime = Hako.Initialize<WasmtimeEngine>(opts => {
opts.MemoryLimitBytes = 50 * 1024 * 1024; // 50 MB max
opts.MaxStackSize = 1024 * 1024; // 1 MB stack
});
realm.SetInterruptHandler(InterruptHandlers.Deadline(
TimeSpan.FromSeconds(5) // 5 second timeout
));JavaScript can only access .NET functionality you explicitly expose:
realm.WithGlobals(g => g
.WithConsole()
.WithFunction("readFile", ReadFileFunction)
.WithFunction("allowedAPI", AllowedAPIFunction));
// No file system, no network, no reflection—unless you provide it┌─────────────────────────────────────────┐
│ Your .NET Application │
├─────────────────────────────────────────┤
│ Hako Runtime API │
│ (Realm, JSValue, Module System, etc.) │
├─────────────────────────────────────────┤
│ Backend Abstraction │
│ ┌──────────────┬──────────────┐ │
│ │ Wasmtime │ WACS │ │
│ │ Backend │ Backend │ │
│ └──────────────┴──────────────┘ │
├─────────────────────────────────────────┤
│ Hako Engine (compiled to WASM) │
│ ES2023+ JavaScript │
└─────────────────────────────────────────┘
This repository contains multiple packages:
| Package | Description | NuGet |
|---|---|---|
| Hako | Core JavaScript runtime and APIs | |
| Hako.Backend.Wasmtime | wasmtime backend | |
| Hako.Backend.WACS | WACS backend | |
| Hako.SourceGenerator | Automatic binding generator | |
| Hako.Backend | Backend abstraction interfaces |
- ES2023 specification and Phase 4 proposals
- Top-level await
- TypeScript type stripping (not type checking)
- Async/await and Promises
- Asynchronous generators
- ES6 Modules (import/export)
- Proxies and BigInt
- Timers (setTimeout, setInterval, setImmediate)
- Expose .NET functions to JavaScript
- Expose .NET classes to JavaScript ([JSClass] source generation)
- Marshal complex types bidirectionally
- Custom module loaders
- Bytecode compilation and caching
- Multiple isolated realms
- Memory and execution limits
- Rich extension methods for safe API usage
- No reflection. AOT is fully supported. See backends for more information.
- Technical Documentation - Complete API reference and usage guide
- Hako Project - Main Hako project organization
- Blog: Introducing Hako - Design philosophy and architecture
- GitHub Issues - Bug reports and feature requests
- NuGet Packages - Download packages
Check out the examples/ directory for complete samples:
- basics - Hello world and basic evaluation
- host-functions - Exposing .NET functions to JavaScript
- classes - Creating JavaScript classes backed by .NET
- modules - ES6 module system and custom loaders
- typescript - TypeScript type stripping
- marshaling - Complex type marshaling between .NET and JS
- timers - setTimeout, setInterval, and event loop
- iteration - Iterating over arrays, maps, and sets
- scopes - Resource management with DisposableScope
- safety - Memory limits, timeouts, and sandboxing
- raylib - Full game engine bindings example
Licensed under the Apache License 2.0. See LICENSE for details.
Built for the .NET community