什么是 SenRi FFI?
概述
SenRi FFI(千里 FFI)是一个跨运行时 FFI(外部函数接口)统一抽象库,让你用同一套 API 在 KossJS、Node.js 和 Bun 三个 JavaScript 运行时上调用原生 C 动态库。
TIP
不同运行时各自提供了不同的 FFI API(KossJS 的 _senri_ffi、Bun 的 Bun.ffi、Node.js 依赖 koffi)。SenRi FFI 在这些底层 API 之上提供一个统一的抽象层,使得编写一次 FFI 代码即可在三个运行时上运行。
开源协议
本项目采用 Apache 2.0 协议开源发布。
问题背景
在 JavaScript 生态中调用原生 C 库,不同运行时的做法完全不同:
| 运行时 | FFI 方式 | 类型名称示例 |
|---|---|---|
| KossJS | globalThis._senri_ffi(内置) | int32 |
| Bun | Bun.ffi(内置) | i32 |
| Node.js | koffi(需安装) | koffi.int32 |
直接在代码中使用运行时的原生 API 会锁死你的代码到某个特定运行时。SenRi FFI 解决了这个问题。
技术架构
您的应用程序 (JavaScript / TypeScript)
↓
SenRi FFI (统一 API 层)
↓
┌───────────┼───────────┐
↓ ↓ ↓
KossJSAdapter BunAdapter NodeAdapter
↓ ↓ ↓
_senri_ffi Bun.ffi koffi
(内置 FFI) (内置 FFI) (可选安装)适配器模式
SenRi FFI 使用适配器模式:
- 定义统一的抽象接口 (
BaseAdapter) - 三个运行时各自实现适配器:
KossJSAdapter— 包装globalThis._senri_ffiBunAdapter— 包装Bun.ffiNodeAdapter— 包装koffi(懒加载)
- 启动时通过
detectRuntime()自动选择合适的适配器
核心特性
统一类型系统
使用 types.int32、types.cstring 等统一名称,各适配器自动映射为运行时原生类型:
ts
import { types } from 'senri_ffi';
// 在所有运行时上都一样
types.int32 // KossJS: 'int32', Bun: 'i32', Node: koffi.int32
types.float64 // KossJS: 'float64', Bun: 'f64', Node: koffi.double结构体 / 指针 / 数组
支持复合类型描述符:
ts
import { pointer, array } from 'senri_ffi';
pointer(types.int32); // 指向 int32 的指针
array(types.uint8, 256); // uint8[256]回调自动管理
通过 FinalizationRegistry 自动释放 C 回调,防止内存泄漏。
统一错误类型
所有运行时抛出统一的 FFIError / FFITypeError,便于统一处理。
支持的运行时
| 运行时 | 版本要求 | FFI 后端 | 说明 |
|---|---|---|---|
| KossJS | — | _senri_ffi(内置) | 直接使用 |
| Bun | >= 1.0 | Bun.ffi(内置) | 直接使用 |
| Node.js | >= 18 | koffi(需安装) | npm install koffi |
运行时检测优先级
SenRi FFI 按以下优先级自动检测运行时:
- KossJS — 如果
globalThis._senri_ffi为真值 - Bun — 如果
Bun.ffi可用 - Node.js — 如果
process.versions.node存在
检测发生在模块首次加载时,之后使用缓存的适配器实例。
下一步:
