@lark-project/cli
Version:
飞书项目插件开发工具
694 lines (693 loc) • 30.5 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.reverseTransformQueryLocalConfig = void 0;
const dsl_1 = require("./dsl");
const logger_1 = require("../logger");
/**
* 后端把 tree-multi-select 选项的 children 回成 null(push 时不传),但 schema 要求
* children 为数组,这里把 null 补回 [],保证落盘后 schema 校验通过、且与 push 侧形态闭合。
* 递归处理任意层级的级联子项。
* 注:实测部分后端读端点已直接回 [],此时本函数命中不到 null、基本空转——留作 schema 兜底。
*/
function fillNullTreeChildren(options) {
if (!Array.isArray(options))
return options;
return options.map(opt => {
if (!opt || typeof opt !== 'object')
return opt;
if (opt.children === null)
return Object.assign(Object.assign({}, opt), { children: [] });
if (Array.isArray(opt.children))
return Object.assign(Object.assign({}, opt), { children: fillNullTreeChildren(opt.children) });
return opt;
});
}
function denormalizeI18nInfo(i18nInfo) {
if (!i18nInfo)
return undefined;
const out = {};
for (const [lang, value] of Object.entries(i18nInfo)) {
out[lang] = {
name: value === null || value === void 0 ? void 0 : value.name,
description: value === null || value === void 0 ? void 0 : value.description,
};
}
return out;
}
function denormalizeSingleI18nInfo(i18nInfo) {
if (!i18nInfo) {
return { name: '', description: '' };
}
const zh = i18nInfo['zh-cn'];
if (zh) {
return { name: zh.name, description: zh.description };
}
const first = Object.values(i18nInfo)[0];
if (first) {
return { name: first.name, description: first.description };
}
return { name: '', description: '' };
}
function pickPlatformData(platform) {
var _a, _b;
if (!platform)
return undefined;
const result = {};
for (const [platformKey, value] of Object.entries(platform)) {
if (!value)
continue;
const resourceInfo = value;
let scene;
if ((_a = resourceInfo.scene_info_list) === null || _a === void 0 ? void 0 : _a.length) {
scene = resourceInfo.scene_info_list
.map(item => item.value)
.filter((v) => typeof v === 'number');
}
else if ((_b = resourceInfo.scene) === null || _b === void 0 ? void 0 : _b.length) {
scene = resourceInfo.scene;
}
// ISS-Q:远端旧 schema 容许空字符串落库;归一化时把空 resource / type 过滤掉,
// 避免 backendToLocal 输出空字符串后被本地新 schema (minLength) 拦下。
const next = {
resource: resourceInfo.resource || undefined,
scene,
type: resourceInfo.type || undefined,
};
// 保留 resource 存在时的结构;scene 为空时不强行补 [](让 schema 自己约束)
Object.keys(next).forEach(k => {
if (next[k] === undefined) {
delete next[k];
}
});
if (Object.keys(next).length) {
result[platformKey] = next;
}
}
return Object.keys(result).length ? result : undefined;
}
function getExtension(point, subType) {
var _a;
return (_a = point.extension) === null || _a === void 0 ? void 0 : _a.find(ext => ext.ext_subType === subType);
}
function extractExtValue(ext, key) {
if (!(ext === null || ext === void 0 ? void 0 : ext.ext_config))
return undefined;
const cfg = ext.ext_config;
if (cfg && typeof cfg === 'object' && key in cfg) {
return cfg[key];
}
return cfg;
}
/**
* 返回本地点位类型名(camelCase,与 QueryLocalConfigInput 字段对应),
* 而非后端 wire 值。Wire → local 的翻译表:
* board → page / config → configuration / field_template → customField / builder_comp → liteAppComponent
*/
function inferType(point, mapKey) {
// 后端返回的 wire type 转本地名
const wireToLocal = {
board: 'page',
view: 'view',
dashboard: 'dashboard',
config: 'configuration',
control: 'control',
button: 'button',
intercept: 'intercept',
listen_event: 'listen_event',
component: 'component',
field_template: 'customField',
builder_comp: 'liteAppComponent',
ai_node: 'aiNode',
ai_field: 'aiField',
};
if (point.type && wireToLocal[point.type])
return wireToLocal[point.type];
if (point.component_type)
return 'component';
const prefixes = [
['board_', 'page'],
['view_', 'view'],
['dashboard_', 'dashboard'],
['config_', 'configuration'],
['control_', 'control'],
['button_', 'button'],
['intercept_', 'intercept'],
['listen_event_', 'listen_event'],
['field_template_', 'customField'],
['lite_app_', 'liteAppComponent'],
['component_schedule_', 'component'],
// 注意:'ai_node_' 必须排在 'ai_field_' 前,否则用户若把 key 写成 ai_field_xxx
// 也以前缀 ai_ 起头时不会误匹配。这里因为前缀不重叠,顺序无所谓——但保持显式。
['ai_node_', 'aiNode'],
['ai_field_', 'aiField'],
];
for (const [prefix, t] of prefixes) {
if (mapKey.startsWith(prefix))
return t;
}
return undefined;
}
function reversePagePoint(input) {
var _a, _b;
return {
key: input.key || '',
icon: (_a = input.icon) !== null && _a !== void 0 ? _a : '',
name: (_b = input.name) !== null && _b !== void 0 ? _b : '',
description: input.description,
i18n_info: denormalizeI18nInfo(input.i18n_info),
platform: pickPlatformData(input.platform),
};
}
function reverseViewPoint(input) {
var _a, _b;
return {
key: input.key || '',
icon: (_a = input.icon) !== null && _a !== void 0 ? _a : '',
name: (_b = input.name) !== null && _b !== void 0 ? _b : '',
description: input.description,
i18n_info: denormalizeI18nInfo(input.i18n_info),
work_item_type: input.work_item_type,
platform: pickPlatformData(input.platform),
};
}
function reverseDashboardPoint(input) {
var _a;
return {
key: input.key || '',
name: (_a = input.name) !== null && _a !== void 0 ? _a : '',
description: input.description,
i18n_info: denormalizeI18nInfo(input.i18n_info),
work_item_type: input.work_item_type,
custom_work_item_type: input.custom_work_item_type,
platform: pickPlatformData(input.platform),
};
}
function reverseConfigurationPoint(input) {
const platform = pickPlatformData(input.platform);
return {
key: input.key || '',
platform: (platform === null || platform === void 0 ? void 0 : platform.web) ? { web: platform.web } : undefined,
};
}
function reverseControlPoint(input) {
const key = input.key || '';
const platform = (pickPlatformData(input.platform) || {});
const tableCell = (0, dsl_1.denormalizeDslForLocal)(extractExtValue(getExtension(input, 'web_control_table_cell'), key));
if (tableCell !== undefined) {
platform.web = platform.web || {};
platform.web.table_cell = tableCell;
}
// ISS-Q:远端旧 schema 容许 tableUrl.url / token 空字符串;空 url 时整个 table_url 节就不写
// (新 schema UrlHttp pattern '^https?://' 会拒空字符串)。
const tableUrl = extractExtValue(getExtension(input, 'web_control_table_url'), key);
if (tableUrl && typeof tableUrl === 'object' && tableUrl.url) {
platform.web = platform.web || {};
platform.web.table_url = {
url: tableUrl.url,
token: tableUrl.token || undefined,
};
}
const mobileBlockStyle = extractExtValue(getExtension(input, 'mob_control_block_style'), key);
if (typeof mobileBlockStyle === 'string' && mobileBlockStyle) {
platform.mobile = platform.mobile || {};
platform.mobile.mobile_block_style = mobileBlockStyle;
}
// ISS-Q:远端旧 schema 容许 url/token 空字符串;归一化时空字符串 → undefined(不写字段)
// 避免本地新 schema TokenString minLength: 36 / UrlHttp minLength: 1 拒。
return {
key,
name: input.name,
description: input.description,
i18n_info: denormalizeI18nInfo(input.i18n_info),
work_item_type: input.work_item_type,
url: input.url || undefined,
token: input.token || undefined,
platform: Object.keys(platform).length ? platform : undefined,
};
}
function reverseButtonPoint(input) {
const key = input.key || '';
const platform = (pickPlatformData(input.platform) || {});
const mode = extractExtValue(getExtension(input, 'point_run_mode'), key);
if (typeof mode === 'string') {
platform.web = platform.web || {};
platform.web.mode = mode;
}
const initSize = extractExtValue(getExtension(input, 'button_pop_up_init_size'), key);
if (initSize && typeof initSize === 'object') {
platform.web = platform.web || {};
platform.web.init_size = initSize;
}
return {
key,
name: input.name,
description: input.description,
i18n_info: denormalizeI18nInfo(input.i18n_info),
work_item_type: input.work_item_type,
custom_work_item_type: input.custom_work_item_type,
platform: Object.keys(platform).length ? platform : undefined,
};
}
function reverseInterceptPoint(input) {
return {
key: input.key || '',
type: 'intercept',
name: input.name || '',
description: input.description,
i18n_info: denormalizeI18nInfo(input.i18n_info),
url: input.url || '',
token: input.token || '',
event_config: (input.event_config || []),
platform: pickPlatformData(input.platform),
};
}
function reverseListenEventPoint(input) {
return {
key: input.key,
type: 'listen_event',
url: input.url || '',
token: input.token || '',
event_config: (input.event_config || []),
};
}
function reverseComponentSchedulePoint(input) {
const platform = pickPlatformData(input.platform);
return {
key: input.key || '',
component_type: 'schedule',
work_item_type: input.work_item_type,
platform: (platform === null || platform === void 0 ? void 0 : platform.web) ? { web: platform.web } : undefined,
};
}
function reverseCustomFieldPoint(input) {
var _a, _b;
const key = input.key || '';
const tableLayout = (0, dsl_1.denormalizeDslForLocal)(extractExtValue(getExtension(input, 'field_template_table_cell'), key));
const tableUrl = extractExtValue(getExtension(input, 'field_template_table_url'), key);
const mobileBlockStyle = extractExtValue(getExtension(input, 'mob_field_template_block_style'), key);
const extraConfig = extractExtValue(getExtension(input, 'field_template_extra'), key);
const subFieldConfig = extractExtValue(getExtension(input, 'field_template_sub_field'), key);
const platform = pickPlatformData(input.platform) || {};
const webResource = ((_a = platform.web) === null || _a === void 0 ? void 0 : _a.resource) || '';
const mobileResource = ((_b = platform.mobile) === null || _b === void 0 ? void 0 : _b.resource) || '';
return {
key,
i18n_info: denormalizeSingleI18nInfo(input.i18n_info),
subfield: ((subFieldConfig === null || subFieldConfig === void 0 ? void 0 : subFieldConfig.sub_fields) || []).map((sf) => {
var _a, _b, _c;
const sfName = (_b = (_a = sf.field_name) !== null && _a !== void 0 ? _a : sf.name) !== null && _b !== void 0 ? _b : '';
const sfDesc = (_c = sf.field_desc) !== null && _c !== void 0 ? _c : sf.desc;
// 从后端 i18n_info 提取;若无则用 name/desc 构造 zh 条目
const i18nInfo = sf.i18n_info && typeof sf.i18n_info === 'object' && Object.keys(sf.i18n_info).length
? sf.i18n_info
: { zh: { name: sfName, desc: sfDesc !== null && sfDesc !== void 0 ? sfDesc : '' } };
return {
name: sfName || undefined,
i18n_info: i18nInfo,
desc: sfDesc,
field_type: sf.field_type,
field_key: sf.field_key,
options: fillNullTreeChildren(sf.options),
};
}),
platform: {
web: {
resource: webResource,
need_extra_config: extraConfig === null || extraConfig === void 0 ? void 0 : extraConfig.enable_extra_config,
table_layout: tableLayout,
table_data_url: tableUrl === null || tableUrl === void 0 ? void 0 : tableUrl.url,
table_data_token: tableUrl === null || tableUrl === void 0 ? void 0 : tableUrl.token,
},
mobile: {
resource: mobileResource,
mobile_block_style: typeof mobileBlockStyle === 'string' ? mobileBlockStyle : '',
},
},
};
}
function templateToBuilderProp(template, extension) {
var _a, _b;
switch (template) {
case 'text':
return { prop_type: 'text' };
case 'number':
return { prop_type: 'number' };
case 'dateWithTime':
return { prop_type: 'precise_date' };
case 'dateRange':
return { prop_type: 'date_range' };
case 'boolean':
return { prop_type: 'boolean' };
case 'select':
return { prop_type: 'select', options: (_a = extension === null || extension === void 0 ? void 0 : extension.options) === null || _a === void 0 ? void 0 : _a.optionList };
case 'multiSelect':
return { prop_type: 'multi_select', options: (_b = extension === null || extension === void 0 ? void 0 : extension.options) === null || _b === void 0 ? void 0 : _b.optionList };
case 'workItemInstance':
return { prop_type: 'work_item_instance' };
case 'workItemTypeSelect':
return { prop_type: 'work_item_type' };
case 'workItemInstanceWithField':
return { prop_type: 'work_item_instance', with_field: true };
case 'workItemTypeWithField':
return { prop_type: 'work_item_type', with_field: true };
case 'viewSelect':
return { prop_type: 'view_select' };
case 'dataSource':
return { prop_type: 'data_source' };
case 'dataSourceWithField':
return { prop_type: 'data_source', with_field: true };
default:
return undefined;
}
}
function subscribeTemplateToBuilderProp(template) {
switch (template) {
case 'text':
return { prop_type: 'text' };
case 'number':
return { prop_type: 'number' };
case 'dateWithTime':
return { prop_type: 'precise_date' };
case 'dateRange':
return { prop_type: 'date_range' };
case 'workItemInstance':
return { prop_type: 'work_item_instance' };
case 'workItemTypeSelect':
return { prop_type: 'work_item_type' };
case 'viewSelect':
return { prop_type: 'view_select' };
case 'dataSource':
return { prop_type: 'data_source' };
default:
return undefined;
}
}
function reverseLiteAppComponentPoint(input) {
var _a, _b, _c, _d, _e, _f, _g, _h;
const key = input.key || '';
const iconCfg = extractExtValue(getExtension(input, 'point_icon'), key);
const layoutCfg = extractExtValue(getExtension(input, 'builder_comp_layout'), key);
const presetCfg = extractExtValue(getExtension(input, 'builder_comp_default_value'), key);
const propCfg = extractExtValue(getExtension(input, 'builder_comp_prop_config'), key);
const subscribePropCfg = extractExtValue(getExtension(input, 'builder_comp_subscribe_prop_config'), key);
const platform = pickPlatformData(input.platform) || {};
const webResource = ((_a = platform.web) === null || _a === void 0 ? void 0 : _a.resource) || '';
const layout = {
mode: (_b = layoutCfg === null || layoutCfg === void 0 ? void 0 : layoutCfg.mode) !== null && _b !== void 0 ? _b : 0,
staticHeight: layoutCfg === null || layoutCfg === void 0 ? void 0 : layoutCfg.staticHeight,
staticWidth: layoutCfg === null || layoutCfg === void 0 ? void 0 : layoutCfg.staticWidth,
minW: layoutCfg === null || layoutCfg === void 0 ? void 0 : layoutCfg.minWidth,
maxW: layoutCfg === null || layoutCfg === void 0 ? void 0 : layoutCfg.maxWidth,
minH: layoutCfg === null || layoutCfg === void 0 ? void 0 : layoutCfg.minHeight,
maxH: layoutCfg === null || layoutCfg === void 0 ? void 0 : layoutCfg.maxHeight,
presetW: (_e = (_d = (_c = presetCfg === null || presetCfg === void 0 ? void 0 : presetCfg.presets) === null || _c === void 0 ? void 0 : _c[0]) === null || _d === void 0 ? void 0 : _d.layout) === null || _e === void 0 ? void 0 : _e.width,
presetH: (_h = (_g = (_f = presetCfg === null || presetCfg === void 0 ? void 0 : presetCfg.presets) === null || _f === void 0 ? void 0 : _f[0]) === null || _g === void 0 ? void 0 : _g.layout) === null || _h === void 0 ? void 0 : _h.height,
};
Object.keys(layout).forEach(k => layout[k] === undefined && delete layout[k]);
const properties = ((propCfg === null || propCfg === void 0 ? void 0 : propCfg.sub_comp) || [])
.map((ex) => {
const template = ex === null || ex === void 0 ? void 0 : ex.template;
const detail = templateToBuilderProp(template, ex);
if (!detail)
return undefined;
return Object.assign({ name: ex === null || ex === void 0 ? void 0 : ex.propName, prop_key: ex === null || ex === void 0 ? void 0 : ex.propKey }, detail);
})
.filter(Boolean);
const outputs = ((subscribePropCfg === null || subscribePropCfg === void 0 ? void 0 : subscribePropCfg.sub_comp) || [])
.map((ex) => {
const template = ex === null || ex === void 0 ? void 0 : ex.template;
const detail = subscribeTemplateToBuilderProp(template);
if (!detail)
return undefined;
return Object.assign({ name: ex === null || ex === void 0 ? void 0 : ex.propName, prop_key: ex === null || ex === void 0 ? void 0 : ex.propKey }, detail);
})
.filter(Boolean);
return {
key,
i18n_info: input.i18n_info || {},
// icon_url 已标 deprecated:回写仅为 pull 往返一致(避免存量 yaml diff 噪音),上传时会被 localToBackend 忽略
icon_url: (iconCfg === null || iconCfg === void 0 ? void 0 : iconCfg.url) || '',
properties,
outputs,
platform: {
web: {
resource: webResource,
layout,
},
},
};
}
/* ───────────────────── AI Node / AI Field(反向) ───────────────────── */
const AI_NODE_DATA_TYPES = new Set([
'field',
'node_form',
'node_field',
'select',
'multi_select',
'text',
'number',
]);
const AI_FIELD_DATA_TYPES = new Set([
'field',
'role_type',
'select',
'multi_select',
'text',
'number',
'prompt',
]);
/** zh/en/ja 全空(undefined / 空串)时返回 undefined,避免 reverse 出 {zh:'',en:'',ja:''} 这种
* 跟用户手写最简 draft 不匹配、导致 `local-config diff` 误报 modified 的脏对象。 */
function compactI18nText(v) {
if (!v)
return undefined;
const out = {};
if (v.zh)
out.zh = v.zh;
if (v.en)
out.en = v.en;
if (v.ja)
out.ja = v.ja;
return Object.keys(out).length ? out : undefined;
}
/** wire optionList 单项反向:wire.value → local.label;保留 translation。
* 老数据可能没 value,则退回 wire.label,保证 round-trip 不丢失。 */
function reverseOptionList(optionList) {
if (!Array.isArray(optionList) || !optionList.length)
return undefined;
return optionList
.filter((o) => o && typeof o === 'object')
.map((o) => {
var _a, _b, _c, _d, _e, _f, _g;
return ({
label: typeof o.value === 'string' && o.value ? o.value : ((_a = o.label) !== null && _a !== void 0 ? _a : ''),
translation: {
zh: (_c = (_b = o.translation) === null || _b === void 0 ? void 0 : _b.zh) !== null && _c !== void 0 ? _c : '',
en: (_e = (_d = o.translation) === null || _d === void 0 ? void 0 : _d.en) !== null && _e !== void 0 ? _e : '',
ja: (_g = (_f = o.translation) === null || _f === void 0 ? void 0 : _f.ja) !== null && _g !== void 0 ? _g : '',
},
});
});
}
/** 从 wire 推回本地 data_type。template 是分发依据:fieldSelect 走 opts.dataType,
* 其它(select/multi_select/text/number/prompt)由 template 直接对应一项。 */
function inferDataTypeFromWire(template, optsDataType, validSet) {
if (typeof template === 'string' && template !== 'fieldSelect') {
return validSet.has(template) ? [template] : [];
}
if (Array.isArray(optsDataType)) {
return optsDataType.filter((v) => typeof v === 'string' && validSet.has(v));
}
return [];
}
function reverseAINodeProperty(sub) {
var _a, _b;
if (!sub || typeof sub !== 'object')
return undefined;
const propKey = typeof sub.propKey === 'string' ? sub.propKey : undefined;
if (!propKey)
return undefined;
const opts = (_a = sub.options) !== null && _a !== void 0 ? _a : {};
const dataType = inferDataTypeFromWire(sub.template, opts.dataType, AI_NODE_DATA_TYPES);
const isFieldSelect = sub.template === 'fieldSelect' || (sub.template == null && Array.isArray(opts.dataType));
return {
key: propKey,
name: (_b = compactI18nText(sub.propName)) !== null && _b !== void 0 ? _b : {},
description: compactI18nText(sub.propDesc),
data_type: dataType,
input_field_types: isFieldSelect && Array.isArray(opts.fieldType) && opts.fieldType.length ? opts.fieldType : undefined,
max_field_count: isFieldSelect && typeof opts.max === 'number' ? opts.max : undefined,
options: reverseOptionList(opts.optionList),
};
}
function reverseAIFieldProperty(sub) {
var _a, _b;
if (!sub || typeof sub !== 'object')
return undefined;
const propKey = typeof sub.propKey === 'string' ? sub.propKey : undefined;
if (!propKey)
return undefined;
const opts = (_a = sub.options) !== null && _a !== void 0 ? _a : {};
const isPrompt = sub.template === 'prompt';
const isFieldSelect = sub.template === 'fieldSelect' || (sub.template == null && Array.isArray(opts.dataType));
const rawDataType = isPrompt
? ['prompt']
: inferDataTypeFromWire(sub.template, opts.dataType, AI_FIELD_DATA_TYPES);
return {
key: propKey,
name: (_b = compactI18nText(sub.propName)) !== null && _b !== void 0 ? _b : {},
description: compactI18nText(sub.propDesc),
data_type: rawDataType,
input_field_types: !isPrompt && isFieldSelect && Array.isArray(opts.fieldType) && opts.fieldType.length ? opts.fieldType : undefined,
options: !isPrompt ? reverseOptionList(opts.optionList) : undefined,
};
}
function reverseAINodePoint(input) {
var _a, _b, _c, _d;
const key = input.key || '';
const propsExt = extractExtValue(getExtension(input, 'ai_node_properties'), key);
const cardExt = extractExtValue(getExtension(input, 'ai_node_card'), key);
const properties = ((propsExt === null || propsExt === void 0 ? void 0 : propsExt.sub_prop) || [])
.map(reverseAINodeProperty)
.filter((p) => p !== undefined);
const enableCard = !!(cardExt === null || cardExt === void 0 ? void 0 : cardExt.enable_ai_workflow_node_card);
const platformWeb = (_b = (_a = input.platform) === null || _a === void 0 ? void 0 : _a.web) === null || _b === void 0 ? void 0 : _b.resource;
const platformMobile = (_d = (_c = input.platform) === null || _c === void 0 ? void 0 : _c.mobile) === null || _d === void 0 ? void 0 : _d.resource;
const platform = enableCard && (platformWeb || platformMobile)
? {
web: platformWeb ? { resource: platformWeb } : undefined,
mobile: platformMobile ? { resource: platformMobile } : undefined,
}
: undefined;
// 不输出 type(schema 里是可选 const,CLI 转换时会强制覆盖);needCustomCard 仅 true 时输出
// (缺省即 false)——这样 reverse 出的形态跟"用户手写最简 draft"对齐,`local-config diff`
// 在 set→update 往返后不会因为多了 type / needCustomCard:false 而误报 modified。
return {
key,
url: input.url || '',
token: input.token || '',
properties: properties.length ? properties : undefined,
needCustomCard: enableCard ? true : undefined,
platform: platform,
};
}
// 与 localToBackend.ts AI_FIELD_OUTPUT_TYPES_ALL 同步——后端拉回的 field_types 若恰好等于全集,
// 折叠回 user-friendly 的 'all' 以避免本地配置 churn。业务侧扩展白名单时两边同步改。
const AI_FIELD_OUTPUT_TYPES_ALL = [
'text',
'number',
'date',
'schedule',
'bool',
'multi-text',
'multi-user',
'user',
'multi-file',
'link',
];
function foldAIFieldOutputTypes(input) {
if (!Array.isArray(input))
return [];
const types = input.filter((v) => typeof v === 'string');
if (types.length !== AI_FIELD_OUTPUT_TYPES_ALL.length)
return types;
const set = new Set(types);
return AI_FIELD_OUTPUT_TYPES_ALL.every(t => set.has(t)) ? ['all'] : types;
}
function reverseAIFieldPoint(input) {
const key = input.key || '';
const propsExt = extractExtValue(getExtension(input, 'ai_field_properties'), key);
const properties = ((propsExt === null || propsExt === void 0 ? void 0 : propsExt.sub_prop) || [])
.map(reverseAIFieldProperty)
.filter((p) => p !== undefined);
// 不输出 type(同 reverseAINodePoint)——保持 reverse 形态与最简 draft 对齐
return {
key,
url: input.url || '',
token: input.token || '',
properties: properties.length ? properties : undefined,
output_field_types: foldAIFieldOutputTypes(input.field_types),
};
}
/**
* 反向:把后端 point_info_map 结构转换回本地 YAML 对应的前端结构。
*/
function reverseTransformQueryLocalConfig(input) {
const out = {};
if (!input)
return out;
for (const [mapKey, value] of Object.entries(input)) {
const point = value;
const pointType = inferType(point, mapKey);
switch (pointType) {
case 'page':
out.page = out.page || [];
out.page.push(reversePagePoint(point));
break;
case 'view':
out.view = out.view || [];
out.view.push(reverseViewPoint(point));
break;
case 'dashboard':
out.dashboard = out.dashboard || [];
out.dashboard.push(reverseDashboardPoint(point));
break;
case 'configuration':
out.configuration = out.configuration || [];
out.configuration.push(reverseConfigurationPoint(point));
break;
case 'control':
out.control = out.control || [];
out.control.push(reverseControlPoint(point));
break;
case 'button':
out.button = out.button || [];
out.button.push(reverseButtonPoint(point));
break;
case 'intercept':
out.intercept = out.intercept || [];
out.intercept.push(reverseInterceptPoint(point));
break;
case 'listen_event':
out.listen_event = out.listen_event || [];
out.listen_event.push(reverseListenEventPoint(point));
break;
case 'component':
if (point.component_type === 'schedule') {
out.component = out.component || [];
out.component.push(reverseComponentSchedulePoint(point));
}
break;
case 'customField':
out.customField = out.customField || [];
out.customField.push(reverseCustomFieldPoint(point));
break;
case 'liteAppComponent':
out.liteAppComponent = out.liteAppComponent || [];
out.liteAppComponent.push(reverseLiteAppComponentPoint(point));
break;
// aiNode / aiField 是单对象——AI 插件全工程恰好一个。后端意外回多条同类型的话
// 取首条 + warn,已有的不覆盖,避免悄悄丢数据。
case 'aiNode':
if (out.aiNode === undefined) {
out.aiNode = reverseAINodePoint(point);
}
else {
logger_1.logger.warn(`Multiple ai_node points received from backend; keeping "${out.aiNode.key}" and dropping "${point.key || mapKey}".`);
}
break;
case 'aiField':
if (out.aiField === undefined) {
out.aiField = reverseAIFieldPoint(point);
}
else {
logger_1.logger.warn(`Multiple ai_field points received from backend; keeping "${out.aiField.key}" and dropping "${point.key || mapKey}".`);
}
break;
default:
break;
}
}
return out;
}
exports.reverseTransformQueryLocalConfig = reverseTransformQueryLocalConfig;