feat: manually mirror opencoze's code from bytedance
Change-Id: I09a73aadda978ad9511264a756b2ce51f5761adf
This commit is contained in:
22
frontend/packages/workflow/variable/src/datas/index.ts
Normal file
22
frontend/packages/workflow/variable/src/datas/index.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { WorkflowNodeOutputVariablesData } from './workflow-node-output-variables-data';
|
||||
export { WorkflowNodeInputVariablesData } from './workflow-node-input-variables-data';
|
||||
export {
|
||||
WorkflowNodeRefVariablesData,
|
||||
type UpdateRefInfo,
|
||||
} from './workflow-node-ref-variables-data';
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { type FlowNodeEntity } from '@flowgram-adapter/free-layout-editor';
|
||||
import { EntityData } from '@flowgram-adapter/free-layout-editor';
|
||||
import {
|
||||
getFormValueByPathEnds,
|
||||
type RefExpressionContent,
|
||||
type InputValueVO,
|
||||
type WorkflowNodeRegistry,
|
||||
} from '@coze-workflow/base';
|
||||
|
||||
import { type WorkflowVariable, WorkflowVariableFacadeService } from '../core';
|
||||
|
||||
interface InputVariable {
|
||||
name?: string;
|
||||
refVariable?: WorkflowVariable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the data for ref variables of a flow node.
|
||||
*/
|
||||
export class WorkflowNodeInputVariablesData extends EntityData {
|
||||
static readonly type = 'WorkflowNodeInputVariablesData';
|
||||
|
||||
declare entity: FlowNodeEntity;
|
||||
|
||||
getDefaultData() {
|
||||
return {};
|
||||
}
|
||||
|
||||
protected get facadeService() {
|
||||
return this.entity.getService(WorkflowVariableFacadeService);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取输入的表单值
|
||||
*/
|
||||
get inputParameters(): InputValueVO[] {
|
||||
const registry = this.entity.getNodeRegister() as WorkflowNodeRegistry;
|
||||
|
||||
if (registry.getNodeInputParameters) {
|
||||
return registry.getNodeInputParameters(this.entity) || [];
|
||||
} else {
|
||||
return (
|
||||
getFormValueByPathEnds<InputValueVO[]>(
|
||||
this.entity,
|
||||
'/inputParameters',
|
||||
) || []
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有的输入变量,包括变量名和引用的变量实例
|
||||
*/
|
||||
|
||||
get inputVariables(): InputVariable[] {
|
||||
return this.inputParameters.map(_input => {
|
||||
const { name } = _input;
|
||||
|
||||
const refVariable = this.facadeService.getVariableFacadeByKeyPath(
|
||||
(_input.input?.content as RefExpressionContent)?.keyPath,
|
||||
{ node: this.entity, checkScope: true },
|
||||
);
|
||||
|
||||
return {
|
||||
name,
|
||||
refVariable,
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
ASTKind,
|
||||
FlowNodeVariableData,
|
||||
type ObjectType,
|
||||
} from '@flowgram-adapter/free-layout-editor';
|
||||
import { type FlowNodeEntity } from '@flowgram-adapter/free-layout-editor';
|
||||
import { EntityData } from '@flowgram-adapter/free-layout-editor';
|
||||
import { type Disposable } from '@flowgram-adapter/common';
|
||||
|
||||
import { type WorkflowVariable, WorkflowVariableFacadeService } from '../core';
|
||||
|
||||
/**
|
||||
* Represents the data for output variables of a flow node.
|
||||
*/
|
||||
export class WorkflowNodeOutputVariablesData extends EntityData {
|
||||
static readonly type = 'WorkflowNodeOutputVariablesData';
|
||||
|
||||
declare entity: FlowNodeEntity;
|
||||
|
||||
getDefaultData() {
|
||||
return {};
|
||||
}
|
||||
|
||||
protected get variableData(): FlowNodeVariableData {
|
||||
return this.entity.getData(FlowNodeVariableData);
|
||||
}
|
||||
|
||||
protected get facadeService() {
|
||||
return this.entity.getService(WorkflowVariableFacadeService);
|
||||
}
|
||||
|
||||
protected get outputObjectType(): ObjectType | undefined {
|
||||
const output = this.variableData.public.output.variables[0];
|
||||
if (output?.type?.kind !== ASTKind.Object) {
|
||||
return undefined;
|
||||
}
|
||||
return output.type as ObjectType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the list of workflow variables based on the output object type properties.
|
||||
* @returns An array of workflow variables.
|
||||
*/
|
||||
get variables(): WorkflowVariable[] {
|
||||
return (this.outputObjectType?.properties || []).map(_property =>
|
||||
this.facadeService.getVariableFacadeByField(_property),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a workflow variable by its key.
|
||||
* @param key - The key of the variable.
|
||||
* @returns The workflow variable or undefined if not found.
|
||||
*/
|
||||
getVariableByKey(key: string): WorkflowVariable | undefined {
|
||||
const field = this.outputObjectType?.propertyTable.get(key);
|
||||
return field
|
||||
? this.facadeService.getVariableFacadeByField(field)
|
||||
: undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a callback function that will be invoked whenever any variable changes.
|
||||
*
|
||||
* @param cb - The callback function to be executed on any variable change.
|
||||
* @returns A `Disposable` object that can be used to unregister the callback.
|
||||
*/
|
||||
onAnyVariablesChange(cb: () => void): Disposable {
|
||||
return this.variableData.public.ast.subscribe(cb);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { set } from 'lodash-es';
|
||||
import { FlowNodeVariableData } from '@flowgram-adapter/free-layout-editor';
|
||||
import {
|
||||
Emitter,
|
||||
type FormModelV2,
|
||||
isFormV2,
|
||||
} from '@flowgram-adapter/free-layout-editor';
|
||||
import { FlowNodeFormData } from '@flowgram-adapter/free-layout-editor';
|
||||
import { type FlowNodeEntity } from '@flowgram-adapter/free-layout-editor';
|
||||
import { EntityData } from '@flowgram-adapter/free-layout-editor';
|
||||
|
||||
import { convertGlobPath } from '../utils/path';
|
||||
import { type ValueExpression } from '../typings';
|
||||
import { traverseAllRefExpressions } from '../core/utils/traverse-refs';
|
||||
import { matchPath } from '../core/utils/name-path';
|
||||
import { type WorkflowVariable, WorkflowVariableFacadeService } from '../core';
|
||||
import { allGlobalVariableKeys } from '../constants';
|
||||
|
||||
type KeyPath = string[];
|
||||
type DataPath = string;
|
||||
type Refs = Record<DataPath, KeyPath>;
|
||||
type RefVariables = Record<DataPath, WorkflowVariable | undefined>;
|
||||
|
||||
export interface UpdateRefInfo {
|
||||
beforeKeyPath: KeyPath;
|
||||
afterKeyPath?: KeyPath;
|
||||
afterExpression?: ValueExpression;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the data for ref variables of a flow node.
|
||||
*/
|
||||
export class WorkflowNodeRefVariablesData extends EntityData {
|
||||
static readonly type = 'WorkflowNodeRefVariablesData';
|
||||
|
||||
declare entity: FlowNodeEntity;
|
||||
|
||||
protected onBatchUpdateRefsEmitter = new Emitter<UpdateRefInfo[]>();
|
||||
|
||||
onBatchUpdateRefs = this.onBatchUpdateRefsEmitter.event;
|
||||
|
||||
constructor(entity: FlowNodeEntity) {
|
||||
super(entity);
|
||||
|
||||
this.toDispose.push(this.onBatchUpdateRefsEmitter);
|
||||
}
|
||||
|
||||
getDefaultData() {
|
||||
return {};
|
||||
}
|
||||
|
||||
protected get formData(): FlowNodeFormData {
|
||||
return this.entity.getData(FlowNodeFormData);
|
||||
}
|
||||
|
||||
protected get variableData(): FlowNodeVariableData {
|
||||
return this.entity.getData(FlowNodeVariableData);
|
||||
}
|
||||
|
||||
protected get facadeService() {
|
||||
return this.entity.getService(WorkflowVariableFacadeService);
|
||||
}
|
||||
|
||||
get refs(): Refs {
|
||||
const refs: Refs = {};
|
||||
|
||||
const fullData = this.formData.formModel.getFormItemValueByPath('/');
|
||||
|
||||
if (fullData) {
|
||||
traverseAllRefExpressions(fullData, (_ref, _dataPath) => {
|
||||
const keyPath = _ref?.content?.keyPath;
|
||||
if (!keyPath?.length) {
|
||||
return;
|
||||
}
|
||||
refs[convertGlobPath(_dataPath)] = keyPath;
|
||||
});
|
||||
}
|
||||
|
||||
return refs;
|
||||
}
|
||||
|
||||
get refVariables(): RefVariables {
|
||||
return Object.entries(this.refs).reduce((_acm, _curr) => {
|
||||
const [dataPath, keyPath] = _curr;
|
||||
|
||||
return {
|
||||
..._acm,
|
||||
[dataPath]: this.facadeService.getVariableFacadeByKeyPath(keyPath, {
|
||||
node: this.entity,
|
||||
checkScope: true,
|
||||
}),
|
||||
};
|
||||
}, {} satisfies RefVariables);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量更新变量引用
|
||||
* @param updateInfos 变更的 KeyPath 信息
|
||||
*/
|
||||
batchUpdateRefs(updateInfos: UpdateRefInfo[]) {
|
||||
let needUpdate = false;
|
||||
const fullData = this.formData.formModel.getFormItemValueByPath('/');
|
||||
|
||||
const setValueIn = (path: string, nextValue: unknown) => {
|
||||
// 新表单引擎更新数据
|
||||
if (isFormV2(this.entity)) {
|
||||
(this.formData.formModel as FormModelV2).setValueIn(path, nextValue);
|
||||
return;
|
||||
}
|
||||
|
||||
// 老表单引擎更新数据
|
||||
set(fullData, path, nextValue);
|
||||
return;
|
||||
};
|
||||
|
||||
Object.entries(this.refs).forEach(_entry => {
|
||||
const [dataPath, keyPath] = _entry;
|
||||
const updateInfo = updateInfos.find(_info =>
|
||||
matchPath(_info.beforeKeyPath, keyPath),
|
||||
);
|
||||
|
||||
if (updateInfo) {
|
||||
needUpdate = true;
|
||||
|
||||
// 没有传入更新后的 KeyPath,则更新 content
|
||||
if (!updateInfo.afterKeyPath) {
|
||||
// rehaje 更新 bug:设置值时需要 setter 内值局部更新,不能更改 setter 整体值
|
||||
setValueIn(
|
||||
`${dataPath}.content`,
|
||||
updateInfo.afterExpression?.content,
|
||||
);
|
||||
setValueIn(`${dataPath}.type`, updateInfo.afterExpression?.type);
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取更新后的 KeyPath
|
||||
* 假设要替换:[A, B] -> [C, D, E]
|
||||
* 当前 KeyPath 为 [A, B, F, G]
|
||||
* 则 nextPath 为 [C, D, E] + [F, G] = [C, D, E, F, G]
|
||||
*/
|
||||
const nextPath = [
|
||||
...updateInfo.afterKeyPath,
|
||||
...keyPath.slice(updateInfo.beforeKeyPath.length),
|
||||
];
|
||||
|
||||
setValueIn(`${dataPath}.content.keyPath`, nextPath);
|
||||
}
|
||||
});
|
||||
|
||||
if (needUpdate) {
|
||||
this.onBatchUpdateRefsEmitter.fire(updateInfos);
|
||||
this.formData.fireChange();
|
||||
}
|
||||
}
|
||||
|
||||
// 拥有全局变量的引用
|
||||
get hasGlobalRef(): boolean {
|
||||
return Object.values(this.refs).some(_keyPath =>
|
||||
(allGlobalVariableKeys as string[]).includes(_keyPath[0]),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user