@northsea4/extension-runtime
v1.1.0
Published
Safe rule runtime for browser extensions (MV3-friendly)
Downloads
9
Maintainers
Readme
@northsea4/extension-runtime
一个面向浏览器扩展(尤其 MV3)的规则运行时。
- 默认使用安全表达式模式(
expr) - 支持沙盒 JS 模式(
js,需宿主注入执行器) - 提供统一
run()API、超时控制、策略校验与结果标准化
安装
pnpm add @northsea4/extension-runtime快速开始
import { createRuntime } from '@northsea4/extension-runtime'
const runtime = createRuntime()
const result = await runtime.run(
{
id: 'rule-1',
name: 'Upper Case',
version: '1.0.0',
mode: 'expr',
code: 'upper(input.name)',
helpers: ['upper']
},
{
input: { name: 'clouddrive' },
env: {},
now: new Date().toISOString()
}
)
if (result.ok) {
console.log(result.output) // "CLOUDDRIVE"
}也可以使用便捷调用(默认 js 模式):
const result = await runtime.exec('return { upper: String(ctx.input.name).toUpperCase() }', {
name: 'clouddrive'
})
// 明确指定模式也可以:
const exprResult = await runtime.execExpr(
'upper(input.name)',
{ name: 'clouddrive' },
{ helpers: ['upper'] }
)
const jsResult = await runtime.execJs('return { ok: true, name: ctx.input.name }', {
name: 'clouddrive'
})
// 透传 traceId(便于日志关联)
const traced = await runtime.exec('return { ok: true }', {}, { traceId: 'trace-20260318-001' })
console.log(traced.traceId) // trace-20260318-001
// 支持取消执行(AbortSignal)
const controller = new AbortController()
const task = runtime.exec('return { ok: true }', {}, { signal: controller.signal })
controller.abort()
const canceled = await task
console.log(canceled.error?.code) // CANCELED两种模式
expr(推荐默认)
- 适合大多数可配置规则
- 通过 AST 解释执行,不依赖
eval/new Function - 安全边界更清晰,可控性更高
js(高级兜底)
- 适合复杂自定义逻辑
- 必须在
createRuntime时传入sandboxExecutor - 运行时会做策略校验与超时约束
import { createRuntime } from '@northsea4/extension-runtime'
const runtime = createRuntime({
sandboxExecutor: async ({ code, ctx }) => {
// 在宿主侧实现 postMessage -> sandbox page 的执行链路
return { code, input: ctx.input }
},
logger: console
})Helper
可通过 registerHelper 注册自定义 helper:
runtime.registerHelper('suffix', (v, s) => `${String(v)}${String(s)}`)表达式工具(同步 API)
除了 execExpr(统一结果结构、异步)外,也提供同步工具,便于需要“同步判定/编译缓存”的场景:
import { compileExpr, evalExpr, parseExpression } from '@northsea4/extension-runtime'
const ast = parseExpression('input.count > 0')
const ok = evalExpr('input.count > 0', { count: 1 }) // true
const compiled = compileExpr<{ count: number }, boolean>('input.count > 10')
const pass = compiled({ count: 12 }) // true输出结果
run() 返回统一结构:
ok: 是否成功output: 成功结果error: 失败信息(含错误码)traceId: 本次执行链路 ID(可透传或自动生成)metrics: 执行耗时和模式信息
开启 logger 后,debug/warn 日志会带统一字段:
modechannelelapsedMsruleIdtraceId
常见错误码(节选):
TIMEOUT: 执行超时CANCELED: 执行被取消PARSE_ERROR: 表达式解析失败POLICY_DENIED: 策略或配置不允许SERIALIZE_ERROR: 消息或结果不可序列化UNSUPPORTED_ENV: 当前上下文不支持所需 API(如无 DOM / 无 runtime API)SANDBOX_INIT_FAILED: sandbox iframe 初始化失败SANDBOX_UNAVAILABLE: sandbox 目标窗口不可用RELAY_UNAVAILABLE: tab relay 通道不可用OFFSCREEN_UNAVAILABLE: offscreen 通道不可用
开发
pnpm --filter @northsea4/extension-runtime build
pnpm --filter @northsea4/extension-runtime test发布文档
示例
原生扩展示例位于
examples/browser-extension/先执行
pnpm --filter @northsea4/extension-runtime build同步 vendor 文件(推荐):
pnpm --filter @northsea4/extension-runtime sync:browser-example或手动同步:
cp packages/extension-runtime/lib/playground.js packages/extension-runtime/examples/browser-extension/vendor/extension-runtime.js打开 Chrome 扩展管理页(
chrome://extensions),开启开发者模式点击“加载已解压的扩展程序”,选择
packages/extension-runtime/examples/browser-extension在扩展卡片里点击“详情” -> “扩展程序选项”,打开 options 页测试
expr/js规则执行
Extension 集成示例(更具体)
下面给 3 种常见接入方式。你可以按环境选一种,不需要全上。
最小文件结构
src/
entrypoints/
popup/main.ts
sandbox/main.ts
content/main.ts
background/main.ts
offscreen/main.ts场景 A:扩展页直连 sandbox(popup/options/content 有 DOM)
1) sandbox 页面(执行端)
// src/entrypoints/sandbox/main.ts
import { registerHtmlSandboxHandler } from '@northsea4/extension-runtime/extension'
registerHtmlSandboxHandler({
helpers: {
concat: (a, b) => `${String(a)}${String(b)}`
}
})2) popup/options 页面(调用端)
// src/entrypoints/popup/main.ts
import { createRuntime } from '@northsea4/extension-runtime'
import { createHtmlSandboxExecutor } from '@northsea4/extension-runtime/extension'
import Browser from 'webextension-polyfill'
const sandboxExecutor = createHtmlSandboxExecutor({
sandboxUrl: Browser.runtime.getURL('sandbox.html'),
iframeId: 'runtime-sandbox-frame',
warmupOnCreate: true
})
// 也可手动控制预热时机(例如首屏空闲时)
// await sandboxExecutor.warmup()
const runtime = createRuntime({
sandboxExecutor
})
const result = await runtime.execJs(
"return { msg: concat(ctx.input.name, '-ok') }",
{ name: 'clouddrive' },
{
id: 'popup-js-rule',
helpers: ['concat'],
env: { source: 'popup' }
}
)
console.log(result)createHtmlSandboxExecutor 的 targetOrigin 默认自动推断:
- 普通网页 URL:使用精确 origin
- 扩展/opaque 场景(如
chrome-extension://...的 sandbox):自动回退为* - 如需降低首次
execJs延迟,可开启warmupOnCreate或在业务空闲期调用warmup()
场景 B:background 通过 tab relay(依赖 content script)
1) content script 注册 relay 服务
// src/entrypoints/content/main.ts
import {
createHtmlSandboxExecutor,
registerTabRelaySandboxService
} from '@northsea4/extension-runtime/extension'
import Browser from 'webextension-polyfill'
const cleanup = await registerTabRelaySandboxService({
execute: createHtmlSandboxExecutor({
sandboxUrl: Browser.runtime.getURL('sandbox.html'),
iframeId: 'runtime-sandbox-relay-frame'
})
})
window.addEventListener('beforeunload', cleanup)2) background 使用 relay executor
// src/entrypoints/background/main.ts
import { createRuntime } from '@northsea4/extension-runtime'
import { createTabRelaySandboxExecutor } from '@northsea4/extension-runtime/extension'
const runtime = createRuntime({
sandboxExecutor: createTabRelaySandboxExecutor({
targetTabId: 123,
retry: {
maxRetries: 1,
retryDelayMs: 50
}
})
})
const result = await runtime.exec(
"return { from: 'relay', upper: String(ctx.input.name).toUpperCase() }",
{ name: 'clouddrive' },
{
id: 'bg-relay-js-rule',
env: { source: 'background' }
}
)
console.log(result)场景 C:background 走 offscreen(不依赖 tab)
说明:
offscreen主要是 Chromium 能力,跨浏览器项目建议加降级方案(例如回退到 tab relay)。
1) offscreen 页面注册处理器
// src/entrypoints/offscreen/main.ts
import { registerOffscreenSandboxHandler } from '@northsea4/extension-runtime/extension'
import { createHtmlSandboxExecutor } from '@northsea4/extension-runtime/extension'
import Browser from 'webextension-polyfill'
registerOffscreenSandboxHandler({
execute: createHtmlSandboxExecutor({
sandboxUrl: Browser.runtime.getURL('sandbox.html'),
iframeId: 'runtime-offscreen-sandbox-frame'
})
})2) background 调用 offscreen executor
// src/entrypoints/background/main.ts
import { createRuntime } from '@northsea4/extension-runtime'
import { createOffscreenSandboxExecutor } from '@northsea4/extension-runtime/extension'
const runtime = createRuntime({
sandboxExecutor: createOffscreenSandboxExecutor({
documentPath: 'offscreen.html',
reason: 'DOM_PARSER',
justification: 'Execute extension-runtime js rules',
retry: {
maxRetries: 1,
retryDelayMs: 50
}
})
})
const result = await runtime.exec(
"return { from: 'offscreen', total: Number(ctx.input.a) + Number(ctx.input.b) }",
{ a: 2, b: 3 },
{
id: 'bg-offscreen-js-rule',
env: { source: 'background' }
}
)
console.log(result)Extension API 列表
@northsea4/extension-runtime/extension 提供:
createHtmlSandboxExecutorregisterHtmlSandboxHandlercreateTabRelaySandboxExecutorregisterTabRelaySandboxServicecreateOffscreenSandboxExecutorregisterOffscreenSandboxHandler
