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,61 @@
/*
* 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 { useEffect } from 'react';
import { isEqual } from 'lodash-es';
import { produce } from 'immer';
import { getIsStructOutput } from '../utils';
import type { FeishuBaseConfigFe } from '../../types';
import { type ConfigStore } from '../../store';
import { mutateOutputStruct } from './output-struct';
export const useSubscribeAndUpdateConfig = (store: ConfigStore) => {
useEffect(() => {
const unsub = store.subscribe((state, prevState) => {
const curConfig = state.config;
const preConfig = prevState.config;
const updatedConfig = produce<FeishuBaseConfigFe | null>(cfg =>
mutateFieldsInteraction(cfg, preConfig),
)(curConfig);
if (!updatedConfig || isEqual(curConfig, updatedConfig)) {
return;
}
state.setConfig(updatedConfig);
});
return unsub;
}, []);
};
const mutateFieldsInteraction = (
config: FeishuBaseConfigFe | null,
preConfig: FeishuBaseConfigFe | null,
) => {
if (!config) {
return;
}
if (!getIsStructOutput(config.output_type)) {
return;
}
if (isEqual(config, preConfig)) {
return;
}
mutateOutputStruct(
config.output_sub_component,
preConfig?.output_sub_component,
);
};

View File

@@ -0,0 +1,160 @@
/*
* 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 {
verifyOutputStructFieldAsGroupByKey,
verifyOutputStructFieldAsPrimaryKey,
} from '../utils';
import type {
BaseOutputStructLineType,
OutputSubComponentFe,
} from '../../types';
export const mutateOutputStruct = (
outputStructConfig: OutputSubComponentFe,
preOutputStructConfig?: OutputSubComponentFe,
) => {
const fields = outputStructConfig.item_list;
if (!fields || !fields.length) {
return;
}
mutateOutputStructPrimaryKey(fields, preOutputStructConfig?.item_list);
mutateOutputStructGroupByKey(fields, preOutputStructConfig?.item_list);
};
const getIsPrimaryField = (field: BaseOutputStructLineType) => field.is_primary;
const getIsGroupByKeyField = (field: BaseOutputStructLineType) =>
field.is_group_by_key;
const mutateOutputStructGroupByKey = (
fields: BaseOutputStructLineType[],
preFields?: BaseOutputStructLineType[],
) => {
const groupByKeyFields = fields.filter(getIsGroupByKeyField);
if (!groupByKeyFields.length) {
return;
}
const getter = (field: BaseOutputStructLineType) => field.is_group_by_key;
const setter = (field: BaseOutputStructLineType, val: boolean) =>
(field.is_group_by_key = val);
mutatePositiveFieldsMoreThanOne({
curFields: groupByKeyFields,
getter,
setter,
matchFn: filed => !!filed.is_group_by_key,
preFields,
});
mutateOnlyPositiveField({
curFields: groupByKeyFields,
getter,
setter,
verify: verifyOutputStructFieldAsGroupByKey,
});
};
const mutateOutputStructPrimaryKey = (
fields: BaseOutputStructLineType[],
preFields?: BaseOutputStructLineType[],
) => {
const primaryFields = fields.filter(getIsPrimaryField);
if (!primaryFields.length) {
return;
}
const setter = (field: BaseOutputStructLineType, val: boolean) =>
(field.is_primary = val);
const getter = (field: BaseOutputStructLineType) => field.is_primary;
mutatePositiveFieldsMoreThanOne({
curFields: primaryFields,
getter,
setter,
matchFn: field => !!field.is_primary,
preFields,
});
mutateOnlyPositiveField({
curFields: primaryFields,
getter,
setter,
verify: verifyOutputStructFieldAsPrimaryKey,
});
};
const mutateOnlyPositiveField = ({
curFields,
getter,
setter,
verify,
}: {
curFields: BaseOutputStructLineType[];
getter: (field: BaseOutputStructLineType) => boolean | undefined;
setter: (field: BaseOutputStructLineType, val: boolean) => void;
verify: (field: BaseOutputStructLineType) => boolean;
}) => {
const onlyField = curFields.at(0);
if (!onlyField) {
return;
}
const notValid = !verify(onlyField);
if (notValid && getter(onlyField)) {
setter(onlyField, false);
}
};
const mutatePositiveFieldsMoreThanOne = ({
curFields,
matchFn,
setter,
getter,
preFields,
}: {
curFields: BaseOutputStructLineType[];
matchFn: (filed: BaseOutputStructLineType) => boolean;
setter: (field: BaseOutputStructLineType, val: boolean) => void;
getter: (field: BaseOutputStructLineType) => boolean | undefined;
preFields?: BaseOutputStructLineType[];
}) => {
if (curFields.length <= 1) {
return;
}
const preMatchedFieldsId =
preFields?.filter(matchFn).map(field => field._id) || [];
curFields.forEach(field => {
if (preMatchedFieldsId.includes(field._id) && getter(field)) {
setter(field, false);
}
});
const leftMatchedFields = curFields.filter(matchFn);
if (leftMatchedFields.length <= 1) {
return;
}
leftMatchedFields.forEach((field, idx) => {
const targetVal = idx === leftMatchedFields.length - 1;
if (getter(field) !== targetVal) {
setter(field, targetVal);
}
});
};

View File

@@ -0,0 +1,174 @@
/*
* 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 { isNumber } from 'lodash-es';
import { I18n } from '@coze-arch/i18n';
import { InputComponentType } from '@coze-arch/bot-api/connector_api';
import {
type BaseOutputStructLineType,
type FeishuBaseConfigFe,
type InputConfigFe,
} from '../types';
import { INPUT_CONFIG_TEXT_MAX_CHAR } from '../constants';
import {
getIsSelectType,
getIsStructOutput,
verifyOutputStructFieldAsGroupByKey,
verifyOutputStructFieldAsPrimaryKey,
} from './utils';
export const validateFullConfig = (config: FeishuBaseConfigFe): boolean => {
if (!validateOutputConfig(config)) {
return false;
}
const inputValid = validateInputConfig(config);
// console.log('inputValid', inputValid);
return inputValid;
};
const validateOutputConfig = (config: FeishuBaseConfigFe) => {
if (!getIsStructOutput(config.output_type)) {
return isNumber(config.output_type);
}
const structFields = config.output_sub_component.item_list || [];
const structPatternVerified = validateOutputStructPattern(structFields);
if (!structPatternVerified) {
return false;
}
return validateOutputStructGroupByKeyAndPrimaryKey(structFields);
};
const validateOutputStructPattern = (
structFields: BaseOutputStructLineType[],
) => {
if (structFields.length < 1) {
return false;
}
return structFields.every(
field => !!field.key && isNumber(field.output_type),
);
};
const validateOutputStructGroupByKeyAndPrimaryKey = (
structFields: BaseOutputStructLineType[],
): boolean => {
const groupByKeyVerifyRes = validateOutputStructGroupByKey(structFields);
if (!groupByKeyVerifyRes.ok) {
return false;
}
const primaryKeyVerifyRes = validateOutputStructPrimaryKey(structFields);
return primaryKeyVerifyRes.ok;
};
interface StructOutputGroupByOrPrimaryKeyVerifyRes {
ok: boolean;
error: string;
}
export const validateOutputStructGroupByKey = (
structFields: BaseOutputStructLineType[],
): StructOutputGroupByOrPrimaryKeyVerifyRes => {
const fields = structFields.filter(field => field.is_group_by_key);
if (fields.length > 1) {
return {
ok: false,
error: I18n.t('publish_base_configFields_requiredWarn'),
};
}
const field = fields.at(0);
if (!field) {
return {
ok: false,
error: I18n.t('publish_base_configFields_requiredWarn'),
};
}
if (!verifyOutputStructFieldAsGroupByKey(field)) {
return {
ok: false,
error: '',
};
}
return { ok: true, error: '' };
};
export const validateOutputStructPrimaryKey = (
structFields: BaseOutputStructLineType[],
): StructOutputGroupByOrPrimaryKeyVerifyRes => {
const fields = structFields.filter(field => field.is_primary);
if (fields.length > 1) {
return {
ok: false,
error: I18n.t('publish_base_configFields_requiredWarn'),
};
}
const field = fields.at(0);
if (!field) {
return {
ok: false,
error: I18n.t('publish_base_configFields_requiredWarn'),
};
}
if (verifyOutputStructFieldAsPrimaryKey(field)) {
return { ok: true, error: '' };
}
return {
ok: false,
error: '',
};
};
const validateInputConfig = (config: FeishuBaseConfigFe) => {
if (!config.input_config.length) {
return false;
}
if (!validateInputFieldsCommonPattern(config.input_config)) {
return false;
}
return config.input_config.every(validateSingleInputFieldControl);
};
const validateInputFieldsCommonPattern = (inputConfigs: InputConfigFe[]) => {
if (!inputConfigs.every(cfg => cfg.title && cfg.input_component)) {
return false;
}
return !inputConfigs.some(cfg => cfg.invalid);
};
export const validateSingleInputFieldControl = (
inputControlConfig: InputConfigFe,
): boolean => {
const { type } = inputControlConfig.input_component;
if (!type) {
return false;
}
if (type === InputComponentType.Text) {
const maxChar = inputControlConfig.input_component.max_char;
return (
maxChar !== undefined &&
Number.isInteger(maxChar) &&
maxChar > 0 &&
maxChar <= INPUT_CONFIG_TEXT_MAX_CHAR
);
}
if (getIsSelectType(type)) {
return (
inputControlConfig.input_component.choice?.length > 0 &&
inputControlConfig.input_component.choice.every(x => !!x.name.trim())
);
}
return !!inputControlConfig.input_component.supported_type?.length;
};

View File

@@ -0,0 +1,48 @@
/*
* 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 { InputComponentType } from '@coze-arch/bot-api/connector_api';
import { type BaseOutputStructLineType } from '../types';
const OUTPUT_TYPE_STRUCT = 25;
export const OUTPUT_TYPE_TEXT = 1;
const OUTPUT_TYPE_NUMBER = 2;
export const getIsStructOutput = (id: number): boolean =>
id === OUTPUT_TYPE_STRUCT;
export const getIsTextOutput = (id: number | undefined): boolean =>
id === OUTPUT_TYPE_TEXT;
export const getIsNumberOutput = (id: number | undefined): boolean =>
id === OUTPUT_TYPE_NUMBER;
export const getIsSelectType = (type: InputComponentType) =>
[InputComponentType.SingleSelect, InputComponentType.MultiSelect].includes(
type,
);
export const verifyOutputStructFieldAsGroupByKey = (
field: BaseOutputStructLineType,
) => getIsTextOutput(field.output_type);
export const verifyOutputStructFieldAsPrimaryKey = (
field: BaseOutputStructLineType,
) => {
const outputType = field.output_type;
return getIsTextOutput(outputType) || getIsNumberOutput(outputType);
};