logologo
Empezar
Manual
Desarrollo
Plugins
API
Inicio
English
简体中文
日本語
한국어
Español
Português
Deutsch
Français
Русский
Empezar
Manual
Desarrollo
Plugins
API
Inicio
logologo

Introducción

¿Qué es FlowEngine?
Relación entre FlowEngine y plugins
Inicio rápido
Hoja de ruta de aprendizaje

Guía

Registrar FlowModel
Crear FlowModel
Renderizar FlowModel
Flujo de eventos y configuración de FlowModel
Persistencia de FlowModel
Ciclo de vida de FlowModel
Sistema de contexto de FlowModel
Mecanismo reactivo: Observable
FlowModel vs React.Component
Puntos de extensión del plugin RunJS

Definitions

ModelDefinition
FlowDefinition
EventDefinition
ActionDefinition
StepDefinition
Previous PageFlowModel vs React.Component
Next PageModelDefinition
Aviso de traducción IA

Este documento ha sido traducido por IA. Para información precisa, consulte la versión en inglés.

#Puntos de extensión del plugin RunJS (documentación de ctx / snippets / mapeo de escenas)

Cuando un plugin añade o extiende las capacidades de RunJS, se recomienda registrar el "mapeo de contexto / documentación de ctx / código de ejemplo" a través de los puntos de extensión oficiales. Esto garantiza que:

  • El CodeEditor pueda ofrecer autocompletado para ctx.xxx.yyy.
  • La IA de codificación pueda obtener referencias estructuradas de la API de ctx y ejemplos.

Este capítulo presenta dos puntos de extensión:

  • registerRunJSContextContribution(...)
  • registerRunJSSnippet(...)

#1. registerRunJSContextContribution

Se utiliza para registrar "contribuciones" (contributions) de RunJS. Sus usos típicos incluyen:

  • Añadir o sobrescribir mapeos en RunJSContextRegistry (modelClass -> RunJSContext, incluyendo scenes).
  • Extender RunJSDocMeta (descripciones/ejemplos/plantillas de completado para la API de ctx) para FlowRunJSContext o un RunJSContext personalizado.

#Descripción del comportamiento

  • Las contribuciones se ejecutan de forma unificada durante la fase setupRunJSContexts().
  • Si setupRunJSContexts() ya se ha completado, las registros tardíos se ejecutarán inmediatamente (sin necesidad de reiniciar el setup).
  • Cada contribución se ejecutará como máximo una vez para cada RunJSVersion.

#Ejemplo: Añadir un contexto de modelo donde se puede escribir JS

import { registerRunJSContextContribution, FlowRunJSContext, RunJSContextRegistry } from '@nocobase/flow-engine';

registerRunJSContextContribution(({ version, FlowRunJSContext: BaseCtx, RunJSContextRegistry: Registry }) => {
  if (version !== 'v1') return;

  class MyPluginRunJSContext extends BaseCtx {}

  // 1) Documentación/autocompletado de ctx (RunJSDocMeta)
  MyPluginRunJSContext.define({
    label: 'Contexto RunJS de MyPlugin',
    properties: {
      myPlugin: {
        description: 'Espacio de nombres de mi plugin',
        detail: 'object',
        properties: {
          hello: {
            description: 'Saludar',
            detail: '(name: string) => string',
            completion: { insertText: `ctx.myPlugin.hello('World')` },
          },
        },
      },
    },
  });

  // 2) Mapeo de modelo -> contexto (la escena afecta al autocompletado del editor y al filtrado de snippets)
  Registry.register('v1', 'MyPluginJSModel', MyPluginRunJSContext, { scenes: ['block'] });
});

#2. registerRunJSSnippet

Se utiliza para registrar fragmentos de código de ejemplo (snippets) para RunJS, que sirven para:

  • Autocompletado de snippets en el CodeEditor.
  • Material de referencia/ejemplo para la IA de codificación (filtrable por escena/versión/idioma).

