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 React, { type FC } from 'react';
import {
FlowNodeVariableData,
ScopeProvider,
} from '@flowgram-adapter/free-layout-editor';
import { type DecoratorComponentProps } from '@flowgram-adapter/free-layout-editor';
const PrivateScopeDecorator: FC<DecoratorComponentProps> = props => {
const { context, children } = props;
const privateScope = context.node?.getData(FlowNodeVariableData)?.private;
if (privateScope) {
return (
<ScopeProvider value={{ scope: privateScope }}>{children}</ScopeProvider>
);
}
return <>{children}</>;
};
export const privateScopeDecorator = {
key: 'PrivateScopeDecorator',
component: PrivateScopeDecorator,
};

View File

@@ -0,0 +1,37 @@
/*
* 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 { provideNodeOutputVariables } from './variable-providers/provide-node-output-variables';
import { provideNodeBatchVariables } from './variable-providers/provide-node-batch-variables';
import { provideLoopOutputsVariables } from './variable-providers/provide-loop-output-variables';
import { provideLoopInputsVariables } from './variable-providers/provide-loop-input-variables';
import { consumeRefValueExpression } from './variable-consumers/consume-ref-value-expression';
import { privateScopeDecorator } from './decorators/private-scope-decorator';
export { provideMergeGroupVariablesEffect } from './variable-providers/provide-merge-group-variables';
export const variableProviders: VariableProviderAbilityOptions[] = [
provideNodeOutputVariables,
provideNodeBatchVariables,
provideLoopInputsVariables,
provideLoopOutputsVariables,
];
export const variableConsumers = [consumeRefValueExpression];
export const variableDecorators = [privateScopeDecorator];

View File

@@ -0,0 +1,51 @@
/*
* 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 { ASTFactory, type ASTNode } from '@flowgram-adapter/free-layout-editor';
import { type VariableConsumerAbilityOptions } from '@flowgram-adapter/free-layout-editor';
/**
* TODO 数组内 variable-consumer 拿不到 value 值
*/
export const consumeRefValueExpression: VariableConsumerAbilityOptions = {
key: 'consume-ref-value-expression',
parse(v, ctx) {
console.log(
'[ debugger test change ] > ',
ctx.formItem?.formModel,
ctx.formItem?.path,
v,
);
return ASTFactory.createKeyPathExpression({
keyPath: v?.content?.keyPath,
});
},
onInit(ctx) {
const { options, scope, formItem } = ctx;
const astKey = options?.namespace || formItem?.path || '';
return scope.ast.subscribe<ASTNode>(
_type => {
console.log('[ debugger type ] >', _type);
},
{
selector: _ast => _ast.get(astKey)?.returnType as ASTNode,
},
);
},
};

View File

@@ -0,0 +1,36 @@
/*
* 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 { uniqBy } from 'lodash-es';
import { type PropertyJSON } from '@flowgram-adapter/free-layout-editor';
import { type RefExpression } from '@coze-workflow/base/types';
export interface InputItem {
name: string;
input: RefExpression;
}
export const uniqInputs = (inputs?: InputItem[]): InputItem[] =>
uniqBy(
(inputs || []).filter(_input => _input && _input?.name),
_child => _child?.name,
);
export const uniqProperties = (properties?: PropertyJSON[]): PropertyJSON[] =>
uniqBy(
(properties || []).filter(_input => _input && _input?.key),
_child => _child?.key,
);

View File

@@ -0,0 +1,118 @@
/*
* 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 { ASTFactory } from '@flowgram-adapter/free-layout-editor';
import { type VariableProviderAbilityOptions } from '@flowgram-adapter/free-layout-editor';
import { ValueExpressionType } from '../../typings';
import { createRefExpression } from '../../core/extend-ast/custom-key-path-expression';
import { type InputItem, uniqInputs, uniqProperties } from './common';
interface ValueType {
inputParameters?: InputItem[];
variableParameters?: InputItem[];
}
export const parseLoopInputsByViewVariableMeta = (
nodeId: string,
value: ValueType,
) => {
const { inputParameters, variableParameters } = value || {};
const batchProperties = uniqInputs(inputParameters).map(_input =>
ASTFactory.createProperty({
key: _input?.name,
meta: {
label: `item (in ${_input?.name})`,
},
initializer: ASTFactory.createEnumerateExpression({
enumerateFor: createRefExpression({
keyPath: _input?.input?.content?.keyPath || [],
rawMeta: _input?.input?.rawMeta,
}),
}),
}),
);
const variableProperties = uniqInputs(variableParameters).map(_input => {
// 没有 rawMeta 时,可能是历史数据,走下面的兜底逻辑
if (_input?.input?.rawMeta?.type) {
return ASTFactory.createProperty({
key: _input?.name,
meta: {
mutable: true,
},
initializer: createRefExpression({
keyPath: _input?.input?.content?.keyPath || [],
rawMeta: _input?.input?.rawMeta,
}),
});
}
if (_input?.input?.type === ValueExpressionType.REF) {
return ASTFactory.createProperty({
key: _input?.name,
meta: {
mutable: true,
},
// 直接引用变量
initializer: ASTFactory.createKeyPathExpression({
keyPath: _input?.input?.content?.keyPath || [],
}),
});
}
return ASTFactory.createProperty({
key: _input?.name,
meta: {
mutable: true,
},
type: ASTFactory.createString(),
});
});
const indexProperties = [
ASTFactory.createProperty({
key: 'index',
type: ASTFactory.createInteger(),
}),
];
const properties = uniqProperties([
...batchProperties,
...indexProperties,
...variableProperties,
]);
return [
ASTFactory.createVariableDeclaration({
key: `${nodeId}.locals`,
type: ASTFactory.createObject({
properties,
}),
}),
];
};
/**
* 循环输入变量同步
*/
export const provideLoopInputsVariables: VariableProviderAbilityOptions = {
key: 'provide-loop-input-variables',
namespace: '/node/locals',
private: true,
scope: 'private',
parse(value: ValueType, context) {
return parseLoopInputsByViewVariableMeta(context.node.id, value);
},
};

