The js-host-api crate uses a clean naming convention where:
- Rust types are named
*Wrapperto clearly indicate they wrap the core types - JavaScript types use clean names matching the underlying Hyperlight types
This is achieved using the #[napi(js_name = "...")] attribute.
| Rust Type (Internal) | JavaScript Type (Exported) | Wraps |
|---|---|---|
SandboxBuilderWrapper |
SandboxBuilder |
SandboxBuilder |
ProtoJSSandboxWrapper |
ProtoJSSandbox |
ProtoJSSandbox |
JSSandboxWrapper |
JSSandbox |
JSSandbox |
LoadedJSSandboxWrapper |
LoadedJSSandbox |
LoadedJSSandbox |
SnapshotWrapper |
Snapshot |
Snapshot |
InterruptHandleWrapper |
InterruptHandle |
Arc<dyn InterruptHandle> |
CallHandlerOptions |
CallHandlerOptions |
N/A (plain object) |
/// JavaScript-friendly wrapper for SandboxBuilder
#[napi(js_name = "SandboxBuilder")]
pub struct SandboxBuilderWrapper {
inner: Arc<Mutex<Option<SandboxBuilder>>>,
}
#[napi]
impl SandboxBuilderWrapper {
#[napi(constructor)]
pub fn new() -> Self {
Self {
inner: Arc::new(Mutex::new(Some(SandboxBuilder::new()))),
}
}
/// Builder is consumed on build — Arc<Mutex<Option>> enables
/// one-shot consumption from &self (required by NAPI).
#[napi]
pub async fn build(&self) -> napi::Result<ProtoJSSandboxWrapper> {
let builder = self.take_inner()?;
// ...
}
}- Clear Rust Code: The
Wrappersuffix makes it obvious these are NAPI wrappers - Clean JavaScript API: Users see natural type names like
SandboxBuilder,JSSandbox, etc. - Matches Core Library: JavaScript types mirror the underlying Hyperlight types
- Maintainability: Easy to distinguish wrapper code from core library code
- Documentation: Type names in docs match what users actually use
const { SandboxBuilder } = require('@hyperlight-dev/js-host-api');
// Build the sandbox pipeline
const builder = new SandboxBuilder();
const protoSandbox = await builder.build();
const jsSandbox = await protoSandbox.loadRuntime();
// Register handlers before loading
jsSandbox.addHandler('echo', 'function handler(event) { return event; }');
const loadedSandbox = await jsSandbox.getLoadedSandbox();
// Execute a handler using its routing key — pass objects in, get objects back
const result = await loadedSandbox.callHandler('echo', { message: 'hello' });
// Execute with monitoring (recommended)
const guarded = await loadedSandbox.callHandler('echo', { message: 'hello' }, {
wallClockTimeoutMs: 5000,
cpuTimeoutMs: 3000,
});TypeScript definitions (index.d.ts) are auto-generated by napi build --platform
from the #[napi] attributes in src/lib.rs. Do not edit index.d.ts
manually — it will be overwritten on the next build.
To regenerate after changing the Rust API:
cd src/js-host-api && npx napi build --platformWhen adding new wrapper types, follow this pattern:
#[napi(js_name = "YourTypeName")]
pub struct YourTypeNameWrapper {
inner: Arc<Mutex<Option<YourTypeName>>>,
}This ensures:
- Rust code is explicit about wrapper types
- JavaScript gets clean, idiomatic names
Arc<Mutex<Option<_>>>enables one-shot consumption from&self(NAPI requirement)- Consistency across the codebase