logologo
Começar
Manual
Desenvolvimento
Plugins
API
Início
English
简体中文
日本語
한국어
Español
Português
Deutsch
Français
Русский
Começar
Manual
Desenvolvimento
Plugins
API
Início
logologo

Introdução

O que é o FlowEngine?
Relação entre FlowEngine e Plugins
Início Rápido
Roteiro de Aprendizagem

Guia

Registrar FlowModel
Criar FlowModel
Renderizar FlowModel
Fluxo de Eventos e Configuração do FlowModel
Persistência do FlowModel
Ciclo de Vida do FlowModel
Sistema de Contexto do FlowModel
Mecanismo Reativo: Observable
FlowModel vs React.Component
Pontos de Extensão do Plugin RunJS

Definições

ModelDefinition
FlowDefinition
EventDefinition
ActionDefinition
StepDefinition
Previous PageFlowModel vs React.Component
Next PageModelDefinition
Aviso de tradução por IA

Este documento foi traduzido por IA. Para informações precisas, consulte a versão em inglês.

#Pontos de Extensão do Plugin RunJS (Documentação ctx / Snippets / Mapeamento de Cena)

Quando um plugin adiciona ou estende as capacidades do RunJS, recomenda-se registrar o "mapeamento de contexto / documentação do ctx / código de exemplo" por meio dos pontos de extensão oficiais. Isso garante que:

  • O CodeEditor possa fornecer preenchimento automático (auto-completion) para ctx.xxx.yyy.
  • A codificação por IA possa obter referências de API ctx estruturadas e exemplos.

Este capítulo apresenta dois pontos de extensão:

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

#1. registerRunJSContextContribution

Usado para registrar "contribuições" (contributions) do RunJS. Usos típicos incluem:

  • Adicionar/sobrescrever mapeamentos de RunJSContextRegistry (modelClass -> RunJSContext, incluindo scenes).
  • Estender RunJSDocMeta (descrições/exemplos/modelos de preenchimento para a API ctx) para FlowRunJSContext ou um RunJSContext personalizado.

#Descrição do Comportamento

  • As contribuições são executadas coletivamente durante a fase setupRunJSContexts().
  • Se o setupRunJSContexts() já tiver sido concluído, registros tardios serão executados imediatamente (sem necessidade de reiniciar o setup).
  • Cada contribuição será executada no máximo uma vez para cada RunJSVersion.

#Exemplo: Adicionando um Contexto de Modelo editável por JS

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

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

  class MyPluginRunJSContext extends BaseCtx {}

  // 1) Documentação/preenchimento do ctx (RunJSDocMeta)
  MyPluginRunJSContext.define({
    label: 'Contexto RunJS do MyPlugin',
    properties: {
      myPlugin: {
        description: 'Namespace do meu plugin',
        detail: 'object',
        properties: {
          hello: {
            description: 'Dizer olá',
            detail: '(name: string) => string',
            completion: { insertText: `ctx.myPlugin.hello('Mundo')` },
          },
        },
      },
    },
  });

  // 2) Mapeamento model -> context (a cena afeta o preenchimento do editor/filtragem de snippets)
  Registry.register('v1', 'MyPluginJSModel', MyPluginRunJSContext, { scenes: ['block'] });
});

#2. registerRunJSSnippet

Usado para registrar trechos de código de exemplo (snippets) para o RunJS, utilizados para:

  • Preenchimento de snippets no CodeEditor.
  • Servir como exemplos/materiais de referência para codificação por IA (podem ser filtrados por cena/versão/idioma).

#Recomendação de Nomeação de ref

Sugere-se usar: plugin/<nomeDoPlugin>/<topico>, por exemplo:

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