#Recomendación de nomenclatura para ref

Se sugiere utilizar: plugin/<nombreDelPlugin>/<tema>, por ejemplo:

  • plugin/plugin-my/foo
  • plugin/plugin-my/api-request-example

Evite conflictos con los espacios de nombres global/* o scene/* del núcleo (core).

#Estrategia de conflictos

  • Por defecto, no se sobrescriben los ref existentes (devuelve false, pero no lanza un error).
  • Si necesita sobrescribir, pase explícitamente { override: true }.

#Ejemplo: Registrar un snippet

import { registerRunJSSnippet } from '@nocobase/flow-engine';

registerRunJSSnippet('plugin/plugin-my/hello', async () => ({
  default: {
    label: 'Hola (Mi Plugin)',
    description: 'Ejemplo mínimo para mi plugin',
    prefix: 'my-hello',
    versions: ['v1'],
    scenes: ['block'],
    contexts: ['*'],
    content: `
// Snippet de mi plugin
ctx.message.success('Hola desde el plugin');
`,
  },
}));

#3. Mejores prácticas

  • Mantenimiento por capas de documentación + snippets:
    • RunJSDocMeta: Descripciones/plantillas de completado (cortas, estructuradas).
    • Snippets: Ejemplos largos (reutilizables, filtrables por escena/versión).
  • Evitar prompts excesivamente largos: Los ejemplos no deben ser demasiados; priorice "plantillas mínimas ejecutables".
  • Prioridad de escena: Si su código JS se ejecuta principalmente en escenarios como formularios o tablas, asegúrese de completar correctamente el campo scenes para mejorar la relevancia del autocompletado y los ejemplos.

#4. Ocultar autocompletado basado en el ctx real: hidden(ctx)

Ciertas API de ctx dependen fuertemente del escenario (por ejemplo, ctx.popup solo está disponible cuando hay una ventana emergente o un cajón abierto). Cuando desee ocultar estas API no disponibles durante el autocompletado, puede definir hidden(ctx) para la entrada correspondiente en RunJSDocMeta:

  • Si devuelve true: Oculta el nodo actual y su subárbol.
  • Si devuelve string[]: Oculta subrutas específicas bajo el nodo actual (permite devolver varias rutas; las rutas son relativas; oculta el subárbol por coincidencia de prefijo).

hidden(ctx) admite async: usted puede usar await ctx.getVar('ctx.xxx') para decidir (a discreción del desarrollador). Se recomienda que sea rápido y sin efectos secundarios (no realice peticiones de red).

Ejemplo: Mostrar el autocompletado de ctx.popup.* solo cuando exista popup.uid.

FlowRunJSContext.define({
  properties: {
    popup: {
      description: 'Contexto de ventana emergente (async)',
      hidden: async (ctx) => !(await ctx.getVar('ctx.popup'))?.uid,
      properties: {
        uid: 'UID de la ventana emergente',
      },
    },
  },
});

Ejemplo: La ventana emergente está disponible pero se ocultan algunas subrutas (solo rutas relativas; por ejemplo, ocultar record y parent.record).

FlowRunJSContext.define({
  properties: {
    popup: {
      description: 'Contexto de ventana emergente (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: 'UID de la ventana emergente',
        record: 'Registro de la ventana emergente',
        parent: {
          properties: {
            record: 'Registro padre',
          },
        },
      },
    },
  },
});

Nota: El CodeEditor siempre habilita el filtrado de autocompletado basado en el ctx real (fail-open, no lanza errores).

#5. info/meta en tiempo de ejecución y API de información de contexto (para autocompletado y LLM)

Además de mantener la documentación de ctx de forma estática mediante FlowRunJSContext.define(), usted también puede inyectar info/meta en tiempo de ejecución a través de FlowContext.defineProperty/defineMethod, y exportar información de contexto serializable para el CodeEditor o modelos de lenguaje (LLM) mediante las siguientes API:

  • await ctx.getApiInfos(options?): Información estática de la API.
  • await ctx.getVarInfos(options?): Información de la estructura de variables (proviene de meta, admite expansión por ruta/profundidad máxima).
  • await ctx.getEnvInfos(): Instantánea del entorno en tiempo de ejecución.

#5.1 defineMethod(name, fn, info?)

info admite (todos opcionales):

  • description / detail / examples
  • ref: string | { url: string; title?: string }
  • params / returns (estilo JSDoc)

Nota: getApiInfos() devuelve documentación estática de la API y no incluirá campos como deprecated, disabled o disabledReason.

Ejemplo: Proporcionar un enlace de documentación para ctx.refreshTargets()

ctx.defineMethod('refreshTargets', async () => {
  // ...
}, {
  description: 'Refrescar los datos de los bloques de destino',
  detail: '() => Promise<void>',
  ref: { url: 'https://docs.nocobase.com/', title: 'Documentación' },
});

#5.2 defineProperty(key, { meta?, info? })

  • meta: Se utiliza para la interfaz de usuario del selector de variables (getPropertyMetaTree / FlowContextSelector), determinando si se muestra, la estructura del árbol, si está deshabilitado, etc. (admite funciones/async).
    • Campos comunes: title / type / properties / sort / hidden / disabled / disabledReason / buildVariablesParams
  • info: Se utiliza para la documentación estática de la API (getApiInfos) y descripciones para el LLM; no afecta a la interfaz del selector de variables (admite funciones/async).
    • Campos comunes: title / type / interface / description / examples / ref / params / returns

Cuando solo se proporciona meta (y no info):

  • getApiInfos() no devolverá esa clave (ya que la documentación estática no se infiere de meta).
  • getVarInfos() construirá la estructura de variables basada en meta (utilizado para selectores de variables o árboles de variables dinámicas).

#5.3 API de información de contexto

Se utiliza para exportar la "información de capacidades de contexto disponibles".

type FlowContextInfosEnvNode = {
  description?: string;
  getVar?: string; // Puede usarse directamente en await ctx.getVar(getVar), se recomienda empezar con "ctx."
  value?: any; // Valor estático resuelto (serializable, solo se devuelve si se puede inferir)
  properties?: Record<string, FlowContextInfosEnvNode>;
};

type FlowContextApiInfos = Record<string, any>; // Documentación estática (nivel superior)
type FlowContextVarInfos = Record<string, any>; // Estructura de variables (expandible por ruta/profundidad)
type FlowContextEnvInfos = {
  popup?: FlowContextInfosEnvNode;
  block?: FlowContextInfosEnvNode;
  flowModel?: FlowContextInfosEnvNode;
  resource?: FlowContextInfosEnvNode;
  record?: FlowContextInfosEnvNode;
  currentViewBlocks?: FlowContextInfosEnvNode;
};

Parámetros comunes:

  • getApiInfos({ version }): Versión de la documentación de RunJS (por defecto v1).
  • getVarInfos({ path, maxDepth }): Recorte y nivel máximo de expansión (por defecto 3).

Nota: Los resultados devueltos por las API anteriores no contienen funciones, por lo que son adecuados para ser serializados y enviados directamente a un LLM.

#5.4 await ctx.getVar(path)

Cuando usted tiene una "cadena de ruta de variable" (por ejemplo, proveniente de una configuración o entrada del usuario) y desea obtener directamente el valor en tiempo de ejecución de esa variable, puede usar getVar:

  • Ejemplo: const v = await ctx.getVar('ctx.record.roles.id')
  • path es una ruta de expresión que comienza con ctx. (por ejemplo, ctx.record.id / ctx.record.roles[0].id).

Adicionalmente: Los métodos o propiedades que comienzan con un guion bajo _ se consideran miembros privados y no aparecerán en los resultados de getApiInfos() o getVarInfos().