zentao-api 0.4.0 → 0.5.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.
@@ -110,4 +110,18 @@ export function applyBuiltinOverrides() {
110
110
  return action;
111
111
  });
112
112
  });
113
+ // 为 task/create|update 补充 parent 字段(创建子任务)
114
+ ['create', 'update'].forEach((actionName) => {
115
+ extendModuleAction('task', actionName, (action) => {
116
+ const properties = action.requestBody?.schema?.properties;
117
+ if (properties && !properties.parent) {
118
+ properties.parent = {
119
+ type: 'integer',
120
+ description: '父任务',
121
+ format: 'int32',
122
+ };
123
+ }
124
+ return action;
125
+ });
126
+ });
113
127
  }
@@ -6,10 +6,9 @@ import type { ModuleAction, ModuleActionParam, ModuleActionParamRole, ModuleDefi
6
6
  * 任何写入尝试在严格模式下会抛 `TypeError`;如需修改请使用 {@link defineModules}。
7
7
  *
8
8
  * @param moduleName - 模块名。
9
- * @returns 已注册的模块定义。
10
- * @throws {ZentaoError} `E_INVALID_MODULE` —— 模块未注册。
9
+ * @returns 已注册的模块定义;模块未注册时返回 `undefined`。
11
10
  */
12
- export declare function getModule(moduleName: string): ModuleDefinition;
11
+ export declare function getModule(moduleName: string): ModuleDefinition | undefined;
13
12
  /**
14
13
  * 获取指定模块下的某个动作。
15
14
  *
@@ -23,10 +22,9 @@ export declare function getModule(moduleName: string): ModuleDefinition;
23
22
  *
24
23
  * @param moduleName - 模块名(大小写不敏感)。
25
24
  * @param actionName - 动作名(大小写不敏感);支持 `ls` 作为 `list` 的别名。
26
- * @returns 匹配到的动作定义。
27
- * @throws {ZentaoError} `E_INVALID_MODULE`(模块未注册)或 `E_INVALID_ACTION`(动作不存在)。
25
+ * @returns 匹配到的动作定义;模块未注册或动作不存在时返回 `undefined`。
28
26
  */
29
- export declare function getModuleAction(moduleName: string, actionName: string): ModuleAction;
27
+ export declare function getModuleAction(moduleName: string, actionName: string): ModuleAction | undefined;
30
28
  /**
31
29
  * 获取指定模块下的某个动作的参数。
32
30
  *
@@ -54,3 +52,10 @@ export declare function getModuleNames(): string[];
54
52
  * @returns 已注册返回 `true`,否则 `false`。
55
53
  */
56
54
  export declare function isModuleName(moduleName: string): boolean;
55
+ /**
56
+ * 获取对象属性。
57
+ *
58
+ * @param objectType - 对象类型。
59
+ * @returns 对象属性。
60
+ */
61
+ export declare function getObjectProps(objectType: string): Record<string, string>;
@@ -1,4 +1,4 @@
1
- import { ZentaoError } from '../misc/errors.js';
1
+ import { objectProps } from './object-props.js';
2
2
  import { getModuleMapState, getModulesState } from './registry-store.js';
3
3
  /**
4
4
  * 获取模块定义。
@@ -7,15 +7,10 @@ import { getModuleMapState, getModulesState } from './registry-store.js';
7
7
  * 任何写入尝试在严格模式下会抛 `TypeError`;如需修改请使用 {@link defineModules}。
8
8
  *
9
9
  * @param moduleName - 模块名。
10
- * @returns 已注册的模块定义。
11
- * @throws {ZentaoError} `E_INVALID_MODULE` —— 模块未注册。
10
+ * @returns 已注册的模块定义;模块未注册时返回 `undefined`。
12
11
  */
13
12
  export function getModule(moduleName) {
14
- const module = getModuleMapState().get(moduleName.toLowerCase());
15
- if (!module) {
16
- throw new ZentaoError('E_INVALID_MODULE', { module: moduleName });
17
- }
18
- return module;
13
+ return getModuleMapState().get(moduleName.toLowerCase());
19
14
  }
20
15
  /**
21
16
  * 获取指定模块下的某个动作。
@@ -30,11 +25,12 @@ export function getModule(moduleName) {
30
25
  *
31
26
  * @param moduleName - 模块名(大小写不敏感)。
32
27
  * @param actionName - 动作名(大小写不敏感);支持 `ls` 作为 `list` 的别名。
33
- * @returns 匹配到的动作定义。
34
- * @throws {ZentaoError} `E_INVALID_MODULE`(模块未注册)或 `E_INVALID_ACTION`(动作不存在)。
28
+ * @returns 匹配到的动作定义;模块未注册或动作不存在时返回 `undefined`。
35
29
  */
36
30
  export function getModuleAction(moduleName, actionName) {
37
31
  const module = getModule(moduleName);
32
+ if (!module)
33
+ return undefined;
38
34
  const normalized = actionName === 'ls' ? 'list' : actionName;
39
35
  const direct = module.actions.find((action) => String(action.name).toLowerCase() === normalized.toLowerCase());
40
36
  if (direct)
@@ -45,7 +41,7 @@ export function getModuleAction(moduleName, actionName) {
45
41
  if (custom)
46
42
  return custom;
47
43
  }
48
- throw new ZentaoError('E_INVALID_ACTION', { module: moduleName, action: actionName });
44
+ return undefined;
49
45
  }
50
46
  /**
51
47
  * 获取指定模块下的某个动作的参数。
@@ -125,3 +121,12 @@ export function getModuleNames() {
125
121
  export function isModuleName(moduleName) {
126
122
  return getModuleMapState().has(moduleName.toLowerCase());
127
123
  }
124
+ /**
125
+ * 获取对象属性。
126
+ *
127
+ * @param objectType - 对象类型。
128
+ * @returns 对象属性。
129
+ */
130
+ export function getObjectProps(objectType) {
131
+ return objectProps[objectType];
132
+ }
@@ -4238,4 +4238,4 @@ export declare const MODULES: readonly [{
4238
4238
  }];
4239
4239
  export { type DefineModulesOptions, defineModules, defineModuleActions, extendModuleAction, resetModuleDefinitions, } from './define.js';
4240
4240
  export { applyBuiltinOverrides };
4241
- export { getModule, getModuleAction, getModuleActionParams, getModuleNames, isModuleName, } from './query.js';
4241
+ export { getModule, getModuleAction, getModuleActionParams, getModuleNames, isModuleName, getObjectProps, } from './query.js';
@@ -16,4 +16,4 @@ export { applyBuiltinOverrides };
16
16
  // - 注册为 store 的「重置后钩子」,使 resetModuleDefinitions 还原内置基线后自动重新应用。
17
17
  setPostResetHook(applyBuiltinOverrides);
18
18
  applyBuiltinOverrides();
19
- export { getModule, getModuleAction, getModuleActionParams, getModuleNames, isModuleName, } from './query.js';
19
+ export { getModule, getModuleAction, getModuleActionParams, getModuleNames, isModuleName, getObjectProps, } from './query.js';
@@ -6,9 +6,28 @@ const SCOPE_MAP = {
6
6
  project: 'projects',
7
7
  execution: 'executions',
8
8
  };
9
+ const VALID_SCOPES = new Set(Object.values(SCOPE_MAP));
9
10
  const SCOPE_KEY_ORDER = ['execution', 'project', 'product'];
10
- /** 从调用参数中推断作用域列表路径,优先级为执行 > 项目 > 产品。 */
11
+ /**
12
+ * 从调用参数中推断作用域列表路径。
13
+ *
14
+ * 优先使用显式 `scope` + `scopeID`;二者缺一则回落到别名推断,
15
+ * 别名优先级为执行 > 项目 > 产品。
16
+ */
11
17
  function pickScope(params) {
18
+ const explicitScope = params.scope;
19
+ const explicitScopeID = params.scopeID;
20
+ if (!isBlank(explicitScope) && !isBlank(explicitScopeID)) {
21
+ const scope = String(explicitScope);
22
+ if (!VALID_SCOPES.has(scope)) {
23
+ throw new ZentaoError('E_INVALID_PARAM', { param: 'scope', value: scope });
24
+ }
25
+ const scopeID = Number(explicitScopeID);
26
+ if (Number.isNaN(scopeID)) {
27
+ throw new ZentaoError('E_INVALID_PARAM', { param: 'scopeID', value: String(explicitScopeID) });
28
+ }
29
+ return { scope, scopeID };
30
+ }
12
31
  for (const key of SCOPE_KEY_ORDER) {
13
32
  const value = params[key] ?? params[`${key}ID`];
14
33
  if (isBlank(value))
@@ -98,7 +117,7 @@ function resolvePathValues(module, action, params) {
98
117
  if (pathParamNames.includes('scope')) {
99
118
  const scope = pickScope(params);
100
119
  if (!scope)
101
- throw new ZentaoError('E_MISSING_PARAM', { param: 'product/project/execution' });
120
+ throw new ZentaoError('E_MISSING_PARAM', { param: 'scope/scopeID or product/project/execution' });
102
121
  values.scope = scope.scope;
103
122
  values.scopeID = scope.scopeID;
104
123
  }
@@ -180,6 +199,9 @@ function buildRequestBody(action, params) {
180
199
  /** 将模块名、动作名和调用参数解析为实际 API 请求路径、查询参数和请求体。 */
181
200
  export function resolveActionRequest(module, actionName, params = {}) {
182
201
  const action = getModuleAction(module.name, actionName);
202
+ if (!action) {
203
+ throw new ZentaoError('E_INVALID_ACTION', { module: module.name, action: actionName });
204
+ }
183
205
  const { values, id } = resolvePathValues(module, action, params);
184
206
  return {
185
207
  module: module.name,
@@ -70,6 +70,8 @@ type BodyParams<A> = A extends {
70
70
  data?: string | Record<string, unknown>;
71
71
  };
72
72
  type ScopedParams<PathParams> = 'scope' extends keyof PathParams ? {
73
+ scope?: 'products' | 'projects' | 'executions' | (string & {});
74
+ scopeID?: string | number;
73
75
  product?: string | number;
74
76
  productID?: string | number;
75
77
  project?: string | number;
@@ -176,6 +176,9 @@ export async function request(name, params = {}, options = {}) {
176
176
  }
177
177
  const { moduleName, actionName, id } = splitRequestName(name);
178
178
  const module = getModule(moduleName);
179
+ if (!module) {
180
+ throw new ZentaoError('E_INVALID_MODULE', { module: moduleName });
181
+ }
179
182
  // recPerPage 是最常用的列表参数,允许在全局或本次调用中统一覆盖。
180
183
  const recPerPage = params.recPerPage ?? options.recPerPage ?? globals.recPerPage;
181
184
  const mergedParams = {
@@ -186,6 +189,9 @@ export async function request(name, params = {}, options = {}) {
186
189
  // autoFill:update 动作先 GET 当前对象,用现值补齐用户未显式传入的字段,
187
190
  // 避免禅道 PUT 把未提交字段覆盖为空。
188
191
  const action = getModuleAction(moduleName, actionName);
192
+ if (!action) {
193
+ throw new ZentaoError('E_INVALID_ACTION', { module: moduleName, action: actionName });
194
+ }
189
195
  const finalParams = action.type === 'update' && (options.autoFill ?? globals.autoFill)
190
196
  ? await autoFillUpdateParams(module, action, mergedParams, options)
191
197
  : mergedParams;
package/dist/version.js CHANGED
@@ -1,5 +1,5 @@
1
- const fallbackBuild = "2026-07-21T02:40:34.356Z";
2
- const fallbackVersion = "0.4.0";
1
+ const fallbackBuild = "2026-08-11T11:36:59.266Z";
2
+ const fallbackVersion = "0.5.0";
3
3
  /**
4
4
  * 构建标识,由构建脚本通过 `__ZENTAO_API_BUILD__` 注入。
5
5
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zentao-api",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "type": "module",
5
5
  "description": "Browser and Node.js SDK for ZenTao API",
6
6
  "license": "MIT",
@@ -70,6 +70,8 @@
70
70
  "vitepress": "^1.6.4"
71
71
  },
72
72
  "engines": {
73
- "node": ">=18"
74
- }
73
+ "node": ">=18",
74
+ "bun": ">=1.0.0"
75
+ },
76
+ "packageManager": "bun@1.3.13"
75
77
  }