iii Browser SDK(iii-browser-sdk)参考:把浏览器标签页变成 iii Engine 上的 Worker
【免费下载链接】iiiEffortlessly compose, extend, and observe every service in real-time for the first time ever.项目地址: https://gitcode.com/GitHub_Trending/mo/iii
本文围绕 iii 0.12.0 文档中的 Browser SDK 参考页 展开,系统讲解iii-browser-sdk的完整公共 API 面:如何用一个 WebSocket 把浏览器标签页接入 iii engine、在页面中注册可被引擎回调的函数与自定义触发器、以三种动作模式(同步 / 即发即忘 / 入队)调用后端函数,以及ChannelReader/ChannelWriter流式通道、连接状态与错误语义。读完后你将能在前端代码中直接使用registerWorker、registerFunction、trigger、TriggerAction等原语,并理解这些接口在仓库源码 sdk/packages/node/iii-browser 中的真实落点。
定位:独立实现,而非 Node SDK 的浏览器构建
文档原文档明确了一个前提:Browser SDK 是 Node SDK 之外的一个独立实现(standalone implementation),而不是 Node SDK 编译出来的浏览器产物。这一点在仓库中可以得到印证:
- 包目录独立存在:sdk/packages/node/iii-browser,包名为
iii-browser-sdk; - package.json 的描述为 “III SDK for the browser — no OpenTelemetry, no Node.js dependencies”,且
devDependencies中没有 OpenTelemetry 相关依赖; - 包声明
"type": "module",通过tsdown同时产出 ESM/CJS(dist/index.mjs/dist/index.cjs),可直接在任意现代浏览器环境中以原生WebSocket运行。
这意味着浏览器端的取舍是刻意的:去掉结构化 Logger 与 OpenTelemetry 导出面,保留全部“Worker 原语”(函数、触发器、通道),使前端页面可以作为一个 iii Worker 参与整个系统的函数调用与事件驱动网络。
安装与包结构
安装方式(来自文档原文):
npm install iii-browser-sdk从 package.json 的exports字段可以看到该包提供的四个入口:
| 导入路径 | 内容 |
|---|---|
iii-browser-sdk | 根入口:registerWorker、TriggerAction、ChannelReader、ChannelWriter、IIIConnectionState等 |
iii-browser-sdk/stream | 流式原语(IStream、StreamChangeEvent等类型) |
iii-browser-sdk/state | 状态原语类型 |
iii-browser-sdk/helpers | 辅助工具 |
根入口的实际导出清单可以在 src/index.ts 中逐条核对:运行时值导出为ChannelReader/ChannelWriter、RegistrationRejectedError、EngineFunctions/EngineTriggers常量、registerWorker与TriggerAction;类型导出则覆盖InitOptions、StreamChannelRef、TriggerRequest、ISdk、FunctionRef、Trigger、TriggerTypeRef、EnqueueResult、MessageType等。
建立连接:registerWorker与InitOptions
文档签名:
function registerWorker(address: string, options?: InitOptions): ISdk;address是 engine 的 SDK WebSocket URL。调用时连接会自动建立,没有单独的connect()步骤;返回的ISdk实例携带文档中列出的全部方法。仓库包 README 给出最小示例:
import { registerWorker } from 'iii-browser-sdk' const iii = registerWorker('ws://remotehost:3111')InitOptions的可选字段(结合文档与包内类型定义):
| 字段 | 类型 | 说明 |
|---|---|---|
invocationTimeoutMs | number | trigger()的默认超时(毫秒),默认30000 |
reconnectionConfig | Partial<IIIReconnectionConfig> | WebSocket 重连行为配置,IIIReconnectionConfig类型同样从包根导出 |
headers | Record<string, string> | 浏览器原生 WebSocket 不支持自定义请求头,认证应改用查询参数或 Cookie |
注意部署侧的前置条件:浏览器连接的是 engine 的 worker-manager 监听器。包 README 提示需要在 engine 配置中为一个公开监听了(例如iii-worker-manager#browser)配置 RBAC 保护,且不应把私有监听器暴露给不可信的浏览器客户端。
常用方法
registerFunction:在标签页中注册可调用的函数
worker.registerFunction( functionId: string, handler: RemoteFunctionHandler, options?: RegisterFunctionOptions, ): FunctionRef;functionId:唯一函数标识,使用::做命名空间(例如ui::show-notification);handler:异步处理函数(data: TInput) => Promise<TOutput>,引擎侧任何 worker 都可以通过trigger回调它——这是“后端向浏览器推数据”的实时模式基础;options:可选的description、request_format/response_format、metadata;- 返回值
FunctionRef含id与unregister(),可随时把函数从引擎注销。
典型的浏览器侧函数注册(来自 包 README 的 Hello World):
iii.registerFunction('ui::show-notification', async (data: { title: string; body: string }) => { showToast(data.title, data.body) return { displayed: true } })registerTrigger:把已注册函数绑定到触发器实例
worker.registerTrigger(trigger: RegisterTriggerInput): Trigger;文档特别强调了一个 API 不对称点:触发器没有顶层的unregisterTrigger方法,注销必须通过返回句柄上的Trigger.unregister()完成。RegisterTriggerInput的字段为type(触发器类型)、function_id、config及可选id。
registerTriggerType/unregisterTriggerType:自定义触发器类型
worker.registerTriggerType<TConfig>( triggerType: RegisterTriggerTypeInput, handler: TriggerHandler<TConfig>, ): TriggerTypeRef<TConfig>; worker.unregisterTriggerType(triggerType: RegisterTriggerTypeInput): void;registerTriggerType让当前标签页声明并承载一种新的触发器类型:handler是一个实现TriggerHandler的对象,含registerTrigger/unregisterTrigger两个异步方法,在触发器实例被引擎注册/注销时分别回调。返回的TriggerTypeRef<TConfig>提供id、registerTrigger、registerFunction、unregister等便捷方法,免去每次重复填写type字段。浏览器是自定义触发器类型的自然宿主之一——例如把“倒计时结束”“用户点击”这类纯前端事件做成可被引擎调度的触发器类型。
trigger:调用已注册函数
worker.trigger<TInput, TOutput>(request: TriggerRequest<TInput>): Promise<TOutput>;TriggerRequest字段:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
function_id | string | 是 | 目标函数 ID |
payload | TInput | 是 | 调用载荷 |
action | TriggerAction | 否 | 路由动作;省略即同步请求/响应 |
timeoutMs | number | 否 | 覆盖InitOptions.invocationTimeoutMs的默认超时 |
Promise 的解析语义随action变化(文档原文要点):
- 同步调用:解析为函数的返回值;
TriggerAction.Enqueue:解析为一个EnqueueResult(入队回执);TriggerAction.Void:解析为undefined。
// 同步调用 const users = await iii.trigger({ function_id: 'api::get::users', payload: {}, })shutdown
worker.shutdown(): Promise<void>;断开与 engine 的连接并释放资源,建议在页面卸载(beforeunload等时机)时调用。
Trigger actions:Void与Enqueue
TriggerAction是一个运行时常量对象,产出的值传给trigger的action字段:
TriggerAction.Void(); // fire-and-forget TriggerAction.Enqueue({ queue: "math" }); // route through iii-queue其底层类型是联合类型:
{ type: "enqueue"; queue: string } | { type: "void" }因此三种调用形态的完整示例为:
// 1) 同步请求/响应(默认) const result = await iii.trigger({ function_id: 'greet', payload: { name: 'World' } }) // 2) 即发即忘 await iii.trigger({ function_id: 'send-email', payload: { to: 'user@example.com' }, action: TriggerAction.Void(), }) // 3) 通过命名队列异步处理 const receipt = await iii.trigger({ function_id: 'process-order', payload: { orderId: '123' }, action: TriggerAction.Enqueue({ queue: 'orders' }), })Enqueue会把调用路由进 engine 的 iii-queue 子系统,适合长耗时或可重试的任务,回执EnqueueResult可用于幂等判断。
Channels:基于原生 WebSocket 的流式通道
文档说明:ChannelReader和ChannelWriter是包装 engine 流 WebSocket 的运行时类,直接使用浏览器的原生 WebSocket API;StreamChannelRef是在 SDK 各调用之间传递、用于标识某个通道端点的可序列化类型:
type StreamChannelRef = { channel_id: string; access_key: string; direction: "read" | "write"; }StreamChannelRef的字段含义(access_key为访问凭证,direction区分读/写端)在包 README 与 0.11.0 版 自动生成 API 参考 中一致。通道端点本身由iii.createChannel(bufferSize)创建(bufferSize缺省 64),返回含reader/writer与readerRef/writerRef的对象,两个 ref 可以放进调用载荷发给其他 worker,实现跨 worker 的流式数据传输。
读写端方法面(文档列表与 src/channels.ts 源码逐一吻合):
| 类 | 方法 | 说明 |
|---|---|---|
ChannelReader | onMessage(cb) | 注册文本消息回调 |
ChannelReader | onBinary(cb) | 注册二进制帧回调(Uint8Array) |
ChannelReader | readAll() | 异步读取全部待发二进制数据 |
ChannelReader | close() | 关闭读端连接 |
ChannelWriter | sendMessage(msg: string) | 发送文本消息 |
ChannelWriter | sendBinary(data: Uint8Array) | 发送二进制数据 |
ChannelWriter | close() | 关闭写端连接 |
一个典型的本地读取用法(源自包注释与 API 参考示例):
const channel = await iii.createChannel() // 把 writer ref 传给另一个函数,由它对端写入 await iii.trigger({ function_id: 'stream-producer', payload: { outputChannel: channel.writerRef }, }) channel.reader.onMessage((msg) => { console.log('Received:', msg) })连接状态:IIIConnectionState
文档定义:
type IIIConnectionState = | "disconnected" | "connecting" | "connected" | "reconnecting" | "failed";该字面量类型别名从包根导出。src/index.ts 中可以看到它实际由iii-constants模块再导出(连同IIIReconnectionConfig),这五个状态覆盖了浏览器网络波动下的完整生命周期:断线后 SDK 会进入reconnecting并按reconnectionConfig自动重连,重连耗尽后落入failed。前端可以用该状态驱动 UI 上的连接指示器。
Info 类型:FunctionInfo与TriggerInfo
FunctionInfo:function_id(必填)、可选description、可选request_format/response_format、可选metadata;TriggerInfo:id、trigger_type、function_id(必填),可选config/metadata。
文档同时划清了一条边界:WorkerInfo与WorkerMetadata不属于本 SDK。当浏览器客户端需要其他 worker 的元数据时,应使用 engine 的 introspection 函数(参见 engine-sdk 参考的 engine 发现函数一节),即经由engine::workers::list等引擎内省函数完成,而不是在 Browser SDK 中寻找。
MessageType:线协议帧枚举
MessageType是一个运行时枚举,命名了 SDK 与 engine 之间交换的每一类 wire frame,调用方很少直接使用,但在调试协议交互时有用。从 0.11.0 版 自动生成参考 可以列出其成员全集:
InvocationResult、InvokeFunction、RegisterFunction、RegisterService、RegisterTrigger、RegisterTriggerType、TriggerRegistrationResult、UnregisterFunction、UnregisterTrigger、UnregisterTriggerType、WorkerRegistered。
这些帧名与包根导出的RegisterFunctionMessage、RegisterTriggerMessage、RegisterTriggerTypeMessage等消息类型一一对应:每个*Message类型都携带message_type字段标注帧类型,而RegisterFunctionOptions、RegisterTriggerInput等对外输入类型正是把message_type字段剥离后的Omit变体——调用方无需(也不应)手填message_type。
本 SDK 不具备的能力面
文档明确列出了两个“不在此 SDK 中”的表面,理解它们能避免集成时的误用:
- Logger。Browser SDK 不附带结构化 logger;应使用浏览器内置
console,并依赖 iii-observability worker 提供的 OpenTelemetry 表面做导出。这与 package.json 中 “no OpenTelemetry” 的定位一致。 - Error class。不存在
IIIError/IIIInvocationError类;失败一律表现为携带Error实例的 rejected promise,需要自行检查message以及附加的code字段获取 engine 错误码。
try { await iii.trigger({ function_id: 'flaky::op', payload: {} }) } catch (err) { // err 是 Error 实例;engine 错误码在 (err as any).code console.error((err as Error).message) }值得补充的是,src/index.ts 还导出了RegistrationRejectedError,用于函数/触发器注册被引擎拒绝(如命名冲突)的场景,它是当前包内唯一显式导出的错误类型。
工程验证路径
如果想在自己的项目里核对本文行为,仓库提供了完整的测试基建(均为只读参考):
- sdk/packages/node/iii-browser/tests/channels.test.ts、triggers.test.ts、trigger-types.test.ts 等单测,基于
mock-websocket验证线协议行为; - tests/integration 下的
bridge.test.ts、channels.test.ts、trigger-type-lifecycle.test.ts等集成测试,针对真实 engine 验证通道与触发器类型生命周期; - exports.test.ts 校验包根导出面与文档所述公共 API 保持一致。
小结
iii-browser-sdk的公共面可以归纳为一句话:一个 WebSocket 连接 + 与 Node 端同构的 Worker 原语。registerWorker建立连接;registerFunction/registerTrigger/registerTriggerType让浏览器标签页成为引擎可调度的 Worker;trigger配合TriggerAction.Void()/TriggerAction.Enqueue()覆盖同步、即发即忘与队列三种调用模式;ChannelReader/ChannelWriter提供基于原生 WebSocket 的双向流;IIIConnectionState暴露五态连接生命周期;而 Logger 与专用 Error class 是刻意裁剪掉的表面。所有接口细节都可以对照 sdk/packages/node/iii-browser 目录下的src/源码与tests/用例逐条验证。
【免费下载链接】iiiEffortlessly compose, extend, and observe every service in real-time for the first time ever.项目地址: https://gitcode.com/GitHub_Trending/mo/iii
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考