Evite conflitos com os namespaces global/* ou scene/* do núcleo (core).

#Estratégia de Conflito

  • Por padrão, entradas ref existentes não são sobrescritas (retorna false sem lançar erro).
  • Para sobrescrever explicitamente, passe { override: true }.

#Exemplo: Registrando um Snippet

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

registerRunJSSnippet('plugin/plugin-my/hello', async () => ({
  default: {
    label: 'Olá (Meu Plugin)',
    description: 'Exemplo mínimo para o meu plugin',
    prefix: 'my-hello',
    versions: ['v1'],
    scenes: ['block'],
    contexts: ['*'],
    content: `
// Snippet do meu plugin
ctx.message.success('Olá do plugin');
`,
  },
}));

#3. Melhores Práticas

  • Manutenção em Camadas de Documentação + Snippets:
    • RunJSDocMeta: Descrições/modelos de preenchimento (curtos, estruturados).
    • Snippets: Exemplos longos (reutilizáveis, filtráveis por cena/versão).
  • Evite Prompts Excessivamente Longos: Os exemplos devem ser concisos; priorize "modelos mínimos executáveis".
  • Prioridade de Cena: Se o seu código JS roda principalmente em cenários como formulários ou tabelas, certifique-se de preencher o campo scenes corretamente para melhorar a relevância dos preenchimentos e exemplos.

#4. Ocultando Preenchimentos com Base no ctx Real: hidden(ctx)

Certas APIs do ctx são altamente específicas ao contexto (por exemplo, ctx.popup só está disponível quando um pop-up ou gaveta está aberto). Se você deseja ocultar essas APIs indisponíveis durante o preenchimento, pode definir hidden(ctx) para a entrada correspondente no RunJSDocMeta:

  • Retorna true: Oculta o nó atual e sua subárvore.
  • Retorna string[]: Oculta subcaminhos específicos sob o nó atual (suporta o retorno de múltiplos caminhos; os caminhos são relativos; as subárvores são ocultadas com base na correspondência de prefixo).

hidden(ctx) suporta async: Você pode usar await ctx.getVar('ctx.xxx') para determinar a visibilidade (a critério do usuário). Recomenda-se manter essa lógica rápida e sem efeitos colaterais (evite requisições de rede, por exemplo).

Exemplo: Mostrar preenchimentos de ctx.popup.* apenas quando popup.uid existir.

FlowRunJSContext.define({
  properties: {
    popup: {
      description: 'Contexto de pop-up (async)',
      hidden: async (ctx) => !(await ctx.getVar('ctx.popup'))?.uid,
      properties: {
        uid: 'UID do pop-up',
      },
    },
  },
});

Exemplo: O pop-up está disponível, mas alguns subcaminhos estão ocultos (apenas caminhos relativos; por exemplo, ocultando record e parent.record).

FlowRunJSContext.define({
  properties: {
    popup: {
      description: 'Contexto de pop-up (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 do pop-up',
        record: 'Registro do pop-up',
        parent: {
          properties: {
            record: 'Registro pai',
          },
        },
      },
    },
  },
});

Nota: O CodeEditor sempre habilita a filtragem de preenchimento com base no ctx real (fail-open, não lança erros).

#5. info/meta em Tempo de Execução e API de Informações de Contexto (para Preenchimentos e LLMs)

Além de manter a documentação do ctx estaticamente via FlowRunJSContext.define(), você também pode injetar info/meta em tempo de execução via FlowContext.defineProperty/defineMethod. Você pode então gerar informações de contexto serializáveis para o CodeEditor ou LLMs usando as seguintes APIs:

  • await ctx.getApiInfos(options?): Informações estáticas da API.
  • await ctx.getVarInfos(options?): Informações da estrutura de variáveis (obtidas de meta, suporta expansão de path/maxDepth).
  • await ctx.getEnvInfos(): Instantâneo (snapshot) do ambiente de execução.

#5.1 defineMethod(name, fn, info?)

O info suporta (todos opcionais):

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

Nota: getApiInfos() gera documentação estática da API e não incluirá campos como deprecated, disabled ou disabledReason.

Exemplo: Fornecendo links de documentação para ctx.refreshTargets().

ctx.defineMethod('refreshTargets', async () => {
  // ...
}, {
  description: 'Atualiza os dados dos blocos de destino',
  detail: '() => Promise<void>',
  ref: { url: 'https://docs.nocobase.com/', title: 'Docs' },
});

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

  • meta: Usado para a interface do seletor de variáveis (getPropertyMetaTree / FlowContextSelector). Determina a visibilidade, estrutura em árvore, desativação, etc. (suporta funções/async).
    • Campos comuns: title / type / properties / sort / hidden / disabled / disabledReason / buildVariablesParams
  • info: Usado para documentação estática da API (getApiInfos) e descrições para LLMs. Não afeta a interface do seletor de variáveis (suporta funções/async).
    • Campos comuns: title / type / interface / description / examples / ref / params / returns

Quando apenas meta é fornecido (sem info):

  • getApiInfos() não retornará esta chave (pois as docs estáticas da API não são inferidas a partir de meta).
  • getVarInfos() construirá a estrutura da variável com base no meta (usado para seletores de variáveis/árvores de variáveis dinâmicas).

#5.3 API de Informações de Contexto

Usada para gerar "informações de capacidade de contexto disponíveis".

type FlowContextInfosEnvNode = {
  description?: string;
  getVar?: string; // Pode ser usado diretamente em await ctx.getVar(getVar), recomendado começar com "ctx."
  value?: any; // Valor estático resolvido (serializável, retornado apenas quando inferível)
  properties?: Record<string, FlowContextInfosEnvNode>;
};

type FlowContextApiInfos = Record<string, any>; // Documentação estática (nível superior)
type FlowContextVarInfos = Record<string, any>; // Estrutura de variáveis (expansível por path/maxDepth)
type FlowContextEnvInfos = {
  popup?: FlowContextInfosEnvNode;
  block?: FlowContextInfosEnvNode;
  flowModel?: FlowContextInfosEnvNode;
  resource?: FlowContextInfosEnvNode;
  record?: FlowContextInfosEnvNode;
  currentViewBlocks?: FlowContextInfosEnvNode;
};

Parâmetros comuns:

  • getApiInfos({ version }): Versão da documentação do RunJS (padrão é v1).
  • getVarInfos({ path, maxDepth }): Recorte e profundidade máxima de expansão (padrão é 3).

Nota: Os resultados retornados pelas APIs acima não contêm funções e são adequados para serialização direta para LLMs.

#5.4 await ctx.getVar(path)

Quando você tem uma "string de caminho de variável" (por exemplo, vinda de uma configuração ou entrada do usuário) e deseja recuperar o valor em tempo de execução dessa variável diretamente, use getVar:

  • Exemplo: const v = await ctx.getVar('ctx.record.roles.id')
  • path é um caminho de expressão começando com ctx. (ex: ctx.record.id / ctx.record.roles[0].id).

Adicionalmente: Métodos ou propriedades que começam com um sublinhado _ são tratados como membros privados e não aparecerão na saída de getApiInfos() ou getVarInfos().