feat: manually mirror opencoze's code from bytedance

Change-Id: I09a73aadda978ad9511264a756b2ce51f5761adf
This commit is contained in:
fanlv
2025-07-20 17:36:12 +08:00
commit 890153324f
14811 changed files with 1923430 additions and 0 deletions

View 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;
}

View File

@@ -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;
}
};

View 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 {};
}
}

View 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;
}

View 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;
}

View File

@@ -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,
},
];
}