@tuoyuan/gateway-api-select-config 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,147 @@
1
+ /**
2
+ * 入参配置工具函数
3
+ */
4
+
5
+ /**
6
+ * 解析入参配置
7
+ * @param {String} text - JSON 文本
8
+ * @param {String} mode - 模式:fixed/template/function
9
+ * @returns {Object} 解析后的配置
10
+ */
11
+ export function parseReqMapping(text, mode) {
12
+ if (!text || text.trim() === '') {
13
+ return {};
14
+ }
15
+
16
+ try {
17
+ let jsonText = text;
18
+
19
+ // 如果是模板模式,需要处理不带引号的模板变量
20
+ if (mode === 'template') {
21
+ // 临时替换:${var} -> "__TEMPLATE_var__"
22
+ const templateMap = new Map();
23
+ let counter = 0;
24
+
25
+ jsonText = text.replace(/\$\{([^}]+)\}/g, (match, varName) => {
26
+ const placeholder = `"__TEMPLATE_${counter}__"`;
27
+ templateMap.set(placeholder, match);
28
+ counter++;
29
+ return placeholder;
30
+ });
31
+
32
+ // 解析 JSON
33
+ const parsed = JSON.parse(jsonText);
34
+
35
+ // 还原模板变量
36
+ const restore = (obj) => {
37
+ if (typeof obj === 'string') {
38
+ // 检查是否是占位符
39
+ for (const [placeholder, original] of templateMap.entries()) {
40
+ if (obj === placeholder.replace(/"/g, '')) {
41
+ return original;
42
+ }
43
+ }
44
+ return obj;
45
+ } else if (typeof obj === 'object' && obj !== null) {
46
+ if (Array.isArray(obj)) {
47
+ return obj.map(restore);
48
+ } else {
49
+ const result = {};
50
+ for (const [key, value] of Object.entries(obj)) {
51
+ result[key] = restore(value);
52
+ }
53
+ return result;
54
+ }
55
+ }
56
+ return obj;
57
+ };
58
+
59
+ return restore(parsed);
60
+ }
61
+
62
+ // 其他模式直接解析
63
+ const parsed = JSON.parse(jsonText);
64
+
65
+ // 验证格式
66
+ if (typeof parsed !== 'object' || Array.isArray(parsed)) {
67
+ throw new Error('入参配置必须是对象格式');
68
+ }
69
+
70
+ return parsed;
71
+ } catch (err) {
72
+ throw new Error('JSON 格式错误:' + err.message);
73
+ }
74
+ }
75
+
76
+ /**
77
+ * 验证入参配置
78
+ * @param {Object} mapping - 入参配置对象
79
+ * @param {String} mode - 模式
80
+ * @returns {Object} { valid: Boolean, error: String }
81
+ */
82
+ export function validateReqMapping(mapping, mode) {
83
+ if (!mapping || Object.keys(mapping).length === 0) {
84
+ return { valid: true, error: '' };
85
+ }
86
+
87
+ try {
88
+ for (const [key, value] of Object.entries(mapping)) {
89
+ if (mode === 'template') {
90
+ // 模板字符串模式:检查是否包含 ${...}
91
+ if (typeof value === 'string' && !value.includes('${')) {
92
+ // 允许纯字符串,但给出提示
93
+ console.warn(`字段 "${key}" 的值不包含模板变量,建议使用固定值模式`);
94
+ }
95
+ } else if (mode === 'function') {
96
+ // 函数模式:检查是否包含箭头函数
97
+ if (typeof value === 'string' && !value.includes('=>')) {
98
+ return {
99
+ valid: false,
100
+ error: `字段 "${key}" 的值不是有效的函数表达式`
101
+ };
102
+ }
103
+ }
104
+ }
105
+
106
+ return { valid: true, error: '' };
107
+ } catch (err) {
108
+ return { valid: false, error: err.message };
109
+ }
110
+ }
111
+
112
+ /**
113
+ * 格式化入参配置(用于显示)
114
+ * @param {Object} mapping - 入参配置
115
+ * @returns {String} 格式化后的 JSON 字符串
116
+ */
117
+ export function formatReqMapping(mapping) {
118
+ if (!mapping) return '{}';
119
+ return JSON.stringify(mapping, null, 2);
120
+ }
121
+
122
+ /**
123
+ * 检测入参配置的模式
124
+ * @param {Object} mapping - 入参配置
125
+ * @returns {String} 模式:fixed/template/function
126
+ */
127
+ export function detectReqMappingMode(mapping) {
128
+ if (!mapping || Object.keys(mapping).length === 0) {
129
+ return 'fixed';
130
+ }
131
+
132
+ const values = Object.values(mapping);
133
+
134
+ // 检查是否有函数表达式
135
+ const hasFunction = values.some(v =>
136
+ typeof v === 'string' && v.includes('=>')
137
+ );
138
+ if (hasFunction) return 'function';
139
+
140
+ // 检查是否有模板变量
141
+ const hasTemplate = values.some(v =>
142
+ typeof v === 'string' && v.includes('${')
143
+ );
144
+ if (hasTemplate) return 'template';
145
+
146
+ return 'fixed';
147
+ }
@@ -0,0 +1,197 @@
1
+ /**
2
+ * Schema 工具函数
3
+ */
4
+
5
+ /**
6
+ * 从 schema 中提取所有字段
7
+ * @param {Object} schema - JSON Schema
8
+ * @param {String} prefix - 字段前缀
9
+ * @param {Number} level - 嵌套层级
10
+ * @returns {Array} 字段列表
11
+ */
12
+ export function extractSchemaFields(schema, prefix = '', level = 0) {
13
+ const fields = [];
14
+
15
+ if (!schema) return fields;
16
+
17
+ // 处理新格式:type 是 object 但有 items(包装的数组)
18
+ if (schema.type === 'object' && schema.items && !schema.properties) {
19
+ // 当作数组处理
20
+ if (schema.items.type === 'object' && schema.items.properties) {
21
+ const itemFields = extractSchemaFields(schema.items, prefix, level);
22
+ fields.push(...itemFields);
23
+ }
24
+ return fields;
25
+ }
26
+
27
+ // 处理数组类型
28
+ if (schema.type === 'array' && schema.items) {
29
+ // 递归处理数组元素(不添加 [*] 前缀,因为这是顶层数组)
30
+ if (schema.items.type === 'object' && schema.items.properties) {
31
+ const itemFields = extractSchemaFields(schema.items, prefix, level);
32
+ fields.push(...itemFields);
33
+ }
34
+
35
+ return fields;
36
+ }
37
+
38
+ // 处理对象类型
39
+ if (schema.type === 'object' && schema.properties) {
40
+ // 获取必填字段列表
41
+ const requiredFields = schema.required || [];
42
+
43
+ for (const [fieldName, fieldSchema] of Object.entries(schema.properties)) {
44
+ const fieldKey = prefix ? `${prefix}.${fieldName}` : fieldName;
45
+
46
+ // 如果是嵌套对象或数组,递归处理
47
+ if (fieldSchema.type === 'object' || fieldSchema.type === 'array') {
48
+ const nestedFields = extractSchemaFields(fieldSchema, fieldKey, level + 1);
49
+ fields.push(...nestedFields);
50
+ } else {
51
+ // 基础类型字段
52
+ fields.push({
53
+ key: fieldKey,
54
+ name: fieldName,
55
+ label: fieldSchema.label || fieldSchema.title || fieldName,
56
+ type: fieldSchema.type,
57
+ description: fieldSchema.description || '',
58
+ level: level,
59
+ required: requiredFields.includes(fieldName) // 从父对象的 required 数组中判断
60
+ });
61
+ }
62
+ }
63
+ }
64
+
65
+ return fields;
66
+ }
67
+
68
+ /**
69
+ * 构建带路径的 res_schema
70
+ * @param {Object} schema - 原始 schema
71
+ * @param {Object} fieldPaths - 字段路径映射
72
+ * @param {Object} fieldFormats - 字段格式化映射
73
+ * @returns {Object} 带路径的 schema
74
+ */
75
+ export function buildResSchema(schema, fieldPaths, fieldFormats = {}) {
76
+ if (!schema) return null;
77
+
78
+ const result = { ...schema };
79
+
80
+ // 移除 required 数组(对于列表展示场景通常不需要)
81
+ delete result.required;
82
+
83
+ // 处理数组类型
84
+ if (schema.type === 'array' && schema.items) {
85
+ result.items = buildResSchema(schema.items, fieldPaths, fieldFormats);
86
+ return result;
87
+ }
88
+
89
+ // 处理对象类型
90
+ if (schema.type === 'object' && schema.properties) {
91
+ result.properties = {};
92
+
93
+ for (const [fieldName, fieldSchema] of Object.entries(schema.properties)) {
94
+ const fieldKey = fieldName;
95
+ const newFieldSchema = { ...fieldSchema };
96
+
97
+ // 如果是嵌套结构,递归处理
98
+ if (fieldSchema.type === 'object' || fieldSchema.type === 'array') {
99
+ result.properties[fieldName] = buildResSchema(fieldSchema, fieldPaths, fieldFormats);
100
+ } else {
101
+ // 基础类型,添加 path 和 format
102
+ const path = fieldPaths[fieldKey];
103
+ if (path) {
104
+ newFieldSchema.x_path = path;
105
+ }
106
+
107
+ const format = fieldFormats[fieldKey];
108
+ if (format) {
109
+ newFieldSchema.format = format;
110
+ }
111
+
112
+ result.properties[fieldName] = newFieldSchema;
113
+ }
114
+ }
115
+ }
116
+
117
+ return result;
118
+ }
119
+
120
+
121
+
122
+ /**
123
+ * 从 res_schema 中提取实际会输出的字段(用于显示)
124
+ * @param {Object} schema - res_schema
125
+ * @returns {Object} 格式化后的字段信息
126
+ */
127
+ export function extractOutputFields(schema) {
128
+ if (!schema) return {};
129
+
130
+ // 检查 schema 是否不完整
131
+ const checkIncomplete = (s) => {
132
+ if (s?.items?.properties) {
133
+ return Object.keys(s.items.properties).length === 0;
134
+ }
135
+ if (s?.properties) {
136
+ return Object.keys(s.properties).length === 0;
137
+ }
138
+ return false;
139
+ };
140
+
141
+ if (checkIncomplete(schema)) {
142
+ return {
143
+ 模式: '自动推断',
144
+ 说明: 'Schema 不完整,将返回完整的原始数据',
145
+ 数据路径: schema.x_path || schema.x_value || '@'
146
+ };
147
+ }
148
+
149
+ // 提取实际字段配置
150
+ const fields = {};
151
+
152
+ // 处理数组类型
153
+ if (schema.items?.properties) {
154
+ const props = schema.items.properties;
155
+ for (const [key, value] of Object.entries(props)) {
156
+ fields[key] = {
157
+ 类型: value.type,
158
+ 路径: value.x_path,
159
+ 格式: value.format || '无'
160
+ };
161
+ }
162
+ }
163
+ // 处理对象类型
164
+ else if (schema.properties) {
165
+ const props = schema.properties;
166
+ for (const [key, value] of Object.entries(props)) {
167
+ // 跳过嵌套对象,只显示基础字段
168
+ if (value.type !== 'object' && value.type !== 'array') {
169
+ fields[key] = {
170
+ 类型: value.type,
171
+ 路径: value.x_path,
172
+ 格式: value.format || '无'
173
+ };
174
+ }
175
+ }
176
+ }
177
+
178
+ return {
179
+ 数据路径: schema.x_path || schema.x_value || '@',
180
+ 输出字段: fields
181
+ };
182
+ }
183
+
184
+ /**
185
+ * 格式化配置显示
186
+ * @param {Object} config - 配置对象
187
+ * @returns {Object} 格式化后的配置
188
+ */
189
+ export function formatConfig(config) {
190
+ if (!config) return null;
191
+
192
+ return {
193
+ 接口路径: config.apiPath,
194
+ 入参配置: config.reqMapping || {},
195
+ 输出配置: extractOutputFields(config.res_schema)
196
+ };
197
+ }
@@ -0,0 +1,134 @@
1
+ /**
2
+ * 树形结构工具函数
3
+ */
4
+
5
+ /**
6
+ * 过滤 API 树(根据搜索关键词)
7
+ * @param {Array} tree - API 树
8
+ * @param {String} keyword - 搜索关键词
9
+ * @returns {Array} 过滤后的树
10
+ */
11
+ export function filterApiTree(tree, keyword) {
12
+ if (!keyword) return tree;
13
+
14
+ const lowerKeyword = keyword.toLowerCase();
15
+
16
+ const filterNode = (node) => {
17
+ // 检查当前节点是否匹配
18
+ const nameMatch = node.name?.toLowerCase().includes(lowerKeyword);
19
+ const pathMatch = node.apiPath?.toLowerCase().includes(lowerKeyword);
20
+ const isMatch = nameMatch || pathMatch;
21
+
22
+ // 判断是否是叶子节点
23
+ const isLeaf = node.isLeaf || node.apiPath || !node.children || node.children.length === 0;
24
+
25
+ // 如果是叶子节点
26
+ if (isLeaf) {
27
+ return isMatch ? { ...node, highlighted: true } : null;
28
+ }
29
+
30
+ // 如果是分类节点,递归过滤子节点
31
+ if (node.children && node.children.length > 0) {
32
+ const filteredChildren = node.children
33
+ .map(child => filterNode(child))
34
+ .filter(child => child !== null);
35
+
36
+ // 如果当前节点名称匹配,保留所有原始子节点(不过滤)
37
+ if (isMatch) {
38
+ return {
39
+ ...node,
40
+ children: node.children, // 保留所有子节点
41
+ expanded: true,
42
+ highlighted: true
43
+ };
44
+ }
45
+
46
+ // 如果有匹配的子节点,则保留(当前节点不匹配,但子节点匹配)
47
+ if (filteredChildren.length > 0) {
48
+ return {
49
+ ...node,
50
+ children: filteredChildren,
51
+ expanded: true
52
+ };
53
+ }
54
+ }
55
+
56
+ return null;
57
+ };
58
+
59
+ return tree.map(node => filterNode(node)).filter(node => node !== null);
60
+ }
61
+
62
+ /**
63
+ * 检查路径是否匹配模式(支持通配符 *)
64
+ * @param {String} path - API 路径
65
+ * @param {String} pattern - 匹配模式
66
+ * @returns {Boolean}
67
+ */
68
+ export function matchesPattern(path, pattern) {
69
+ if (!path || !pattern) return false;
70
+
71
+ // 将模式转换为正则表达式
72
+ // 例如:*.get_list -> ^.*\.get_list$
73
+ // 例如:system.* -> ^system\..*$
74
+ const regexPattern = pattern
75
+ .replace(/\./g, '\\.') // 转义点号
76
+ .replace(/\*/g, '.*'); // 通配符转为正则
77
+
78
+ const regex = new RegExp(`^${regexPattern}$`);
79
+ return regex.test(path);
80
+ }
81
+
82
+ /**
83
+ * 在树中查找节点
84
+ * @param {Array} tree - API 树
85
+ * @param {String} apiPath - API 路径
86
+ * @returns {Object|null} 找到的节点
87
+ */
88
+ export function findNodeByPath(tree, apiPath) {
89
+ for (const node of tree) {
90
+ if (node.apiPath === apiPath) {
91
+ return node;
92
+ }
93
+
94
+ if (node.children && node.children.length > 0) {
95
+ const found = findNodeByPath(node.children, apiPath);
96
+ if (found) return found;
97
+ }
98
+ }
99
+
100
+ return null;
101
+ }
102
+
103
+ /**
104
+ * 展开到指定节点的路径
105
+ * @param {Array} tree - API 树
106
+ * @param {String} targetId - 目标节点 ID
107
+ * @returns {Boolean} 是否找到并展开
108
+ */
109
+ export function expandToNode(tree, targetId) {
110
+ const expandNode = (node) => {
111
+ if (node.id === targetId) {
112
+ return true;
113
+ }
114
+
115
+ if (node.children && node.children.length > 0) {
116
+ for (const child of node.children) {
117
+ if (expandNode(child)) {
118
+ node.expanded = true;
119
+ return true;
120
+ }
121
+ }
122
+ }
123
+
124
+ return false;
125
+ };
126
+
127
+ for (const node of tree) {
128
+ if (expandNode(node)) {
129
+ return true;
130
+ }
131
+ }
132
+
133
+ return false;
134
+ }