feat: manually mirror opencoze's code from bytedance
Change-Id: I09a73aadda978ad9511264a756b2ce51f5761adf
This commit is contained in:
42
frontend/packages/workflow/variable/src/utils/form.ts
Normal file
42
frontend/packages/workflow/variable/src/utils/form.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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 {
|
||||
type FormModelV2,
|
||||
isFormV2,
|
||||
type FlowNodeEntity,
|
||||
} from '@flowgram-adapter/free-layout-editor';
|
||||
import { FlowNodeFormData } from '@flowgram-adapter/free-layout-editor';
|
||||
|
||||
export function setValueIn(
|
||||
node: FlowNodeEntity,
|
||||
path: string,
|
||||
nextValue: unknown,
|
||||
) {
|
||||
const formData = node.getData(FlowNodeFormData);
|
||||
// 新表单引擎更新数据
|
||||
if (isFormV2(node)) {
|
||||
(formData.formModel as FormModelV2).setValueIn(path, nextValue);
|
||||
return;
|
||||
}
|
||||
|
||||
// 老表单引擎更新数据
|
||||
const fullData = formData.formModel.getFormItemValueByPath('/');
|
||||
set(fullData, path, nextValue);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
|
||||
import { type SchemaObject } from 'ajv';
|
||||
import {
|
||||
VariableTypeDTO,
|
||||
type VariableMetaDTO,
|
||||
AssistTypeDTO,
|
||||
} from '@coze-workflow/base';
|
||||
|
||||
// 需要转化的类型映射
|
||||
const VariableType2JsonSchemaProps = {
|
||||
[VariableTypeDTO.object]: {
|
||||
type: 'object',
|
||||
},
|
||||
[VariableTypeDTO.list]: {
|
||||
type: 'array',
|
||||
},
|
||||
[VariableTypeDTO.float]: {
|
||||
type: 'number',
|
||||
},
|
||||
[VariableTypeDTO.integer]: {
|
||||
type: 'integer',
|
||||
},
|
||||
[VariableTypeDTO.boolean]: {
|
||||
type: 'boolean',
|
||||
},
|
||||
[VariableTypeDTO.string]: {
|
||||
type: 'string',
|
||||
},
|
||||
[VariableTypeDTO.time]: {
|
||||
type: 'string',
|
||||
},
|
||||
};
|
||||
|
||||
const inputToJsonSchema = (
|
||||
input,
|
||||
level = 0,
|
||||
transformer?: (input: unknown) => VariableMetaDTO,
|
||||
): SchemaObject | undefined => {
|
||||
const _input = transformer ? transformer(input) : input;
|
||||
const { type, description } = _input;
|
||||
const props = VariableType2JsonSchemaProps[type];
|
||||
if (type === VariableTypeDTO.object) {
|
||||
const properties = {};
|
||||
const required: string[] = [];
|
||||
for (const field of _input.schema) {
|
||||
properties[field.name] = inputToJsonSchema(field, level + 1, transformer);
|
||||
if (field.required) {
|
||||
required.push(field.name);
|
||||
}
|
||||
}
|
||||
return {
|
||||
...props,
|
||||
description,
|
||||
required,
|
||||
properties,
|
||||
};
|
||||
} else if (type === VariableTypeDTO.list) {
|
||||
return {
|
||||
...props,
|
||||
description,
|
||||
items: inputToJsonSchema(_input.schema, level + 1, transformer),
|
||||
};
|
||||
}
|
||||
// 基础类型不需要生成jsonSchema, 图片类型不需要jsonSchema, 直接抛异常跳出递归
|
||||
if (
|
||||
level === 0 ||
|
||||
type === 'image' ||
|
||||
(_input.assistType && _input.assistType !== AssistTypeDTO.time)
|
||||
) {
|
||||
throw Error('not json type');
|
||||
}
|
||||
|
||||
return { ...props, description };
|
||||
};
|
||||
|
||||
export const generateInputJsonSchema = (
|
||||
input: VariableMetaDTO,
|
||||
transformer?: (input: unknown) => VariableMetaDTO,
|
||||
): SchemaObject | undefined => {
|
||||
try {
|
||||
const jsonSchema = inputToJsonSchema(input, 0, transformer);
|
||||
return jsonSchema;
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
26
frontend/packages/workflow/variable/src/utils/json.ts
Normal file
26
frontend/packages/workflow/variable/src/utils/json.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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 function safeJSONParse(jsonStr?: string) {
|
||||
try {
|
||||
if (!jsonStr) {
|
||||
return {};
|
||||
}
|
||||
return JSON.parse(jsonStr);
|
||||
} catch (e) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
24
frontend/packages/workflow/variable/src/utils/path.ts
Normal file
24
frontend/packages/workflow/variable/src/utils/path.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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 { FormPathService } from '@flowgram-adapter/free-layout-editor';
|
||||
export function convertGlobPath(path: string) {
|
||||
if (path.startsWith('/')) {
|
||||
const parts = FormPathService.normalize(path).slice(1).split('/');
|
||||
return parts.join('.');
|
||||
}
|
||||
return path;
|
||||
}
|
||||
102
frontend/packages/workflow/variable/src/utils/sub-canvas.tsx
Normal file
102
frontend/packages/workflow/variable/src/utils/sub-canvas.tsx
Normal file
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* 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 {
|
||||
FlowNodeVariableData,
|
||||
type Scope,
|
||||
} from '@flowgram-adapter/free-layout-editor';
|
||||
import { type FlowNodeEntity } from '@flowgram-adapter/free-layout-editor';
|
||||
import { type WorkflowNodeMeta } from '@flowgram-adapter/free-layout-editor';
|
||||
|
||||
/**
|
||||
* 获取实际的父节点
|
||||
* @param node
|
||||
* @returns
|
||||
*/
|
||||
export function getParentNode(
|
||||
node: FlowNodeEntity,
|
||||
): FlowNodeEntity | undefined {
|
||||
const initParent = node.document.originTree.getParent(node);
|
||||
|
||||
if (!initParent) {
|
||||
return initParent;
|
||||
}
|
||||
const nodeMeta = initParent.getNodeMeta<WorkflowNodeMeta>();
|
||||
const subCanvas = nodeMeta.subCanvas?.(initParent);
|
||||
if (subCanvas?.isCanvas) {
|
||||
return subCanvas.parentNode;
|
||||
}
|
||||
|
||||
return initParent;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取实际的子节点
|
||||
* @param node
|
||||
* @returns
|
||||
*/
|
||||
export function getChildrenNode(node: FlowNodeEntity): FlowNodeEntity[] {
|
||||
const nodeMeta = node.getNodeMeta<WorkflowNodeMeta>();
|
||||
const subCanvas = nodeMeta.subCanvas?.(node);
|
||||
|
||||
if (subCanvas) {
|
||||
// 子画布本身不存在 children
|
||||
if (subCanvas.isCanvas) {
|
||||
return [];
|
||||
} else {
|
||||
return subCanvas.canvasNode.collapsedChildren;
|
||||
}
|
||||
}
|
||||
|
||||
return node.document.originTree.getChildren(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* 节点是否包含子画布
|
||||
* @param node
|
||||
* @returns
|
||||
*/
|
||||
export function hasChildCanvas(node: FlowNodeEntity): boolean {
|
||||
const nodeMeta = node.getNodeMeta<WorkflowNodeMeta>();
|
||||
const subCanvas = nodeMeta.subCanvas?.(node);
|
||||
|
||||
return !!subCanvas?.canvasNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取子节点所有输出变量的作用域链
|
||||
* @param node
|
||||
* @returns
|
||||
*/
|
||||
export function getHasChildCanvasNodePublicDeps(
|
||||
node: FlowNodeEntity,
|
||||
includePrivate = true,
|
||||
): Scope[] {
|
||||
const _private = node.getData(FlowNodeVariableData)?.private;
|
||||
|
||||
return getChildrenNode(node)
|
||||
.map(_node => _node.getData(FlowNodeVariableData).public)
|
||||
.concat(_private && includePrivate ? [_private] : []);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取父节点的
|
||||
* @param node
|
||||
* @returns
|
||||
*/
|
||||
export function getParentPublic(node: FlowNodeEntity): Scope | undefined {
|
||||
return getParentNode(node)?.getData(FlowNodeVariableData)?.public;
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* 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 VariableProviderAbilityOptions } from '@flowgram-adapter/free-layout-editor';
|
||||
import {
|
||||
ASTKind,
|
||||
DataEvent,
|
||||
type FlowNodeEntity,
|
||||
FlowNodeVariableData,
|
||||
type Effect,
|
||||
type Scope,
|
||||
type EffectOptions,
|
||||
} from '@flowgram-adapter/free-layout-editor';
|
||||
|
||||
/**
|
||||
* 根据 VariableProvider 生成 FormV2 的 Effect
|
||||
* @param options
|
||||
* @returns
|
||||
*/
|
||||
export function createEffectFromVariableProvider(
|
||||
options: VariableProviderAbilityOptions,
|
||||
): EffectOptions[] {
|
||||
const getScope = (node: FlowNodeEntity): Scope => {
|
||||
const variableData: FlowNodeVariableData =
|
||||
node.getData(FlowNodeVariableData);
|
||||
|
||||
if (options.private) {
|
||||
return variableData.initPrivate();
|
||||
}
|
||||
return variableData.public;
|
||||
};
|
||||
|
||||
const transformValueToAST: Effect = ({ value, context }) => {
|
||||
if (!context) {
|
||||
return;
|
||||
}
|
||||
const { node } = context;
|
||||
const scope = getScope(node);
|
||||
|
||||
const defaultNamespace = options.private ? '/node/locals' : '/node/outputs';
|
||||
|
||||
scope.ast.set(options.namespace || defaultNamespace, {
|
||||
kind: ASTKind.VariableDeclarationList,
|
||||
declarations: options.parse(value, {
|
||||
node,
|
||||
scope,
|
||||
options,
|
||||
formItem: undefined,
|
||||
}),
|
||||
});
|
||||
};
|
||||
|
||||
return [
|
||||
{
|
||||
event: DataEvent.onValueInit,
|
||||
effect: (params => {
|
||||
const { context } = params;
|
||||
|
||||
const scope = getScope(context.node);
|
||||
const disposable = options.onInit?.({
|
||||
node: context.node,
|
||||
scope,
|
||||
options,
|
||||
formItem: undefined,
|
||||
// @ts-expect-error 新表单引擎不支持
|
||||
triggerSync: undefined,
|
||||
});
|
||||
|
||||
if (disposable) {
|
||||
// 作用域销毁时同时销毁该监听
|
||||
scope.toDispose.push(disposable);
|
||||
}
|
||||
|
||||
transformValueToAST(params);
|
||||
}) as Effect,
|
||||
},
|
||||
{
|
||||
event: DataEvent.onValueChange,
|
||||
effect: (params => {
|
||||
transformValueToAST(params);
|
||||
}) as Effect,
|
||||
},
|
||||
];
|
||||
}
|
||||
Reference in New Issue
Block a user