UNPKG

@lark-project/cli

Version:

飞书项目插件开发工具

694 lines (693 loc) 30.5 kB
"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;