docs/docs/cn/flow-engine/runjs-extension-points.md
当插件新增或扩展 RunJS 能力时,建议通过 官方扩展点把“上下文映射 / ctx 文档 / 示例代码”一并注册进去,让:
ctx.xxx.yyyctx API reference + 示例本章介绍两个扩展点:
registerRunJSContextContribution(...)registerRunJSSnippet(...)registerRunJSContextContribution用于注册 RunJS 的“贡献”(contribution),典型用途:
RunJSContextRegistry 映射(modelClass -> RunJSContext,含 scenes)FlowRunJSContext 或自定义 RunJSContext 扩展 RunJSDocMeta(ctx API 的说明/示例/补全模板)setupRunJSContexts() 阶段统一执行;setupRunJSContexts() 已完成,晚注册会立即执行一次(无需重跑 setup);RunJSVersion 最多只会执行一次。import { registerRunJSContextContribution, FlowRunJSContext, RunJSContextRegistry } from '@nocobase/flow-engine';
registerRunJSContextContribution(({ version, FlowRunJSContext: BaseCtx, RunJSContextRegistry: Registry }) => {
if (version !== 'v1') return;
class MyPluginRunJSContext extends BaseCtx {}
// 1) ctx 文档/补全(RunJSDocMeta)
MyPluginRunJSContext.define({
label: 'MyPlugin RunJS context',
properties: {
myPlugin: {
description: 'My plugin namespace',
detail: 'object',
properties: {
hello: {
description: 'Say hello',
detail: '(name: string) => string',
completion: { insertText: `ctx.myPlugin.hello('World')` },
},
},
},
},
});
// 2) model -> context 映射(scene 影响编辑器补全/snippets 筛选)
Registry.register('v1', 'MyPluginJSModel', MyPluginRunJSContext, { scenes: ['block'] });
});
registerRunJSSnippet用于注册 RunJS 的示例代码片段(snippets),用于:
建议使用:plugin/<pluginName>/<topic>,例如:
plugin/plugin-my/fooplugin/plugin-my/api-request-example避免与 core 的 global/*、scene/* 冲突。
ref(返回 false,但不抛错){ override: true }import { registerRunJSSnippet } from '@nocobase/flow-engine';
registerRunJSSnippet('plugin/plugin-my/hello', async () => ({
default: {
label: 'Hello (My Plugin)',
description: 'Minimal example for my plugin',
prefix: 'my-hello',
versions: ['v1'],
scenes: ['block'],
contexts: ['*'],
content: `
// My plugin snippet
ctx.message.success('Hello from plugin');
`,
},
}));
RunJSDocMeta:描述/补全模板(短、结构化)scenes,提升补全与示例相关性。hidden(ctx)某些 ctx API 具有强场景性(例如 ctx.popup 仅在弹窗/抽屉打开时可用)。当你希望在补全时隐藏这些不可用 API,可以在 RunJSDocMeta 中为对应条目定义 hidden(ctx):
true:隐藏当前节点及其子树string[]:隐藏当前节点下的指定子路径(支持一次返回多个路径;路径为相对路径;按前缀匹配隐藏子树)hidden(ctx) 支持 async:你可以使用 await ctx.getVar('ctx.xxx') 来判断(由使用者自行决断)。建议尽量快速、无副作用(不要发网络请求)。
示例:仅当存在 popup.uid 时展示 ctx.popup.* 补全
FlowRunJSContext.define({
properties: {
popup: {
description: 'Popup context (async)',
hidden: async (ctx) => !(await ctx.getVar('ctx.popup'))?.uid,
properties: {
uid: 'Popup uid',
},
},
},
});
示例:popup 可用但隐藏部分子路径(仅相对路径;例如隐藏 record 与 parent.record)
FlowRunJSContext.define({
properties: {
popup: {
description: 'Popup context (async)',
hidden: async (ctx) => {
const popup = await ctx.getVar('ctx.popup');
if (!popup?.uid) return true;
const hidden: string[] = [];
if (!popup?.record) hidden.push('record');
if (!popup?.parent?.record) hidden.push('parent.record');
return hidden;
},
properties: {
uid: 'Popup uid',
record: 'Popup record',
parent: {
properties: {
record: 'Parent record',
},
},
},
},
},
});
说明:CodeEditor 始终启用基于实际 ctx 的补全过滤(fail-open,不抛错)。
info/meta 与上下文信息 API(面向补全与大模型)除了通过 FlowRunJSContext.define()(静态)维护 ctx 文档外,你也可以在运行时通过 FlowContext.defineProperty/defineMethod 注入 info/meta,并通过以下 API 输出可序列化的上下文信息,供 CodeEditor/大模型使用:
await ctx.getApiInfos(options?):静态 API 信息await ctx.getVarInfos(options?):变量结构信息(来源 meta,支持 path/maxDepth 展开)await ctx.getEnvInfos():运行时环境快照defineMethod(name, fn, info?)info 支持(均可选):
description / detail / examplesref: string | { url: string; title?: string }params / returns(JSDoc-like)注意:
getApiInfos()输出为静态 API 文档,不会包含deprecated/disabled/disabledReason等字段。
示例:为 ctx.refreshTargets() 提供文档链接
ctx.defineMethod('refreshTargets', async () => {
// ...
}, {
description: '刷新目标区块的数据',
detail: '() => Promise<void>',
ref: { url: 'https://docs.nocobase.com/', title: 'Docs' },
});
defineProperty(key, { meta?, info? })meta:用于变量选择器 UI(getPropertyMetaTree / FlowContextSelector),决定是否展示、树结构、禁用等(支持函数/async)。
title / type / properties / sort / hidden / disabled / disabledReason / buildVariablesParamsinfo:用于静态 API 文档(getApiInfos)与面向大模型的描述,不影响变量选择器 UI(支持函数/async)。
title / type / interface / description / examples / ref / params / returns当仅提供 meta(未提供 info)时:
getApiInfos() 不会返回该 key(因为静态 API 文档不从 meta 推断)getVarInfos() 会基于 meta 构建变量结构(用于变量选择器/动态变量树)用于输出“可用的上下文能力信息”,返回形态为方案 A(不再包 { apis/envs/... } 一层)。
type FlowContextInfosEnvNode = {
description?: string;
getVar?: string; // 可直接用于 await ctx.getVar(getVar),推荐以 "ctx." 开头
value?: any; // 已解析的静态值(可序列化,仅在能推断到时返回)
properties?: Record<string, FlowContextInfosEnvNode>;
};
type FlowContextApiInfos = Record<string, any>; // 静态文档(顶层一层)
type FlowContextVarInfos = Record<string, any>; // 变量结构(可按 path/maxDepth 展开)
type FlowContextEnvInfos = {
popup?: FlowContextInfosEnvNode;
block?: FlowContextInfosEnvNode;
flowModel?: FlowContextInfosEnvNode;
resource?: FlowContextInfosEnvNode;
record?: FlowContextInfosEnvNode;
currentViewBlocks?: FlowContextInfosEnvNode;
};
常用参数:
getApiInfos({ version }):RunJS 文档版本(默认 v1)getVarInfos({ path, maxDepth }):剪裁与最大展开层级(默认 3)注意:以上 API 的返回结果都不包含函数,适合直接序列化传给大模型。
await ctx.getVar(path)当你只有一个“变量路径字符串”(例如来自配置/用户输入),希望直接拿到该变量的运行时值时,可以使用 getVar:
const v = await ctx.getVar('ctx.record.roles.id')path 为以 ctx. 开头的表达式路径(例如 ctx.record.id / ctx.record.roles[0].id)另外:以下划线 _ 开头的方法/属性会被视为私有成员,不会出现在 getApiInfos() / getVarInfos() 的输出中。