View File

@@ -0,0 +1,70 @@
/*
* 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 { ASTFactory } from '@flowgram-adapter/free-layout-editor';
import { createWrapArrayExpression } from '../../core/extend-ast/wrap-array-expression';
import { type InputItem, uniqInputs } from './common';
export const parseLoopOutputsByViewVariableMeta = (
nodeId: string,
value: InputItem[],
) => {
const properties = uniqInputs(value || []).map(_input => {
const keyPath = _input?.input?.content?.keyPath;
// 如果选择的是 Loop 的 Variable 内的变量
if (keyPath?.[0] === nodeId) {
return ASTFactory.createProperty({
key: _input?.name,
// 直接引用变量
initializer: ASTFactory.createKeyPathExpression({
keyPath: _input?.input?.content?.keyPath || [],
}),
});
}
return ASTFactory.createProperty({
key: _input?.name,
// 输出类型包一层 Array
initializer: createWrapArrayExpression({
keyPath: _input?.input?.content?.keyPath || [],
}),
});
});
return [
ASTFactory.createVariableDeclaration({
key: `${nodeId}.outputs`,
type: ASTFactory.createObject({
properties,
}),
}),
];
};
/**
* 循环输出变量同步
*/
export const provideLoopOutputsVariables: VariableProviderAbilityOptions = {
key: 'provide-loop-output-variables',
namespace: '/node/outputs',
private: false,
scope: 'public',
parse(value, context) {
return parseLoopOutputsByViewVariableMeta(context.node.id, value);
},
};

View File

@@ -0,0 +1,96 @@
/*
* 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 {
ASTFactory,
ASTKind,
type ObjectType,
} from '@flowgram-adapter/free-layout-editor';
import { type VariableProviderAbilityOptions } from '@flowgram-adapter/free-layout-editor';
import { type EffectOptions } from '@flowgram-adapter/free-layout-editor';
import { createEffectFromVariableProvider } from '../../utils/variable-provider';
import { setValueIn } from '../../utils/form';
import { type RefExpression } from '../../typings';
import {
createMergeGroupExpression,
MergeStrategy,
} from '../../core/extend-ast/merge-group-expression';
import { createRefExpression } from '../../core/extend-ast/custom-key-path-expression';
import { WorkflowVariableFacadeService } from '../../core';
interface MergeGroup {
name: string;
variables: RefExpression[];
}
/**
* 合并组变量同步
*/
export const provideMergeGroupVariables: VariableProviderAbilityOptions = {
key: 'provide-merge-group-variables',
namespace: '/node/outputs',
parse(value: MergeGroup[], context) {
const nodeId = context.node.id;
return [
ASTFactory.createVariableDeclaration({
key: `${nodeId}.outputs`,
type: ASTFactory.createObject({
properties: value?.map(_item =>
ASTFactory.createProperty({
key: _item?.name,
initializer: createMergeGroupExpression({
mergeStrategy: MergeStrategy.FirstNotEmpty,
expressions: _item.variables.map(_v =>
createRefExpression({
keyPath: _v?.content?.keyPath || [],
rawMeta: _v?.rawMeta,
}),
),
}),
}),
),
}),
}),
];
},
onInit(ctx) {
const facadeService = ctx.node.getService(WorkflowVariableFacadeService);
return ctx.scope.ast.subscribe(() => {
// 监听输出变量变化,回填到表单的 outputs
const outputVariable = ctx.scope.output.variables[0];
if (outputVariable?.type?.kind === ASTKind.Object) {
const { properties } = outputVariable.type as ObjectType;
const nextOutputs = properties
.map(
_property =>
// OutputTree 组件中,所有树节点的 key 需要保证是唯一的
facadeService.getVariableFacadeByField(_property)
.viewMetaWithUniqKey,
)
.filter(Boolean);
setValueIn(ctx.node, 'outputs', nextOutputs);
}
});
},
};
export const provideMergeGroupVariablesEffect: EffectOptions[] =
createEffectFromVariableProvider(provideMergeGroupVariables);

View File

@@ -0,0 +1,50 @@
/*
* 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 { FlowNodeFormData } from '@flowgram-adapter/free-layout-editor';
import { Disposable } from '@flowgram-adapter/common';
import { parseNodeBatchByInputList } from '../../core';
export const provideNodeBatchVariables: VariableProviderAbilityOptions = {
key: 'provide-node-batch-variables',
namespace: '/node/locals',
scope: 'private',
parse(value, context) {
const batchMode =
context.formItem?.formModel.getFormItemValueByPath('/batchMode') ||
context.formItem?.formModel.getFormItemValueByPath('/inputs/batchMode');
if (batchMode !== 'batch') {
return [];
}
return parseNodeBatchByInputList(context.node.id, value);
},
onInit(context) {
const formData = context.node.getData(FlowNodeFormData);
if (!formData) {
return Disposable.create(() => null);
}
return formData.onDetailChange(_detail => {
if (_detail.path.includes('/batchMode')) {
context.triggerSync();
}
});
},
};

View File

@@ -0,0 +1,27 @@
/*
* 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 { parseNodeOutputByViewVariableMeta } from '../../core';
export const provideNodeOutputVariables: VariableProviderAbilityOptions = {
key: 'provide-node-output-variables',
namespace: '/node/outputs',
parse(value, context) {
return parseNodeOutputByViewVariableMeta(context.node.id, value);
},
};