@cnbcool/cnb-api-generate 2.7.3 → 2.7.5

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.
@@ -84,9 +84,12 @@ function flattenToolOptions(toolInfo) {
84
84
  ? (0, clean_array_desc_1.cleanArrayDesc)((0, trim_summary_1.trimSummary)(prop.description || ''))
85
85
  : (0, trim_summary_1.trimSummary)(prop.description || '');
86
86
  const arrayHint = isArray ? ' (可多次传入)' : '';
87
+ const fileRefHint = !isArray && prop.type === 'string' && key === 'body'
88
+ ? ' (支持 @file 引用)'
89
+ : '';
87
90
  const desc = isRequired
88
- ? `[必填] ${rawDesc}${arrayHint}`
89
- : `${rawDesc}${arrayHint}`;
91
+ ? `[必填] ${rawDesc}${arrayHint}${fileRefHint}`
92
+ : `${rawDesc}${arrayHint}${fileRefHint}`;
90
93
  const opt = {
91
94
  optKey,
92
95
  valuePlaceholder,
@@ -0,0 +1,62 @@
1
+ import { getToolParamDefs } from './format-params';
2
+
3
+ /**
4
+ * 根据 formatParams 的产物构造调用 toolFunction 的位置参数列表。
5
+ *
6
+ * 关键约定(与代码生成器 generate-function-params-ast-node.ts 编译期一致):
7
+ * - swagger pathParams.length > 1 || queryParams.length > 0 → 第 1 形参是对象
8
+ * `{ ...path, ...query }`
9
+ * - 否则 → 第 1 形参是标量(path 字段值),
10
+ * 或没有第 1 形参(pathParams.length === 0)
11
+ *
12
+ * 这是编译期 swagger 决定的形态,**与运行期 query 是否传值无关**。
13
+ * 例:`GET /{repo}/-/issues?state=open` 即便用户没传 --state,toolFunction
14
+ * 仍期望 `{ repo, state? }` 对象,绝不能把 path 拆成标量。
15
+ *
16
+ * 历史 bug:误把单 path 的 tool 包成对象传入,模板字符串 `${对象}` 渲染成
17
+ * `[object Object]`,URL encode 后变 `[object%20Object]`,触发 404。
18
+ */
19
+ export function buildToolParams(formattedParams: {
20
+ module?: string;
21
+ tool?: string;
22
+ path?: Record<string, any>;
23
+ query?: Record<string, any>;
24
+ data?: any;
25
+ }): any[] {
26
+ const { module: moduleName, tool, path, query, data } = formattedParams;
27
+ const queryHasValue =
28
+ !!query && typeof query === 'object' && Object.keys(query).length > 0;
29
+
30
+ // 1) 决定第 1 形参形态。优先用 swagger 定义;拿不到时按运行期信号兜底(旧行为)。
31
+ let isObjectShape: boolean;
32
+ const defs = moduleName && tool ? getToolParamDefs(moduleName, tool) : null;
33
+ if (defs) {
34
+ const pathDefCount = defs.path ? Object.keys(defs.path).length : 0;
35
+ const queryDefCount = defs.query ? Object.keys(defs.query).length : 0;
36
+ isObjectShape = pathDefCount > 1 || queryDefCount > 0;
37
+ } else {
38
+ // 兜底:path 多于 1 个、或运行期 query 真有值,则按对象形态合并
39
+ const pathKeyCount = path ? Object.keys(path).length : 0;
40
+ isObjectShape = pathKeyCount > 1 || queryHasValue;
41
+ }
42
+
43
+ // 2) 按形态构造第 1 个参数
44
+ let firstParam: Record<string, any> | string | null = null;
45
+ if (isObjectShape) {
46
+ // 对象形态:合并 path 与运行期 query;任一为空都允许,toolFunction 内部解构是可选的
47
+ if (path || queryHasValue) {
48
+ firstParam = { ...(path ?? {}), ...(query ?? {}) };
49
+ }
50
+ } else if (path) {
51
+ // 标量形态:仅当 path 恰好 1 个字段时拆出值;其它情形(含异常的多字段)不构造,
52
+ // 避免悄悄把不该传的字段塞给 toolFunction
53
+ const keys = Object.keys(path);
54
+ if (keys.length === 1) firstParam = path[keys[0]];
55
+ }
56
+
57
+ // 3) 组装最终位置参数列表(与历史行为一致:firstParam 真值才 push;data 真值才 push)
58
+ const toolsParam: any[] = [];
59
+ if (firstParam) toolsParam.push(firstParam);
60
+ if (data) toolsParam.push(data);
61
+ return toolsParam;
62
+ }
@@ -5,6 +5,7 @@ import { diagnoseBuild } from './build-diagnostics';
5
5
  import { analyzeBuildTiming } from './build-timing';
6
6
  import { formatParams } from './format-params';
7
7
  import { formatOutput } from './format-output';
8
+ import { buildToolParams } from './build-tool-params';
8
9
  // @ts-ignore
9
10
  import {getLoader} from '../../loader';
10
11
  /**
@@ -116,38 +117,7 @@ export async function executeAction(
116
117
 
117
118
  const toolFunction = loadToolFunction(formattedParams.module, formattedParams.tool);
118
119
 
119
- const toolsParam: any[] = [];
120
- let pathAndQueryParams: Record<string, any> | string | null = null;
121
-
122
- if (
123
- formattedParams.path &&
124
- Object.keys(formattedParams.path).length === 1 &&
125
- !formattedParams.query
126
- ) {
127
- pathAndQueryParams =
128
- formattedParams.path[Object.keys(formattedParams.path)[0]];
129
- } else {
130
- if (formattedParams.path) {
131
- pathAndQueryParams = { ...formattedParams.path };
132
- }
133
-
134
- if (formattedParams.query) {
135
- pathAndQueryParams = {
136
- ...(pathAndQueryParams && typeof pathAndQueryParams === 'object'
137
- ? pathAndQueryParams
138
- : {}),
139
- ...formattedParams.query,
140
- };
141
- }
142
- }
143
-
144
- if (pathAndQueryParams) {
145
- toolsParam.push(pathAndQueryParams);
146
- }
147
-
148
- if (formattedParams.data) {
149
- toolsParam.push(formattedParams.data);
150
- }
120
+ const toolsParam = buildToolParams(formattedParams);
151
121
  const data = await toolFunction(...toolsParam);
152
122
 
153
123
  const toolKey = `${formattedParams.module}/${formattedParams.tool}`;
@@ -51,6 +51,32 @@ export function formatParams(
51
51
  'module', 'tool', 'help', 'short', 'verbose', 'h', 'v',
52
52
  ]);
53
53
 
54
+ // --data 是 CLI 通用 fallback 选项(见 flatten-tool-options.ts,仅在 tool 有 body 时注册),
55
+ // 接收 JSON 字符串(或 @file 引用)作为 body 整体。
56
+ // 仅当 'data' 不是该 tool 真实的 path/query/body 字段名时才启用 fallback 语义,
57
+ // 否则按 swagger 定义当业务字段处理(保留 commit 120d557 引入的能力)。
58
+ //
59
+ // 与 swagger body schema 类型对齐:
60
+ // - object body(默认):parsed 必须是 object,作为 body base,逐个 body flag(如 --event)覆盖同名字段
61
+ // - array body(如 BatchPinDashboardStar):parsed 必须是 array,直接整体替换 body
62
+ // 类型不匹配(如 object body 接口收到 array)静默忽略,避免破坏 flag 已写入的字段
63
+ const dataIsFallback =
64
+ !!paramDefs.body && !pathDef.data && !queryDef.data && !bodyProps.data;
65
+ const bodyIsArrayType = paramDefs.body?.schema?.type === 'array';
66
+ let dataBaseFromFlag: Record<string, any> | undefined;
67
+ let dataArrayFromFlag: any[] | undefined;
68
+ if (dataIsFallback) {
69
+ reservedKeys.add('data');
70
+ if (typeof params.data === 'string' && params.data.length > 0) {
71
+ const parsed = tryParseJSON(tryReadFileRef(params.data));
72
+ if (bodyIsArrayType && Array.isArray(parsed)) {
73
+ dataArrayFromFlag = parsed;
74
+ } else if (!bodyIsArrayType && parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
75
+ dataBaseFromFlag = parsed as Record<string, any>;
76
+ }
77
+ }
78
+ }
79
+
54
80
  // 收集 array<object> 展开字段的临时存储:{ arrayName: { propName: string[] } }
55
81
  const arrayObjectCollector: Record<string, Record<string, string[]>> = {};
56
82
 
@@ -252,18 +278,19 @@ export function formatParams(
252
278
  if (!formatted.data) formatted.data = {};
253
279
  formatted.data[objectKey] = subProps;
254
280
  }
255
- }
256
281
 
257
- // 当没有传递 query 时,要判断当前 tool 是否支持 query
258
- if (formatted.query === undefined) {
259
- const paramDefs2 = getToolParamDefs(
260
- formatted.module,
261
- formatted.tool,
262
- );
263
- if (paramDefs2?.query) {
264
- formatted.query = {};
282
+ // 合并 --data 提供的 body:
283
+ // - 数组 body:整体替换 formatted.data(数组 body 接口没有逐字段 flag,直接覆盖)
284
+ // - 对象 body:作为 base,逐个 body flag(如 --event)已写入 formatted.data,flag 优先
285
+ if (dataArrayFromFlag) {
286
+ formatted.data = dataArrayFromFlag;
287
+ } else if (dataBaseFromFlag) {
288
+ formatted.data = { ...dataBaseFromFlag, ...(formatted.data || {}) };
265
289
  }
266
290
  }
267
291
 
292
+ // formatted.query 仅在用户真传了 query 值时才存在;toolFunction 第 1 形参形态
293
+ // 由 buildToolParams 通过 swagger 定义判定,不依赖运行期 query 是否有值。
294
+
268
295
  return formatted;
269
296
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cnbcool/cnb-api-generate",
3
- "version": "2.7.3",
3
+ "version": "2.7.5",
4
4
  "main": "./built/index.js",
5
5
  "module": "./src/index.ts",
6
6
  "types": "./src/index.ts",