@reearth/zushi
v0.2.1
Published
A framework-agnostic plugin runtime: run untrusted plugin code in an isolated WASM backend (QuickJS built in), expose a host-defined API, and render plugin UI in sandboxed iframes.
Keywords
Readme
zushi
A framework-agnostic plugin runtime for the browser. Run untrusted plugin code
in an isolated WASM backend — a built-in QuickJS
backend runs JavaScript, with room for other guest languages — expose a
host-defined API into it, and render plugin UI inside sandboxed <iframe>s (or,
opt-in, on a host <canvas>).
The name comes from zushi (厨子), a small Japanese cabinet that enshrines a precious object behind doors you open only when needed — much like a host that encloses an external module and opens it to render on demand.
Why
Running third-party plugin code safely in a web app needs two layers of isolation, and zushi packages both plus the wiring between them:
- A language VM behind a pluggable
Backend(QuickJS for JavaScript by default) — plugin logic never touches the host realm (nowindow,document,fetch) unless you hand it over. - Sandboxed iframes for plugin UI — opaque origin,
postMessage-only.
Only data crosses these boundaries; code and live references do not.
Install
npm install @reearth/zushiQuick start
import { Plugin, quickjs } from "@reearth/zushi";
const plugin = new Plugin({
backend: quickjs(), // the execution backend (QuickJS WASM VM)
surfaces: { main: { container: document.getElementById("plugin-ui")! } },
code: `
// Runs inside the VM — no DOM access.
reearth.ui.show("<h1>Hello from a plugin</h1>");
host.greet("world");
`,
// Build the globals the plugin sees (only \`console\` is provided by default).
exposed: ({ surfaces }) => ({
reearth: { ui: surfaces.main.api },
host: { greet: (name: string) => console.log(`plugin greeted ${name}`) }
})
});
await plugin.start();
// ...later
plugin.dispose();Using React? PluginView / usePlugin (@reearth/zushi/react) tie a plugin to
a component's lifecycle. Want declarative, component-based UI instead of HTML
strings? Opt into the JSX runtime with jsx: true. Drawing to a canvas? Swap in
a host-direct renderer (e.g. react-konva). All covered in the docs below.
Documentation
Full docs live in docs/:
- Getting started · Concepts (architecture, event loop, messaging, lifecycle)
- Core — Backends · Surfaces & iframes · Exposing a host API
- JSX UI (opt-in) — Overview · Runtime API reference · Placement · Synced state · Components & intrinsics · Renderers (canvas, etc.)
- Integration — React adapter · Storage & events · Security model
Exact option/type signatures are documented inline via TSDoc and shipped as
.d.ts — let your editor's hover/autocomplete be the reference.
Examples
pnpm example # Vite dev server: React, vanilla, JSX runtime, and a canvas rendererSee examples/ — one file per case. The vanilla example's plugin
source is also driven by a real-browser regression test
(examples/regression.browser.test.ts),
so it doubles as an end-to-end check of the whole pipeline.
Testing
pnpm test— unit/logic tests in jsdom (VM behavior is real QuickJS; iframe DOM stubbed).pnpm test:browser— real-browser tests (Vitest browser mode / Playwright Chromium): realsrcdoc, injected scripts,postMessage,ResizeObserverauto-resize, sandbox isolation, and the examples end-to-end. Runnpx playwright install chromiumonce.pnpm test:all— both.
License
MIT
