Plugins
WASM 插件
用 Rust、Go、C 或 Zig 编译为 WebAssembly 的高性能插件,通过 WIT 接口实现强类型。
WASM 插件以接近原生的速度运行,并具有强沙箱隔离。可以使用任何能编译为 WebAssembly 的语言。
为什么选 WASM?
| 脚本插件(JS/Lua/Rhai) | WASM 插件 | |
|---|---|---|
| 性能 | 良好 | 接近原生 |
| 语言 | 仅 JS / Lua / Rhai | Rust、Go、C、Zig 等 |
| 类型 | 动态 | 强类型(WIT 接口) |
| 并发 | Per-request(无状态) | 实例池(可有状态) |
| 适合 | 快速集成、Hook | 重计算、数据处理 |
快速开始(Rust)
1. 创建项目
cargo new --lib my-plugin
cd my-plugin2. 添加依赖
[lib]
crate-type = ["cdylib"]
[dependencies]
wit-bindgen = "0.36"3. 编写插件
下载 plugin.wit 接口文件,然后:
wit_bindgen::generate!({
path: "../plugin-wit/plugin.wit",
world: "plugin-world",
});
use exports::raisfast::plugin_wit::plugin_hooks::*;
struct Plugin;
impl Guest for Plugin {
fn on_post_creating(input: PostInput) -> Option<PostInput> {
let mut post = input;
if post.slug.is_none() {
post.slug = Some(post.title.to_lowercase().replace(' ', "-"));
}
Some(post)
}
fn on_post_created(output: PostOutput) {
host_api::log("info", &format!("published: {}", output.slug));
let key = format!("cache/posts/{}.json", output.slug);
host_api::vfs_write(&key, "{}");
}
fn on_cron_tick(payload: Option<String>) {
if let Some(p) = payload {
host_api::log("info", &format!("cron fired: {p}"));
}
}
}
export_plugin_hooks!(Plugin);4. 编译
cargo build --target wasm32-unknown-unknown --release
cp target/wasm32-unknown-unknown/release/my_plugin.wasm plugin.wasm5. Manifest
[plugin]
id = "com.example.my-plugin"
name = "My WASM Plugin"
version = "0.1.0"
runtime = "wasm"
entry = "plugin.wasm"
[hooks.on-post-creating]
priority = 10
[hooks.on-post-created]
priority = 10
[[cron]]
label = "定时任务"
job_type = "my_task"
cron_expr = "0 */6 * * *"6. 部署
extensions/plugins/my-plugin/
├── manifest.toml
└── plugin.wasm重启服务或等待热重载自动加载。
WIT 接口
WASM 插件实现 plugin-hooks 接口,导入 host-api。
Host API(导入)
插件可以调用的函数:
interface host-api {
log: func(level: string, msg: string);
get-config: func(key: string) -> option<string>;
http-get: func(url: string) -> option<string>;
http-post: func(url: string, body: string) -> option<string>;
get-data: func(key: string) -> option<string>;
set-data: func(key: string, value: string) -> bool;
get-post: func(slug: string) -> option<string>;
db-query: func(sql: string, params: option<string>) -> string;
db-execute: func(sql: string, params: option<string>) -> string;
db-begin: func() -> string;
db-commit: func() -> string;
db-rollback: func() -> string;
vfs-read: func(path: string) -> option<string>;
vfs-write: func(path: string, content: string) -> bool;
vfs-delete: func(path: string) -> bool;
vfs-exists: func(path: string) -> bool;
vfs-list: func(path: string) -> option<string>;
vfs-stat: func(path: string) -> option<string>;
emit-event: func(event-type: string, data: string) -> string;
}Plugin Hooks(导出)
插件可以导出的函数:
interface plugin-hooks {
// Filter — 返回 Some 修改数据,返回 None 跳过
on-post-creating: func(input: post-input) -> option<post-input>;
on-post-updating: func(input: post-input) -> option<post-input>;
on-comment-creating: func(input: comment-input) -> option<comment-input>;
on-content-creating: func(input: content-event) -> option<content-event>;
on-content-updating: func(input: content-event) -> option<content-event>;
render-markdown: func(input: string) -> option<string>;
filter-html: func(input: string) -> option<string>;
// Action — 返回值被忽略
on-post-created: func(output: post-output);
on-post-updated: func(output: post-output);
on-post-deleted: func(id: string);
on-comment-created: func(input: comment-input);
on-content-created: func(input: content-event);
on-content-updated: func(input: content-event);
on-content-deleted: func(content-type: string, id: string);
on-login: func(user-id: string);
on-cron-tick: func(payload: option<string>);
}WIT 类型
record post-input {
title: string,
content: string,
slug: option<string>,
excerpt: option<string>,
category-id: option<string>,
tag-ids: option<list<string>>,
status: option<string>,
cover-image: option<string>,
}
record post-output {
id: string,
title: string,
slug: string,
content: string,
excerpt: option<string>,
status: string,
created-by: string,
updated-by: option<string>,
category-id: option<string>,
view-count: s64,
created-at: string,
updated-at: string,
published-at: option<string>,
}
record comment-input {
content: string,
nickname: option<string>,
email: option<string>,
parent-id: option<string>,
}
record content-event {
content-type: string,
data: string,
id: option<string>,
}并发模型
与 JS/Lua(per-request,无状态)不同,WASM 使用实例池:
- N 个预编译实例(通过
PLUGIN_WASM_POOL_SIZE配置,默认 4) - 轮询分发处理并发请求
- 实例可复用 — 可以在调用之间保持状态
- 实例间完全隔离
当前限制
WIT 接口目前仅提供原始 SQL(db-query、db-execute)。以下在 JS/Lua/Rhai 中可用的函数尚未暴露给 WASM:
- 高级 DB API:
dbInsert、dbFetchOne、dbFetchAll、dbUpdate、dbDelete、dbCount、dbIncrement、dbSum、dbGroupBy newId(UUID v7 生成)dbPh(SQL 占位符)
可使用 db-query 和 db-execute 的原始 SQL 作为替代方案。
使用其他语言
任何能编译为 wasm32-unknown-unknown 并支持 Component Model 的语言都可以:
| 语言 | Bindgen 工具 |
|---|---|
| Rust | wit-bindgen |
| Go | wit-bindgen-go |
| C | wit-bindgen-c |
| Zig | 社区工具 |
WIT 接口文件是唯一的契约 — 只要你的语言能实现它,就可以用。
