openyida 2026.6.1-beta.1 → 2026.6.3-beta.0
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.
- package/lib/app/import-app.js +21 -9
- package/lib/connector/action-generator.js +40 -2
- package/lib/connector/api.js +7 -1
- package/lib/connector/connector-add-action.js +4 -2
- package/lib/connector/connector-create.js +4 -2
- package/lib/connector/connector-delete-action.js +2 -1
- package/lib/connector/curl-parser.js +39 -8
- package/lib/connector/doc-parser.js +30 -19
- package/lib/connector/operation-normalizer.js +198 -0
- package/lib/core/chalk.js +2 -0
- package/lib/core/env-cmd.js +9 -9
- package/lib/core/update.js +9 -9
- package/lib/dws/dws-wrapper.js +15 -15
- package/package.json +1 -1
package/lib/app/import-app.js
CHANGED
|
@@ -139,16 +139,28 @@ async function updateFormConfig(appType, formUuid, authRef) {
|
|
|
139
139
|
return result;
|
|
140
140
|
}
|
|
141
141
|
|
|
142
|
-
// ── 适配
|
|
142
|
+
// ── 适配 Schema 中的应用 / 表单标识符 ─────────────────
|
|
143
143
|
//
|
|
144
|
-
//
|
|
145
|
-
//
|
|
144
|
+
// 导入时需要把 Schema 里所有对旧 appType / formUuid 的引用(页面 id、组件
|
|
145
|
+
// props.appType、actions.source、SerialNumberField 公式等)重映射为新值。
|
|
146
|
+
//
|
|
147
|
+
// 早期实现对整个 Schema JSON 做无约束的全局字符串替换,存在数据腐蚀风险:
|
|
148
|
+
// 若某个字段的文本值、label、备注恰好包含旧 appType / formUuid 子串,会被
|
|
149
|
+
// 误替换。这里改为「标识符边界」约束替换——appType / formUuid 都是由字母、
|
|
150
|
+
// 数字、下划线、连字符构成的标识符,只有当匹配片段两侧不是同类标识符字符时
|
|
151
|
+
// 才替换,从而避免误伤包含旧标识符子串的普通文本。
|
|
152
|
+
|
|
153
|
+
function buildIdentifierBoundaryRegExp(identifier) {
|
|
154
|
+
// 标识符字符集:字母、数字、下划线、连字符。
|
|
155
|
+
// 使用 lookbehind / lookahead 保证匹配片段不是更长标识符的一部分。
|
|
156
|
+
return new RegExp(`(?<![A-Za-z0-9_-])${escapeRegExp(identifier)}(?![A-Za-z0-9_-])`, 'g');
|
|
157
|
+
}
|
|
146
158
|
|
|
147
|
-
function
|
|
159
|
+
function adaptSchemaIdentifiers(schema, oldAppType, newAppType, oldFormUuid, newFormUuid) {
|
|
148
160
|
const schemaStr = JSON.stringify(schema);
|
|
149
161
|
const adapted = schemaStr
|
|
150
|
-
.replace(
|
|
151
|
-
.replace(
|
|
162
|
+
.replace(buildIdentifierBoundaryRegExp(oldAppType), newAppType)
|
|
163
|
+
.replace(buildIdentifierBoundaryRegExp(oldFormUuid), newFormUuid);
|
|
152
164
|
return JSON.parse(adapted);
|
|
153
165
|
}
|
|
154
166
|
|
|
@@ -287,8 +299,8 @@ async function run(args) {
|
|
|
287
299
|
continue;
|
|
288
300
|
}
|
|
289
301
|
|
|
290
|
-
// 将 Schema 中所有旧 appType / formUuid
|
|
291
|
-
const adaptedSchema =
|
|
302
|
+
// 将 Schema 中所有旧 appType / formUuid 引用重映射为新值(标识符边界约束,避免误伤普通文本)
|
|
303
|
+
const adaptedSchema = adaptSchemaIdentifiers(
|
|
292
304
|
originalSchema,
|
|
293
305
|
sourceAppType,
|
|
294
306
|
newAppType,
|
|
@@ -377,7 +389,7 @@ module.exports = {
|
|
|
377
389
|
__test__: {
|
|
378
390
|
normalizeImportFormType,
|
|
379
391
|
buildCreateFormPostData,
|
|
380
|
-
|
|
392
|
+
adaptSchemaIdentifiers,
|
|
381
393
|
extractSchemaContent,
|
|
382
394
|
},
|
|
383
395
|
};
|
|
@@ -2,6 +2,42 @@
|
|
|
2
2
|
* 执行动作配置生成模块
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
// 可能携带凭证 / 敏感信息的请求头(小写匹配)。
|
|
6
|
+
// 这些头的真实值绝不能写入生成的 Action 配置,否则会把用户 token 泄漏到
|
|
7
|
+
// 落盘的连接器配置里。命中时用占位符替换,提示用户在连接器认证中配置。
|
|
8
|
+
const SENSITIVE_HEADER_PATTERNS = [
|
|
9
|
+
'authorization',
|
|
10
|
+
'cookie',
|
|
11
|
+
'token',
|
|
12
|
+
'api-key',
|
|
13
|
+
'apikey',
|
|
14
|
+
'secret',
|
|
15
|
+
'access-token',
|
|
16
|
+
'x-acs-dingtalk-access-token',
|
|
17
|
+
'x-api-key',
|
|
18
|
+
'proxy-authorization',
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
function isSensitiveHeader(headerName) {
|
|
22
|
+
const lower = String(headerName || '').toLowerCase();
|
|
23
|
+
return SENSITIVE_HEADER_PATTERNS.some((pattern) => lower.includes(pattern));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* 计算请求头写入 Action 配置的默认值。
|
|
28
|
+
* 敏感头返回占位符(不泄漏真实凭证),普通头截断展示前 50 字符。
|
|
29
|
+
* @param {string} headerName - 请求头名称
|
|
30
|
+
* @param {string} headerValue - 请求头原始值
|
|
31
|
+
* @returns {string} 可安全写入配置的默认值
|
|
32
|
+
*/
|
|
33
|
+
function buildHeaderDefaultValue(headerName, headerValue) {
|
|
34
|
+
if (isSensitiveHeader(headerName)) {
|
|
35
|
+
return '';
|
|
36
|
+
}
|
|
37
|
+
const value = String(headerValue === null || headerValue === undefined ? '' : headerValue);
|
|
38
|
+
return value.length > 50 ? value.substring(0, 50) + '...' : value;
|
|
39
|
+
}
|
|
40
|
+
|
|
5
41
|
/**
|
|
6
42
|
* 根据URL路径生成有意义的动作名称和描述
|
|
7
43
|
* @param {string} path - URL路径
|
|
@@ -125,7 +161,7 @@ function generateOperation(curlData, relevantHeaders) {
|
|
|
125
161
|
paramType: 'String',
|
|
126
162
|
desc: key,
|
|
127
163
|
required: false,
|
|
128
|
-
defaultValue:
|
|
164
|
+
defaultValue: buildHeaderDefaultValue(key, value),
|
|
129
165
|
children: [],
|
|
130
166
|
childList: [],
|
|
131
167
|
__level: 0,
|
|
@@ -262,5 +298,7 @@ function generateOperation(curlData, relevantHeaders) {
|
|
|
262
298
|
|
|
263
299
|
module.exports = {
|
|
264
300
|
generateActionInfo,
|
|
265
|
-
generateOperation
|
|
301
|
+
generateOperation,
|
|
302
|
+
isSensitiveHeader,
|
|
303
|
+
buildHeaderDefaultValue
|
|
266
304
|
};
|
package/lib/connector/api.js
CHANGED
|
@@ -60,7 +60,13 @@ function printTable(headers, rows) {
|
|
|
60
60
|
function buildOperationsSummary(operations) {
|
|
61
61
|
if (!Array.isArray(operations) || operations.length === 0) {return '';}
|
|
62
62
|
|
|
63
|
-
const summaries = operations
|
|
63
|
+
const summaries = operations
|
|
64
|
+
.map(op => op.summary || op.name || op.operationId || op.id || op.url || op.path)
|
|
65
|
+
.filter(Boolean);
|
|
66
|
+
|
|
67
|
+
if (summaries.length === 0) {
|
|
68
|
+
return '';
|
|
69
|
+
}
|
|
64
70
|
|
|
65
71
|
if (summaries.length === 1) {
|
|
66
72
|
return `支持${summaries[0]}`;
|
|
@@ -23,6 +23,7 @@ const {
|
|
|
23
23
|
getConnectorDetail,
|
|
24
24
|
saveConnector,
|
|
25
25
|
} = require('./api');
|
|
26
|
+
const { normalizeOperations } = require('./operation-normalizer');
|
|
26
27
|
|
|
27
28
|
function showUsage() {
|
|
28
29
|
console.log(`
|
|
@@ -99,6 +100,7 @@ async function run(args) {
|
|
|
99
100
|
if (!Array.isArray(newOperations)) {
|
|
100
101
|
newOperations = [newOperations];
|
|
101
102
|
}
|
|
103
|
+
newOperations = normalizeOperations(newOperations);
|
|
102
104
|
console.log(`✓ 已加载 ${newOperations.length} 个执行动作`);
|
|
103
105
|
newOperations.forEach((operation, index) => {
|
|
104
106
|
console.log(` [${index + 1}] ${operation.operationId}: ${operation.summary}`);
|
|
@@ -123,7 +125,7 @@ async function run(args) {
|
|
|
123
125
|
const detail = await getConnectorDetail(connector.connectorName, authRef);
|
|
124
126
|
targetConnector = { ...connector, detail };
|
|
125
127
|
|
|
126
|
-
const existingOps = JSON.parse(targetConnector.detail.operations || '[]');
|
|
128
|
+
const existingOps = normalizeOperations(JSON.parse(targetConnector.detail.operations || '[]'));
|
|
127
129
|
console.log('📋 即将追加到以下连接器:');
|
|
128
130
|
console.log(` 名称: ${targetConnector.displayName}`);
|
|
129
131
|
console.log(` ID: ${targetConnector.id}`);
|
|
@@ -164,7 +166,7 @@ async function run(args) {
|
|
|
164
166
|
}
|
|
165
167
|
|
|
166
168
|
// 合并执行动作
|
|
167
|
-
const existingOperations = JSON.parse(targetConnector.detail.operations || '[]');
|
|
169
|
+
const existingOperations = normalizeOperations(JSON.parse(targetConnector.detail.operations || '[]'));
|
|
168
170
|
const existingIds = new Set(existingOperations.map(op => op.operationId));
|
|
169
171
|
const duplicates = newOperations.filter(op => existingIds.has(op.operationId));
|
|
170
172
|
|
|
@@ -31,6 +31,7 @@ const {
|
|
|
31
31
|
getConnectorDetail,
|
|
32
32
|
saveConnector,
|
|
33
33
|
} = require('./api');
|
|
34
|
+
const { normalizeOperations } = require('./operation-normalizer');
|
|
34
35
|
|
|
35
36
|
function showUsage() {
|
|
36
37
|
console.log(`
|
|
@@ -232,11 +233,11 @@ async function run(args) {
|
|
|
232
233
|
console.log(`当前描述: ${connector.connectorDesc || '(空)'}\n`);
|
|
233
234
|
|
|
234
235
|
const detail = await getConnectorDetail(connector.connectorName, authRef);
|
|
235
|
-
const currentOperations = JSON.parse(detail.operations || '[]');
|
|
236
|
+
const currentOperations = normalizeOperations(JSON.parse(detail.operations || '[]'));
|
|
236
237
|
const newDesc = buildConnectorDesc(options.description, connector.connectorDesc, authRef, currentOperations);
|
|
237
238
|
|
|
238
239
|
await saveConnector({
|
|
239
|
-
operations:
|
|
240
|
+
operations: JSON.stringify(currentOperations),
|
|
240
241
|
displayName: detail.displayName,
|
|
241
242
|
iconUrl: detail.iconUrl || 'chaxun%%#FFA200',
|
|
242
243
|
connectorDesc: newDesc,
|
|
@@ -278,6 +279,7 @@ async function run(args) {
|
|
|
278
279
|
fail('错误: operations 文件不能为空数组');
|
|
279
280
|
process.exit(1);
|
|
280
281
|
}
|
|
282
|
+
operations = normalizeOperations(operations);
|
|
281
283
|
console.log(`✓ 已加载 ${operations.length} 个执行动作`);
|
|
282
284
|
} catch (e) {
|
|
283
285
|
fail(`读取执行动作配置文件失败: ${e.message}`);
|
|
@@ -17,6 +17,7 @@ const {
|
|
|
17
17
|
getConnectorDetail,
|
|
18
18
|
saveConnector,
|
|
19
19
|
} = require('./api');
|
|
20
|
+
const { normalizeOperations } = require('./operation-normalizer');
|
|
20
21
|
|
|
21
22
|
function showUsage() {
|
|
22
23
|
console.log(`
|
|
@@ -63,7 +64,7 @@ async function run(args) {
|
|
|
63
64
|
}
|
|
64
65
|
|
|
65
66
|
const detail = await getConnectorDetail(connector.connectorName, authRef);
|
|
66
|
-
const currentOperations = JSON.parse(detail.operations || '[]');
|
|
67
|
+
const currentOperations = normalizeOperations(JSON.parse(detail.operations || '[]'));
|
|
67
68
|
const targetOperation = currentOperations.find(op => op.operationId === operationId);
|
|
68
69
|
|
|
69
70
|
if (!targetOperation) {
|
|
@@ -2,6 +2,36 @@
|
|
|
2
2
|
* Curl 命令解析模块
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* 从 curl 命令中提取 URL。
|
|
7
|
+
* 早期实现只匹配 `curl "<url>"`(必须带引号且紧跟 curl),导致裸 URL、
|
|
8
|
+
* 单引号、`--url` 参数、`-X POST` 在 URL 之前等常见写法全部解析失败。
|
|
9
|
+
* 这里按优先级多策略提取:
|
|
10
|
+
* 1. 显式 `--url <url>` 参数
|
|
11
|
+
* 2. 命令中任意带引号的 http(s) URL
|
|
12
|
+
* 3. 命令中任意裸 http(s) URL(截断到空白处)
|
|
13
|
+
* @param {string} curlCommand - curl 命令字符串
|
|
14
|
+
* @returns {string} 提取到的 URL,未找到返回空字符串
|
|
15
|
+
*/
|
|
16
|
+
function extractCurlUrl(curlCommand) {
|
|
17
|
+
const explicitUrl = curlCommand.match(/--url\s+['"]?([^'"\s]+)['"]?/);
|
|
18
|
+
if (explicitUrl) {
|
|
19
|
+
return explicitUrl[1];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const quotedUrl = curlCommand.match(/['"](https?:\/\/[^'"]+)['"]/);
|
|
23
|
+
if (quotedUrl) {
|
|
24
|
+
return quotedUrl[1];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const bareUrl = curlCommand.match(/(https?:\/\/[^\s'"]+)/);
|
|
28
|
+
if (bareUrl) {
|
|
29
|
+
return bareUrl[1];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return '';
|
|
33
|
+
}
|
|
34
|
+
|
|
5
35
|
/**
|
|
6
36
|
* 解析 curl 命令
|
|
7
37
|
* @param {string} curlCommand - curl 命令字符串
|
|
@@ -19,10 +49,9 @@ function parseCurl(curlCommand) {
|
|
|
19
49
|
};
|
|
20
50
|
|
|
21
51
|
try {
|
|
22
|
-
// 提取 URL
|
|
23
|
-
|
|
24
|
-
if (
|
|
25
|
-
result.url = urlMatch[1];
|
|
52
|
+
// 提取 URL:兼容带引号 URL、裸 URL、--url 参数、-X POST 在前等多种写法
|
|
53
|
+
result.url = extractCurlUrl(curlCommand);
|
|
54
|
+
if (result.url) {
|
|
26
55
|
const url = new URL(result.url);
|
|
27
56
|
result.protocol = url.protocol.replace(':', '');
|
|
28
57
|
result.host = url.hostname;
|
|
@@ -30,10 +59,10 @@ function parseCurl(curlCommand) {
|
|
|
30
59
|
}
|
|
31
60
|
|
|
32
61
|
// 提取方法
|
|
33
|
-
const methodMatch = curlCommand.match(
|
|
62
|
+
const methodMatch = curlCommand.match(/(?:-X|--request)\s+['"]?(\w+)['"]?/);
|
|
34
63
|
if (methodMatch) {
|
|
35
64
|
result.method = methodMatch[1].toUpperCase();
|
|
36
|
-
} else if (curlCommand.includes('--data') ||
|
|
65
|
+
} else if (curlCommand.includes('--data') || /\s-d\b/.test(curlCommand)) {
|
|
37
66
|
result.method = 'POST';
|
|
38
67
|
}
|
|
39
68
|
|
|
@@ -64,10 +93,11 @@ function detectAuthType(headers) {
|
|
|
64
93
|
const authHeader = headers['Authorization'] || headers['authorization'];
|
|
65
94
|
|
|
66
95
|
if (authHeader) {
|
|
67
|
-
|
|
96
|
+
const scheme = authHeader.trim().toLowerCase();
|
|
97
|
+
if (scheme.startsWith('bearer')) {
|
|
68
98
|
return { type: 'API密钥', code: 'ApiKeyAuth', headerName: 'Authorization' };
|
|
69
99
|
}
|
|
70
|
-
if (
|
|
100
|
+
if (scheme.startsWith('basic')) {
|
|
71
101
|
return { type: '基本身份验证', code: 'BasicAuth', headerName: 'Authorization' };
|
|
72
102
|
}
|
|
73
103
|
}
|
|
@@ -116,6 +146,7 @@ function filterBrowserHeaders(headers) {
|
|
|
116
146
|
|
|
117
147
|
module.exports = {
|
|
118
148
|
parseCurl,
|
|
149
|
+
extractCurlUrl,
|
|
119
150
|
detectAuthType,
|
|
120
151
|
filterBrowserHeaders,
|
|
121
152
|
BROWSER_HEADERS
|
|
@@ -104,21 +104,22 @@ class MarkdownParser extends BaseDocParser {
|
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
parseRequestHeaders() {
|
|
107
|
-
const headerSection = this.extractSection('请求头', '请求参数');
|
|
107
|
+
const headerSection = this.extractSection('请求头', ['查询参数', '请求参数', '请求体', '响应']);
|
|
108
108
|
if (headerSection) {
|
|
109
109
|
this.result.requestInfo.headers = this.parseTable(headerSection);
|
|
110
110
|
}
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
parseQueryParams() {
|
|
114
|
-
const querySection = this.extractSection('查询参数', '请求体')
|
|
114
|
+
const querySection = this.extractSection('查询参数', ['请求体', '响应', '错误码']) ||
|
|
115
|
+
this.extractSection('请求参数', ['请求体', '响应', '错误码']);
|
|
115
116
|
if (querySection) {
|
|
116
117
|
this.result.requestInfo.query = this.parseTable(querySection);
|
|
117
118
|
}
|
|
118
119
|
}
|
|
119
120
|
|
|
120
121
|
parseRequestBody() {
|
|
121
|
-
const bodySection = this.extractSection('请求体', '响应');
|
|
122
|
+
const bodySection = this.extractSection('请求体', ['响应', '返回', '错误码']);
|
|
122
123
|
if (!bodySection) {return;}
|
|
123
124
|
|
|
124
125
|
const jsonMatch = bodySection.match(/```json\s*([\s\S]*?)```/);
|
|
@@ -289,10 +290,13 @@ class MarkdownParser extends BaseDocParser {
|
|
|
289
290
|
|
|
290
291
|
let endIndex = this.content.length;
|
|
291
292
|
if (endMarker) {
|
|
292
|
-
const
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
293
|
+
const markers = Array.isArray(endMarker) ? endMarker : [endMarker];
|
|
294
|
+
markers.forEach(marker => {
|
|
295
|
+
const endMatch = this.content.indexOf(marker, startIndex + startMarker.length);
|
|
296
|
+
if (endMatch !== -1 && endMatch < endIndex) {
|
|
297
|
+
endIndex = endMatch;
|
|
298
|
+
}
|
|
299
|
+
});
|
|
296
300
|
}
|
|
297
301
|
|
|
298
302
|
return this.content.substring(startIndex, endIndex);
|
|
@@ -343,6 +347,15 @@ class MarkdownParser extends BaseDocParser {
|
|
|
343
347
|
}
|
|
344
348
|
}
|
|
345
349
|
|
|
350
|
+
function applyParamLocation(nodes, paramLocation) {
|
|
351
|
+
(Array.isArray(nodes) ? nodes : []).forEach(node => {
|
|
352
|
+
node.paramLocation = paramLocation;
|
|
353
|
+
applyParamLocation(node.childList, paramLocation);
|
|
354
|
+
applyParamLocation(node.children, paramLocation);
|
|
355
|
+
});
|
|
356
|
+
return nodes;
|
|
357
|
+
}
|
|
358
|
+
|
|
346
359
|
/**
|
|
347
360
|
* 文档解析器工厂
|
|
348
361
|
*/
|
|
@@ -424,6 +437,7 @@ function convertToOperationConfig(parseResult) {
|
|
|
424
437
|
childList: [],
|
|
425
438
|
__level: 0,
|
|
426
439
|
hidden: false,
|
|
440
|
+
paramLocation: 'header',
|
|
427
441
|
required: h.required === 'true' || h.required === '是' || h.required === '**' || h.required === true,
|
|
428
442
|
defaultValue: h.example || h.value || ''
|
|
429
443
|
}));
|
|
@@ -433,7 +447,8 @@ function convertToOperationConfig(parseResult) {
|
|
|
433
447
|
desc: '请求头',
|
|
434
448
|
name: 'Headers',
|
|
435
449
|
paramType: 'Object',
|
|
436
|
-
required: false
|
|
450
|
+
required: false,
|
|
451
|
+
paramLocation: 'header'
|
|
437
452
|
});
|
|
438
453
|
|
|
439
454
|
operation.parameters.header = parseResult.requestInfo.headers.map(h => ({
|
|
@@ -452,7 +467,8 @@ function convertToOperationConfig(parseResult) {
|
|
|
452
467
|
children: [],
|
|
453
468
|
childList: [],
|
|
454
469
|
__level: 0,
|
|
455
|
-
hidden: false
|
|
470
|
+
hidden: false,
|
|
471
|
+
paramLocation: 'query'
|
|
456
472
|
}));
|
|
457
473
|
|
|
458
474
|
operation.inputs.push({
|
|
@@ -460,7 +476,8 @@ function convertToOperationConfig(parseResult) {
|
|
|
460
476
|
desc: '查询参数',
|
|
461
477
|
name: 'Query',
|
|
462
478
|
paramType: 'Object',
|
|
463
|
-
required: false
|
|
479
|
+
required: false,
|
|
480
|
+
paramLocation: 'query'
|
|
464
481
|
});
|
|
465
482
|
|
|
466
483
|
operation.parameters.query = parseResult.requestInfo.query.map(q => ({
|
|
@@ -478,7 +495,7 @@ function convertToOperationConfig(parseResult) {
|
|
|
478
495
|
default: JSON.stringify(requestExample)
|
|
479
496
|
};
|
|
480
497
|
|
|
481
|
-
const requestChildList = generateChildList(requestSchema, operation.operationId);
|
|
498
|
+
const requestChildList = applyParamLocation(generateChildList(requestSchema, operation.operationId), 'body');
|
|
482
499
|
if (requestChildList.length > 0) {
|
|
483
500
|
operation.inputs.push({
|
|
484
501
|
defaultValue: JSON.stringify(requestExample),
|
|
@@ -486,7 +503,8 @@ function convertToOperationConfig(parseResult) {
|
|
|
486
503
|
name: 'Body',
|
|
487
504
|
paramType: 'Object',
|
|
488
505
|
required: false,
|
|
489
|
-
childList: requestChildList
|
|
506
|
+
childList: requestChildList,
|
|
507
|
+
paramLocation: 'body'
|
|
490
508
|
});
|
|
491
509
|
}
|
|
492
510
|
}
|
|
@@ -496,13 +514,6 @@ function convertToOperationConfig(parseResult) {
|
|
|
496
514
|
const responseSchema = parseResult.responseInfo.schema;
|
|
497
515
|
operation.responses = responseSchema;
|
|
498
516
|
|
|
499
|
-
if (!operation.parameters.body) {
|
|
500
|
-
const example = generateExample(responseSchema);
|
|
501
|
-
operation.parameters.body = {
|
|
502
|
-
default: JSON.stringify(example)
|
|
503
|
-
};
|
|
504
|
-
}
|
|
505
|
-
|
|
506
517
|
operation.outputs = [generateOutputs(responseSchema, operation.operationId)];
|
|
507
518
|
} else {
|
|
508
519
|
operation.responses = { type: 'object' };
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { generateOutputs } = require('./response-parser');
|
|
4
|
+
|
|
5
|
+
function slugifyOperationId(value, fallback) {
|
|
6
|
+
const slug = String(value || '')
|
|
7
|
+
.trim()
|
|
8
|
+
.replace(/^\//, '')
|
|
9
|
+
.replace(/[^A-Za-z0-9_]+/g, '_')
|
|
10
|
+
.replace(/^_+|_+$/g, '');
|
|
11
|
+
|
|
12
|
+
return slug || fallback;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function deriveOperationId(operation, index) {
|
|
16
|
+
const explicitId = String(operation.operationId || '').trim();
|
|
17
|
+
if (explicitId) {
|
|
18
|
+
return explicitId;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const fallback = `operation_${index + 1}`;
|
|
22
|
+
const candidates = [operation.url, operation.path, operation.id, operation.name, operation.summary];
|
|
23
|
+
for (const candidate of candidates) {
|
|
24
|
+
const operationId = slugifyOperationId(candidate, '');
|
|
25
|
+
if (operationId) {
|
|
26
|
+
return operationId;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return fallback;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function normalizeUrl(value) {
|
|
34
|
+
return String(value || '').trim().replace(/^\/+/, '');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function inferParamLocation(input) {
|
|
38
|
+
if (input.paramLocation) {
|
|
39
|
+
return input.paramLocation;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const name = String(input.name || '').toLowerCase();
|
|
43
|
+
if (name === 'headers' || name === 'header') {
|
|
44
|
+
return 'header';
|
|
45
|
+
}
|
|
46
|
+
if (name === 'query' || name === 'queries') {
|
|
47
|
+
return 'query';
|
|
48
|
+
}
|
|
49
|
+
if (name === 'path' || name === 'paths') {
|
|
50
|
+
return 'path';
|
|
51
|
+
}
|
|
52
|
+
if (name === 'body') {
|
|
53
|
+
return 'body';
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function normalizeParamNode(node, paramLocation) {
|
|
60
|
+
if (!node || typeof node !== 'object') {
|
|
61
|
+
return node;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const normalized = { ...node };
|
|
65
|
+
if (paramLocation && !normalized.paramLocation) {
|
|
66
|
+
normalized.paramLocation = paramLocation;
|
|
67
|
+
}
|
|
68
|
+
if (!Array.isArray(normalized.children)) {
|
|
69
|
+
normalized.children = [];
|
|
70
|
+
}
|
|
71
|
+
if (!Array.isArray(normalized.childList)) {
|
|
72
|
+
normalized.childList = [];
|
|
73
|
+
}
|
|
74
|
+
normalized.children = normalized.children.map(child => normalizeParamNode(child, paramLocation));
|
|
75
|
+
normalized.childList = normalized.childList.map(child => normalizeParamNode(child, paramLocation));
|
|
76
|
+
return normalized;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function normalizeInput(input) {
|
|
80
|
+
if (!input || typeof input !== 'object') {
|
|
81
|
+
return input;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const paramLocation = inferParamLocation(input);
|
|
85
|
+
const normalized = { ...input };
|
|
86
|
+
if (paramLocation && !normalized.paramLocation) {
|
|
87
|
+
normalized.paramLocation = paramLocation;
|
|
88
|
+
}
|
|
89
|
+
if (!Array.isArray(normalized.childList)) {
|
|
90
|
+
normalized.childList = [];
|
|
91
|
+
}
|
|
92
|
+
normalized.childList = normalized.childList.map(child => normalizeParamNode(child, paramLocation));
|
|
93
|
+
return normalized;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function buildParametersFromInputs(inputs) {
|
|
97
|
+
const parameters = { header: [] };
|
|
98
|
+
|
|
99
|
+
for (const input of inputs) {
|
|
100
|
+
if (!input || typeof input !== 'object') {
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const location = inferParamLocation(input);
|
|
105
|
+
const childList = Array.isArray(input.childList) ? input.childList : [];
|
|
106
|
+
|
|
107
|
+
if (location === 'header') {
|
|
108
|
+
parameters.header = childList.map(child => ({
|
|
109
|
+
name: child.name,
|
|
110
|
+
value: child.defaultValue || '',
|
|
111
|
+
}));
|
|
112
|
+
} else if (location === 'query') {
|
|
113
|
+
parameters.query = childList.map(child => ({
|
|
114
|
+
name: child.name,
|
|
115
|
+
value: child.defaultValue || '',
|
|
116
|
+
}));
|
|
117
|
+
} else if (location === 'body' && input.defaultValue !== undefined) {
|
|
118
|
+
parameters.body = { default: input.defaultValue };
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return parameters;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function normalizeParameters(parameters, inputs) {
|
|
126
|
+
const normalized = parameters && typeof parameters === 'object' && !Array.isArray(parameters)
|
|
127
|
+
? { ...parameters }
|
|
128
|
+
: buildParametersFromInputs(inputs);
|
|
129
|
+
|
|
130
|
+
if (!Array.isArray(normalized.header)) {
|
|
131
|
+
normalized.header = [];
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return normalized;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function normalizeOutput(output) {
|
|
138
|
+
if (!output || typeof output !== 'object') {
|
|
139
|
+
return output;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const normalized = { ...output };
|
|
143
|
+
if (!Array.isArray(normalized.childList)) {
|
|
144
|
+
normalized.childList = [];
|
|
145
|
+
}
|
|
146
|
+
return normalized;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function normalizeOperation(operation, index) {
|
|
150
|
+
if (!operation || typeof operation !== 'object' || Array.isArray(operation)) {
|
|
151
|
+
throw new Error(`第 ${index + 1} 个执行动作必须是对象`);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const operationId = deriveOperationId(operation, index);
|
|
155
|
+
const rawUrl = operation.url !== undefined && operation.url !== null ? operation.url : operation.path;
|
|
156
|
+
const hasUrl = rawUrl !== undefined && rawUrl !== null && String(rawUrl).trim() !== '';
|
|
157
|
+
const url = normalizeUrl(rawUrl);
|
|
158
|
+
|
|
159
|
+
if (!hasUrl) {
|
|
160
|
+
throw new Error(`第 ${index + 1} 个执行动作缺少 url/path`);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const summary = operation.summary || operation.name || operationId;
|
|
164
|
+
const description = operation.description || operation.desc || summary;
|
|
165
|
+
const method = String(operation.method || 'get').toLowerCase();
|
|
166
|
+
const inputs = Array.isArray(operation.inputs)
|
|
167
|
+
? operation.inputs.map(normalizeInput)
|
|
168
|
+
: [];
|
|
169
|
+
const responses = operation.responses || { type: 'object', properties: {} };
|
|
170
|
+
const outputs = Array.isArray(operation.outputs) && operation.outputs.length > 0
|
|
171
|
+
? operation.outputs.map(normalizeOutput)
|
|
172
|
+
: [generateOutputs(responses, operationId)];
|
|
173
|
+
|
|
174
|
+
return {
|
|
175
|
+
...operation,
|
|
176
|
+
id: operation.id || `operation-${operationId}`,
|
|
177
|
+
operationId,
|
|
178
|
+
summary,
|
|
179
|
+
description,
|
|
180
|
+
url,
|
|
181
|
+
method,
|
|
182
|
+
inputs,
|
|
183
|
+
parameters: normalizeParameters(operation.parameters, inputs),
|
|
184
|
+
responses,
|
|
185
|
+
outputs,
|
|
186
|
+
origin: operation.origin !== undefined ? operation.origin : true,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function normalizeOperations(operations) {
|
|
191
|
+
const list = Array.isArray(operations) ? operations : [operations];
|
|
192
|
+
return list.map(normalizeOperation);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
module.exports = {
|
|
196
|
+
normalizeOperation,
|
|
197
|
+
normalizeOperations,
|
|
198
|
+
};
|
package/lib/core/chalk.js
CHANGED
package/lib/core/env-cmd.js
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
const { execSync } = require('child_process');
|
|
15
15
|
const readline = require('readline');
|
|
16
|
-
const { warn } = require('./chalk');
|
|
16
|
+
const { c, warn } = require('./chalk');
|
|
17
17
|
const {
|
|
18
18
|
loadEnvsConfig,
|
|
19
19
|
saveEnvsConfig,
|
|
@@ -28,14 +28,14 @@ const {
|
|
|
28
28
|
|
|
29
29
|
// ── 颜色常量 ──────────────────────────────────────────
|
|
30
30
|
|
|
31
|
-
const RESET =
|
|
32
|
-
const BOLD =
|
|
33
|
-
const DIM =
|
|
34
|
-
const GREEN =
|
|
35
|
-
const YELLOW =
|
|
36
|
-
const CYAN =
|
|
37
|
-
const RED =
|
|
38
|
-
const BLUE =
|
|
31
|
+
const RESET = c.reset;
|
|
32
|
+
const BOLD = c.bold;
|
|
33
|
+
const DIM = c.dim;
|
|
34
|
+
const GREEN = c.green;
|
|
35
|
+
const YELLOW = c.yellow;
|
|
36
|
+
const CYAN = c.cyan;
|
|
37
|
+
const RED = c.red;
|
|
38
|
+
const BLUE = c.blue;
|
|
39
39
|
|
|
40
40
|
// ── 工具函数 ──────────────────────────────────────────
|
|
41
41
|
|
package/lib/core/update.js
CHANGED
|
@@ -12,18 +12,18 @@
|
|
|
12
12
|
const { execSync } = require('child_process');
|
|
13
13
|
const { fetchLatestVersion, isNewer } = require('./check-update');
|
|
14
14
|
const { t } = require('./i18n');
|
|
15
|
-
const { warn } = require('./chalk');
|
|
15
|
+
const { c, warn } = require('./chalk');
|
|
16
16
|
const { getNpmExecutable } = require('./utils');
|
|
17
17
|
|
|
18
18
|
// ── ANSI 颜色常量 ──────────────────────────────────
|
|
19
|
-
const RESET =
|
|
20
|
-
const BOLD =
|
|
21
|
-
const DIM =
|
|
22
|
-
const GREEN =
|
|
23
|
-
const YELLOW =
|
|
24
|
-
const CYAN =
|
|
25
|
-
const RED =
|
|
26
|
-
const MAGENTA =
|
|
19
|
+
const RESET = c.reset;
|
|
20
|
+
const BOLD = c.bold;
|
|
21
|
+
const DIM = c.dim;
|
|
22
|
+
const GREEN = c.green;
|
|
23
|
+
const YELLOW = c.yellow;
|
|
24
|
+
const CYAN = c.cyan;
|
|
25
|
+
const RED = c.red;
|
|
26
|
+
const MAGENTA = c.magenta;
|
|
27
27
|
|
|
28
28
|
// ── Spinner 动画 ───────────────────────────────────
|
|
29
29
|
const SPINNER_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
package/lib/dws/dws-wrapper.js
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
const { execSync, spawn } = require('child_process');
|
|
14
14
|
const { t } = require('../core/i18n');
|
|
15
|
-
const { warn } = require('../core/chalk');
|
|
15
|
+
const { c, warn } = require('../core/chalk');
|
|
16
16
|
|
|
17
17
|
const DWS_BINARY_NAME = 'dws';
|
|
18
18
|
const INSTALL_SCRIPTS = {
|
|
@@ -51,16 +51,16 @@ function getDwsVersion() {
|
|
|
51
51
|
* 显示安装指引
|
|
52
52
|
*/
|
|
53
53
|
function showInstallGuide() {
|
|
54
|
-
const RESET =
|
|
55
|
-
const BOLD =
|
|
56
|
-
const DIM =
|
|
57
|
-
const CYAN =
|
|
58
|
-
const GREEN =
|
|
59
|
-
const YELLOW =
|
|
60
|
-
const BLUE =
|
|
54
|
+
const RESET = c.reset;
|
|
55
|
+
const BOLD = c.bold;
|
|
56
|
+
const DIM = c.dim;
|
|
57
|
+
const CYAN = c.cyan;
|
|
58
|
+
const GREEN = c.green;
|
|
59
|
+
const YELLOW = c.yellow;
|
|
60
|
+
const BLUE = c.blue;
|
|
61
61
|
|
|
62
|
-
const BG_BLUE =
|
|
63
|
-
const WHITE =
|
|
62
|
+
const BG_BLUE = c.bgBlue;
|
|
63
|
+
const WHITE = c.white;
|
|
64
64
|
|
|
65
65
|
const isWindows = process.platform === 'win32';
|
|
66
66
|
const installCommand = isWindows ? INSTALL_SCRIPTS.windows : INSTALL_SCRIPTS.unix;
|
|
@@ -221,11 +221,11 @@ async function executeDwsCommand(args) {
|
|
|
221
221
|
* 显示 dws 帮助信息
|
|
222
222
|
*/
|
|
223
223
|
function showDwsHelp() {
|
|
224
|
-
const RESET =
|
|
225
|
-
const BOLD =
|
|
226
|
-
const CYAN =
|
|
227
|
-
const GREEN =
|
|
228
|
-
const YELLOW =
|
|
224
|
+
const RESET = c.reset;
|
|
225
|
+
const BOLD = c.bold;
|
|
226
|
+
const CYAN = c.cyan;
|
|
227
|
+
const GREEN = c.green;
|
|
228
|
+
const YELLOW = c.yellow;
|
|
229
229
|
|
|
230
230
|
console.log('');
|
|
231
231
|
console.log(`${BOLD}openyida dws - 钉钉 CLI 集成${RESET}`);
|