denox
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseDenox — Embed Deno JS/TS Runtime in Elixir
Denox — 在Elixir中嵌入Deno JS/TS运行时
Overview
概述
Denox embeds a Deno V8 runtime into Elixir via a Rustler NIF. Evaluate JS/TS, load ES modules, import from CDNs/npm/jsr, and call functions across the boundary — all in-process.
Denox通过Rustler NIF将Deno V8运行时嵌入到Elixir中。您可以执行JS/TS代码、加载ES模块、从CDN/npm/jsr导入资源,以及跨边界调用函数——所有操作都在进程内完成。
When to Use
适用场景
- Evaluating JavaScript or TypeScript from Elixir
- Loading ES modules with /
importexport - Using npm/jsr packages in Elixir applications
- Running async JS (Promises, dynamic )
import() - Calling Elixir functions from JavaScript (callbacks)
- Pre-initializing V8 state with snapshots
- 从Elixir中执行JavaScript或TypeScript代码
- 加载支持/
import的ES模块export - 在Elixir应用中使用npm/jsr包
- 运行异步JS代码(Promises、动态)
import() - 从JavaScript中调用Elixir函数(回调)
- 使用快照预初始化V8状态
Quick Reference
快速参考
Installation
安装
elixir
undefinedelixir
undefinedmix.exs
mix.exs
{:denox, "~> 0.2.0"}
Requires Rust (stable). First compile ~20-30 min (V8 builds from source).{:denox, "~> 0.2.0"}
需要Rust(稳定版)。首次编译约20-30分钟(V8从源码构建)。Core API
核心API
| Function | Purpose | Event Loop |
|---|---|---|
| Create V8 isolate | — |
| Eval JS, return JSON string | No |
| Transpile+eval TS (no type-check) | No |
| Eval with Promises/ | Yes |
| Async TS eval | Yes |
| Eval, discard result ( | No |
| Load ES module file | Yes |
| Read+eval file (no import/export) | No |
| Call named JS function | No |
| Call async JS function | Yes |
| Eval + | No |
| TS eval + decode | No |
| Call + decode | No |
| Async call + decode | Yes |
All functions return . The variants return .
{:ok, result} | {:error, message}exec:ok | {:error, message}| 函数 | 用途 | 事件循环 |
|---|---|---|
| 创建V8隔离环境 | — |
| 执行JS代码,返回JSON字符串 | 否 |
| 转译并执行TS代码(无类型检查) | 否 |
| 执行包含Promises/ | 是 |
| 异步执行TS代码 | 是 |
| 执行代码,丢弃结果(返回 | 否 |
| 加载ES模块文件 | 是 |
| 读取并执行文件(不支持import/export) | 否 |
| 调用命名JS函数 | 否 |
| 调用异步JS函数 | 是 |
| 执行代码并通过 | 否 |
| 执行TS代码并解析结果 | 否 |
| 调用函数并解析结果 | 否 |
| 异步调用函数并解析结果 | 是 |
所有函数返回。系列函数返回。
{:ok, result} | {:error, message}exec:ok | {:error, message}Runtime Options
运行时选项
elixir
Denox.runtime(
base_dir: "lib/js", # resolve relative module imports
sandbox: true, # disable fs/net extensions
cache_dir: "_denox/cache", # disk cache for remote modules
import_map: %{"utils" => "file:///path/to/utils.js"},
callback_pid: pid, # enable JS→Elixir callbacks
snapshot: snapshot_bytes # V8 snapshot for fast cold start
)elixir
Denox.runtime(
base_dir: "lib/js", # 解析相对模块导入路径
sandbox: true, # 禁用文件系统/网络扩展
cache_dir: "_denox/cache", # 远程模块的磁盘缓存目录
import_map: %{"utils" => "file:///path/to/utils.js"},
callback_pid: pid, # 启用JS→Elixir回调
snapshot: snapshot_bytes # 用于快速冷启动的V8快照
)Sync vs Async
同步 vs 异步
Use / for simple expressions. Use / when code contains:
evalcalleval_asynccall_async- or Promises
await - Dynamic
import() - /event-loop-dependent code
setTimeout
elixir
undefined简单表达式使用/。当代码包含以下内容时,使用/:
evalcalleval_asynccall_async- 或Promises
await - 动态
import() - 或依赖事件循环的代码
setTimeout
elixir
undefinedSync — fast, no event loop
同步 — 速度快,无事件循环
{:ok, "3"} = Denox.eval(rt, "1 + 2")
{:ok, "3"} = Denox.eval(rt, "1 + 2")
Async — pumps event loop
异步 — 驱动事件循环
{:ok, "42"} = Denox.eval_async(rt, "return await Promise.resolve(42)")
undefined{:ok, "42"} = Denox.eval_async(rt, "return await Promise.resolve(42)")
undefinedTypeScript
TypeScript支持
Transpile-only via deno_ast/swc. No type-checking (same as without ). Type errors like transpile without error.
deno run--checkconst x: string = 42elixir
{:ok, "42"} = Denox.eval_ts(rt, "const x: number = 42; x")通过deno_ast/swc仅进行转译,不做类型检查(与不带参数的行为一致)。类似的类型错误会被直接转译,不会报错。
--checkdeno runconst x: string = 42elixir
{:ok, "42"} = Denox.eval_ts(rt, "const x: number = 42; x")Function Calls
函数调用
Define functions in JS, call from Elixir with JSON-serializable args:
elixir
Denox.exec(rt, "globalThis.add = (a, b) => a + b")
{:ok, "5"} = Denox.call(rt, "add", [2, 3])
{:ok, 5} = Denox.call_decode(rt, "add", [2, 3])在JS中定义函数,从Elixir传入可JSON序列化的参数进行调用:
elixir
Denox.exec(rt, "globalThis.add = (a, b) => a + b")
{:ok, "5"} = Denox.call(rt, "add", [2, 3])
{:ok, 5} = Denox.call_decode(rt, "add", [2, 3])ES Modules
ES模块
elixir
undefinedelixir
undefinedLoad module with import/export support
加载支持import/export的模块
{:ok, rt} = Denox.runtime(base_dir: "/path/to/project")
{:ok, _} = Denox.eval_module(rt, "/path/to/project/main.ts")
undefined{:ok, rt} = Denox.runtime(base_dir: "/path/to/project")
{:ok, _} = Denox.eval_module(rt, "/path/to/project/main.ts")
undefinedCDN Imports
CDN导入
elixir
{:ok, rt} = Denox.runtime(cache_dir: "_denox/cache")
{:ok, result} = Denox.eval_async(rt, """
const { z } = await import("https://esm.sh/zod@3.22");
return z.string().parse("hello");
""")Must use — dynamic returns a Promise.
eval_asyncimport()elixir
{:ok, rt} = Denox.runtime(cache_dir: "_denox/cache")
{:ok, result} = Denox.eval_async(rt, """
const { z } = await import("https://esm.sh/zod@3.22");
return z.string().parse("hello");
""")必须使用——动态会返回Promise。
eval_asyncimport()Runtime Pool
运行时池
For concurrent workloads (V8 isolates are single-threaded):
elixir
undefined针对并发工作负载(V8隔离环境是单线程的):
elixir
undefinedSupervision tree
监督树
children = [{Denox.Pool, name: :js_pool, size: 4}]
children = [{Denox.Pool, name: :js_pool, size: 4}]
Usage (round-robin)
使用(轮询调度)
{:ok, result} = Denox.Pool.eval(:js_pool, "1 + 2")
Denox.Pool.load_npm(:js_pool, "priv/bundles/zod.js") # load into all
Pool options: `:name` (required), `:size` (default: schedulers count), plus all runtime options.{:ok, result} = Denox.Pool.eval(:js_pool, "1 + 2")
Denox.Pool.load_npm(:js_pool, "priv/bundles/zod.js") # 加载到所有实例
池选项:`:name`(必填)、`:size`(默认:调度器数量),以及所有运行时选项。JS → Elixir Callbacks
JS → Elixir回调
elixir
{:ok, rt, handler} = Denox.CallbackHandler.runtime(
callbacks: %{
"greet" => fn [name] -> "Hello, #{name}!" end,
"add" => fn [a, b] -> a + b end
}
)
{:ok, _} = Denox.eval(rt, ~s[Denox.callback("greet", "Alice")])Callback functions receive a list of decoded JSON arguments.
elixir
{:ok, rt, handler} = Denox.CallbackHandler.runtime(
callbacks: %{
"greet" => fn [name] -> "Hello, #{name}!" end,
"add" => fn [a, b] -> a + b end
}
)
{:ok, _} = Denox.eval(rt, ~s[Denox.callback("greet", "Alice")])回调函数接收已解码的JSON参数列表。
V8 Snapshots
V8快照
Pre-initialize global state for instant startup:
elixir
{:ok, snap} = Denox.create_snapshot("globalThis.helper = (x) => x * 2")
{:ok, rt} = Denox.runtime(snapshot: snap)
{:ok, "10"} = Denox.call(rt, "helper", [5])预初始化全局状态以实现即时启动:
elixir
{:ok, snap} = Denox.create_snapshot("globalThis.helper = (x) => x * 2")
{:ok, rt} = Denox.runtime(snapshot: snap)
{:ok, "10"} = Denox.call(rt, "helper", [5])TypeScript snapshots
TypeScript快照
{:ok, snap} = Denox.create_snapshot("globalThis.add = (a: number, b: number) => a + b", transpile: true)
undefined{:ok, snap} = Denox.create_snapshot("globalThis.add = (a: number, b: number) => a + b", transpile: true)
undefinedDependency Management (Denox.Deps
)
Denox.Deps依赖管理(Denox.Deps
)
Denox.DepsRequires CLI at build-time only.
denoelixir
undefined仅在构建时需要 CLI。
denoelixir
undefinedInstall from deno.json
从deno.json安装依赖
Denox.Deps.install()
Denox.Deps.install()
Add/remove deps
添加/移除依赖
Denox.Deps.add("zod", "npm:zod@^3.22")
Denox.Deps.remove("zod")
Denox.Deps.add("zod", "npm:zod@^3.22")
Denox.Deps.remove("zod")
List deps
列出依赖
{:ok, imports} = Denox.Deps.list()
{:ok, imports} = Denox.Deps.list()
Create runtime with installed deps
使用已安装的依赖创建运行时
{:ok, rt} = Denox.Deps.runtime()
Mix tasks: `mix denox.install`, `mix denox.add <name> <spec>`, `mix denox.remove <name>`.{:ok, rt} = Denox.Deps.runtime()
Mix任务:`mix denox.install`、`mix denox.add <name> <spec>`、`mix denox.remove <name>`。Pre-Bundling (Denox.Npm
)
Denox.Npm预打包(Denox.Npm
)
Denox.NpmBundle npm packages into self-contained JS files (requires CLI):
denoelixir
Denox.Npm.bundle!("npm:zod@3.22", "priv/bundles/zod.js")
:ok = Denox.Npm.load(rt, "priv/bundles/zod.js")将npm包打包为独立的JS文件(需要 CLI):
denoelixir
Denox.Npm.bundle!("npm:zod@3.22", "priv/bundles/zod.js")
:ok = Denox.Npm.load(rt, "priv/bundles/zod.js")Telemetry
遥测
Events:
Types: , , , , ,
[:denox, :eval, :start | :stop | :exception]:eval:eval_ts:eval_async:eval_ts_async:eval_module:eval_file事件:
类型:、、、、、
[:denox, :eval, :start | :stop | :exception]:eval:eval_ts:eval_async:eval_ts_async:eval_module:eval_fileCommon Mistakes
常见错误
| Mistake | Fix |
|---|---|
Using | Use |
Expecting type errors from | Denox is transpile-only, no type-checking |
| Sharing one runtime across concurrent tasks | Use |
Forgetting | Async wraps code in IIFE; use |
CDN import without | Set |
| Running untrusted code without sandbox | Use |
| 错误 | 修复方案 |
|---|---|
使用 | 使用 |
期望 | Denox仅做转译,不进行类型检查 |
| 在并发任务中共享单个运行时 | 使用 |
在 | 异步代码会被包裹在IIFE中;需要用 |
CDN导入未设置 | 设置 |
| 运行不受信任的代码未启用沙箱 | 使用 |