zentao-api 0.2.0-beta.2 → 0.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.
@@ -1,39 +1,140 @@
1
1
  import type { ClientRequestOptions, ZentaoClientOptions } from '../types/index.js';
2
- /** 禅道 API 客户端,负责 Token 注入、请求超时、TLS 选项和响应解析。 */
2
+ /**
3
+ * 禅道 API 客户端,封装一次次原始 HTTP 调用。
4
+ *
5
+ * 主要职责:
6
+ * - 站点根地址规范化与 `/api.php/v2` 拼接
7
+ * - 自动注入 `Token` 头
8
+ * - 请求超时控制(基于 {@link AbortController})
9
+ * - 可选的 TLS 跳过校验(仅 Node.js 运行时)
10
+ * - 响应体的 JSON 解析与错误归一化
11
+ *
12
+ * 适合直接调用裸 API;若希望按模块/动作名调用并自动组装路径、参数、分页,
13
+ * 请改用 {@link request}。
14
+ */
3
15
  export declare class ZentaoClient {
4
16
  /** 禅道站点根地址,不包含 `/api.php/v2`。 */
5
17
  readonly siteUrl: string;
6
- /** 禅道 API v2 根地址。 */
18
+ /** 禅道 API v2 根地址,等于 `siteUrl + '/api.php/v2'`。 */
7
19
  readonly baseUrl: string;
8
20
  private token?;
9
21
  private readonly timeout?;
10
22
  private readonly insecure?;
11
- /** 使用完整配置创建客户端。 */
23
+ /**
24
+ * 使用完整配置创建客户端。
25
+ *
26
+ * @param options - 客户端配置,参见 {@link ZentaoClientOptions}。
27
+ * @throws {ZentaoError} `E_INVALID_BASE_URL` —— `baseUrl` 无法解析为合法的 http(s) URL。
28
+ */
12
29
  constructor(options: ZentaoClientOptions);
13
- /** 使用站点根地址创建客户端。 */
30
+ /**
31
+ * 使用站点根地址创建客户端。
32
+ *
33
+ * @param baseUrl - 禅道站点根地址,例如 `https://zentao.example.com`;
34
+ * 若误传 `/api.php/v2` 后缀会自动剥离。
35
+ * @throws {ZentaoError} `E_INVALID_BASE_URL` —— 地址不合法或协议非 http(s)。
36
+ */
14
37
  constructor(baseUrl: string);
15
38
  /**
16
39
  * 发起一次原始 API 请求。
17
40
  *
18
- * 默认使用 GET;当服务端返回 `{ status: "fail" }` 时仍按原始内容返回,
19
- * 只有 HTTP/网络/超时等传输层错误会抛出 {@link ZentaoError}。
41
+ * 选项优先级:本次调用 `options` > 全局选项({@link getGlobalOptions} > 客户端构造时默认值。
42
+ *
43
+ * 特殊处理:
44
+ * - 默认 HTTP 方法为 `GET`,`GET` 请求即使提供了 `options.body` 也不会发送,避免被部分代理/浏览器拒绝。
45
+ * - 非空响应优先按 JSON 解析;解析失败时回落为字符串原文。
46
+ * - 业务层失败(即响应体 `{ status: "fail" }`)不会抛出,仍按原样返回;只有 HTTP/网络/超时等传输层错误才会抛错。
47
+ * - `insecure` 仅在 Node.js 下可用,浏览器中传入会抛 `E_INSECURE_BROWSER`。
48
+ *
49
+ * @param path - 相对 {@link baseUrl} 的路径,可省略前导 `/`。
50
+ * @param options - 单次请求选项,参见 {@link ClientRequestOptions}。
51
+ * @returns 解析后的响应体;当响应为空字符串时返回 `undefined`。
52
+ * @throws {ZentaoError} 可能抛出 `E_HTTP_ERROR`(非 2xx 状态)、`E_NETWORK_ERROR`(底层 fetch 失败)、
53
+ * `E_TIMEOUT`(超过 `timeout`)或 `E_INSECURE_BROWSER`(浏览器中开启了 `insecure`)。
20
54
  */
21
55
  request(path: string, options?: ClientRequestOptions): Promise<unknown>;
22
- /** 发起 GET 请求并按调用方指定类型返回。 */
56
+ /**
57
+ * 发起 `GET` 请求。
58
+ *
59
+ * @typeParam T - 期望的响应体类型;调用方负责类型收窄,SDK 不做运行时校验。
60
+ * @param path - 相对 {@link baseUrl} 的路径。
61
+ * @returns 解析后的响应体(强转为 `T`)。
62
+ * @throws {ZentaoError} 传输层失败时抛出,详见 {@link ZentaoClient.request}。
63
+ */
23
64
  get<T>(path: string): Promise<T>;
24
- /** 发起 POST 请求并发送 JSON body。 */
25
- post<T>(path: string, body: any): Promise<T>;
26
- /** 发起 PUT 请求并发送 JSON body。 */
27
- put<T>(path: string, body: any): Promise<T>;
28
- /** 发起 DELETE 请求。 */
65
+ /**
66
+ * 发起 `POST` 请求,`body` 会被序列化为 JSON。
67
+ *
68
+ * @typeParam T - 期望的响应体类型。
69
+ * @param path - 相对 {@link baseUrl} 的路径。
70
+ * @param body - JSON 请求体,传入对象/数组将被 `JSON.stringify`。
71
+ * @returns 解析后的响应体(强转为 `T`)。
72
+ * @throws {ZentaoError} 传输层失败时抛出,详见 {@link ZentaoClient.request}。
73
+ */
74
+ post<T>(path: string, body: unknown): Promise<T>;
75
+ /**
76
+ * 发起 `PUT` 请求,`body` 会被序列化为 JSON。
77
+ *
78
+ * @typeParam T - 期望的响应体类型。
79
+ * @param path - 相对 {@link baseUrl} 的路径。
80
+ * @param body - JSON 请求体。
81
+ * @returns 解析后的响应体(强转为 `T`)。
82
+ * @throws {ZentaoError} 传输层失败时抛出,详见 {@link ZentaoClient.request}。
83
+ */
84
+ put<T>(path: string, body: unknown): Promise<T>;
85
+ /**
86
+ * 发起 `DELETE` 请求。
87
+ *
88
+ * @typeParam T - 期望的响应体类型。
89
+ * @param path - 相对 {@link baseUrl} 的路径。
90
+ * @returns 解析后的响应体(强转为 `T`)。
91
+ * @throws {ZentaoError} 传输层失败时抛出,详见 {@link ZentaoClient.request}。
92
+ */
29
93
  delete<T>(path: string): Promise<T>;
30
- /** 使用账号密码登录,成功后把返回 Token 写入当前客户端实例。 */
94
+ /**
95
+ * 使用账号密码登录禅道。
96
+ *
97
+ * 成功后会把返回的 Token 写入当前客户端实例(后续请求自动带上 `Token` 头);
98
+ * 当全局 `persistProfiles` 为真时,会同时把账号、Token、用户信息、服务端配置和
99
+ * 客户端偏好(仅在显式设置过 `timeout` / `insecure` 时)持久化为本地 profile,
100
+ * 并切换为当前 profile,方便下次通过 {@link ZentaoClient.fromProfile} 直接登录态恢复。
101
+ *
102
+ * @param account - 禅道用户账号。
103
+ * @param password - 禅道用户密码(明文,仅在传输层 TLS 内使用)。
104
+ * @returns 登录成功后返回的 API Token。
105
+ * @throws {ZentaoError} `E_LOGIN_FAILED` —— 服务端返回 `status !== "success"` 或缺失 token;
106
+ * 也可能因底层 {@link ZentaoClient.request} 而抛出 HTTP/网络/超时错误。
107
+ */
31
108
  login(account: string, password: string): Promise<string>;
32
- /** 创建客户端实例,语义等同于 `new ZentaoClient(options)`。 */
109
+ /**
110
+ * 创建客户端实例,语义等同于 `new ZentaoClient(options)`,便于链式调用。
111
+ *
112
+ * @param options - 客户端配置,参见 {@link ZentaoClientOptions}。
113
+ * @returns 新建的客户端实例。
114
+ * @throws {ZentaoError} 同 {@link ZentaoClient | 构造函数}:`E_INVALID_BASE_URL` 等。
115
+ */
33
116
  static create(options: ZentaoClientOptions): ZentaoClient;
34
- /** 创建客户端并写入全局选项,供高阶 `request()` 默认使用。 */
117
+ /**
118
+ * 创建客户端并写入全局选项,作为 {@link request} 默认使用的实例。
119
+ *
120
+ * 适合应用入口处一次性完成初始化,后续 `request("module/action", params)` 可省略 `options.client`。
121
+ * 多次调用会覆盖上一次的全局客户端。
122
+ *
123
+ * @param options - 客户端配置,参见 {@link ZentaoClientOptions}。
124
+ * @returns 新建并已注册为全局默认的客户端实例。
125
+ * @throws {ZentaoError} 同 {@link ZentaoClient | 构造函数}:`E_INVALID_BASE_URL` 等。
126
+ */
35
127
  static init(options: ZentaoClientOptions): ZentaoClient;
36
- /** 根据本地持久化 profile 创建客户端;不传 key 时使用当前 profile。 */
128
+ /**
129
+ * 根据本地持久化 profile 创建客户端。
130
+ *
131
+ * 实际会调用 {@link switchProfile}:若 `profileKey` 存在则刷新其 `lastUsedTime` 并设为当前 profile;
132
+ * 不传 `profileKey` 时使用当前 profile。Profile 中保存的 `timeout` / `insecure` 偏好也会被带回到客户端实例。
133
+ *
134
+ * @param profileKey - 可选的 profile key,格式为 `account@server`;不传时使用当前 profile。
135
+ * @returns 用 profile 还原后的客户端实例。
136
+ * @throws {ZentaoError} `E_NO_PROFILE`(无任何 profile 且未传 key)、`E_PROFILE_NOT_FOUND`(指定 key 不存在)、
137
+ * `E_PROFILE_STORAGE_UNAVAILABLE`(运行时无法访问持久化存储)。
138
+ */
37
139
  static fromProfile(profileKey?: string): Promise<ZentaoClient>;
38
140
  }
39
- export declare function createClient(options: ZentaoClientOptions): ZentaoClient;
@@ -27,11 +27,23 @@ async function parseResponse(response) {
27
27
  return text;
28
28
  }
29
29
  }
30
- /** 禅道 API 客户端,负责 Token 注入、请求超时、TLS 选项和响应解析。 */
30
+ /**
31
+ * 禅道 API 客户端,封装一次次原始 HTTP 调用。
32
+ *
33
+ * 主要职责:
34
+ * - 站点根地址规范化与 `/api.php/v2` 拼接
35
+ * - 自动注入 `Token` 头
36
+ * - 请求超时控制(基于 {@link AbortController})
37
+ * - 可选的 TLS 跳过校验(仅 Node.js 运行时)
38
+ * - 响应体的 JSON 解析与错误归一化
39
+ *
40
+ * 适合直接调用裸 API;若希望按模块/动作名调用并自动组装路径、参数、分页,
41
+ * 请改用 {@link request}。
42
+ */
31
43
  export class ZentaoClient {
32
44
  /** 禅道站点根地址,不包含 `/api.php/v2`。 */
33
45
  siteUrl;
34
- /** 禅道 API v2 根地址。 */
46
+ /** 禅道 API v2 根地址,等于 `siteUrl + '/api.php/v2'`。 */
35
47
  baseUrl;
36
48
  token;
37
49
  timeout;
@@ -47,8 +59,19 @@ export class ZentaoClient {
47
59
  /**
48
60
  * 发起一次原始 API 请求。
49
61
  *
50
- * 默认使用 GET;当服务端返回 `{ status: "fail" }` 时仍按原始内容返回,
51
- * 只有 HTTP/网络/超时等传输层错误会抛出 {@link ZentaoError}。
62
+ * 选项优先级:本次调用 `options` > 全局选项({@link getGlobalOptions} > 客户端构造时默认值。
63
+ *
64
+ * 特殊处理:
65
+ * - 默认 HTTP 方法为 `GET`,`GET` 请求即使提供了 `options.body` 也不会发送,避免被部分代理/浏览器拒绝。
66
+ * - 非空响应优先按 JSON 解析;解析失败时回落为字符串原文。
67
+ * - 业务层失败(即响应体 `{ status: "fail" }`)不会抛出,仍按原样返回;只有 HTTP/网络/超时等传输层错误才会抛错。
68
+ * - `insecure` 仅在 Node.js 下可用,浏览器中传入会抛 `E_INSECURE_BROWSER`。
69
+ *
70
+ * @param path - 相对 {@link baseUrl} 的路径,可省略前导 `/`。
71
+ * @param options - 单次请求选项,参见 {@link ClientRequestOptions}。
72
+ * @returns 解析后的响应体;当响应为空字符串时返回 `undefined`。
73
+ * @throws {ZentaoError} 可能抛出 `E_HTTP_ERROR`(非 2xx 状态)、`E_NETWORK_ERROR`(底层 fetch 失败)、
74
+ * `E_TIMEOUT`(超过 `timeout`)或 `E_INSECURE_BROWSER`(浏览器中开启了 `insecure`)。
52
75
  */
53
76
  async request(path, options = {}) {
54
77
  const globals = getGlobalOptions();
@@ -59,9 +82,7 @@ export class ZentaoClient {
59
82
  const url = buildUrl(this.baseUrl, path, options.query);
60
83
  const controller = new AbortController();
61
84
  const timer = setTimeout(() => controller.abort(), timeout);
62
- const headers = {
63
- 'Content-Type': 'application/json',
64
- };
85
+ const headers = {};
65
86
  if (this.token) {
66
87
  headers.Token = this.token;
67
88
  }
@@ -71,7 +92,8 @@ export class ZentaoClient {
71
92
  signal: controller.signal,
72
93
  };
73
94
  // GET 请求不携带 body,避免浏览器和部分代理拒绝请求。
74
- if (options.body && method !== 'GET') {
95
+ if (options.body !== undefined && method !== 'GET') {
96
+ headers['Content-Type'] = 'application/json';
75
97
  init.body = JSON.stringify(options.body);
76
98
  }
77
99
  try {
@@ -101,23 +123,66 @@ export class ZentaoClient {
101
123
  clearTimeout(timer);
102
124
  }
103
125
  }
104
- /** 发起 GET 请求并按调用方指定类型返回。 */
126
+ /**
127
+ * 发起 `GET` 请求。
128
+ *
129
+ * @typeParam T - 期望的响应体类型;调用方负责类型收窄,SDK 不做运行时校验。
130
+ * @param path - 相对 {@link baseUrl} 的路径。
131
+ * @returns 解析后的响应体(强转为 `T`)。
132
+ * @throws {ZentaoError} 传输层失败时抛出,详见 {@link ZentaoClient.request}。
133
+ */
105
134
  async get(path) {
106
135
  return this.request(path, { method: 'GET' });
107
136
  }
108
- /** 发起 POST 请求并发送 JSON body。 */
137
+ /**
138
+ * 发起 `POST` 请求,`body` 会被序列化为 JSON。
139
+ *
140
+ * @typeParam T - 期望的响应体类型。
141
+ * @param path - 相对 {@link baseUrl} 的路径。
142
+ * @param body - JSON 请求体,传入对象/数组将被 `JSON.stringify`。
143
+ * @returns 解析后的响应体(强转为 `T`)。
144
+ * @throws {ZentaoError} 传输层失败时抛出,详见 {@link ZentaoClient.request}。
145
+ */
109
146
  async post(path, body) {
110
147
  return this.request(path, { method: 'POST', body });
111
148
  }
112
- /** 发起 PUT 请求并发送 JSON body。 */
149
+ /**
150
+ * 发起 `PUT` 请求,`body` 会被序列化为 JSON。
151
+ *
152
+ * @typeParam T - 期望的响应体类型。
153
+ * @param path - 相对 {@link baseUrl} 的路径。
154
+ * @param body - JSON 请求体。
155
+ * @returns 解析后的响应体(强转为 `T`)。
156
+ * @throws {ZentaoError} 传输层失败时抛出,详见 {@link ZentaoClient.request}。
157
+ */
113
158
  async put(path, body) {
114
159
  return this.request(path, { method: 'PUT', body });
115
160
  }
116
- /** 发起 DELETE 请求。 */
161
+ /**
162
+ * 发起 `DELETE` 请求。
163
+ *
164
+ * @typeParam T - 期望的响应体类型。
165
+ * @param path - 相对 {@link baseUrl} 的路径。
166
+ * @returns 解析后的响应体(强转为 `T`)。
167
+ * @throws {ZentaoError} 传输层失败时抛出,详见 {@link ZentaoClient.request}。
168
+ */
117
169
  async delete(path) {
118
170
  return this.request(path, { method: 'DELETE' });
119
171
  }
120
- /** 使用账号密码登录,成功后把返回 Token 写入当前客户端实例。 */
172
+ /**
173
+ * 使用账号密码登录禅道。
174
+ *
175
+ * 成功后会把返回的 Token 写入当前客户端实例(后续请求自动带上 `Token` 头);
176
+ * 当全局 `persistProfiles` 为真时,会同时把账号、Token、用户信息、服务端配置和
177
+ * 客户端偏好(仅在显式设置过 `timeout` / `insecure` 时)持久化为本地 profile,
178
+ * 并切换为当前 profile,方便下次通过 {@link ZentaoClient.fromProfile} 直接登录态恢复。
179
+ *
180
+ * @param account - 禅道用户账号。
181
+ * @param password - 禅道用户密码(明文,仅在传输层 TLS 内使用)。
182
+ * @returns 登录成功后返回的 API Token。
183
+ * @throws {ZentaoError} `E_LOGIN_FAILED` —— 服务端返回 `status !== "success"` 或缺失 token;
184
+ * 也可能因底层 {@link ZentaoClient.request} 而抛出 HTTP/网络/超时错误。
185
+ */
121
186
  async login(account, password) {
122
187
  const response = await this.post('/users/login', { account, password });
123
188
  if (response.status !== 'success' || !response.token) {
@@ -144,17 +209,42 @@ export class ZentaoClient {
144
209
  }
145
210
  return response.token;
146
211
  }
147
- /** 创建客户端实例,语义等同于 `new ZentaoClient(options)`。 */
212
+ /**
213
+ * 创建客户端实例,语义等同于 `new ZentaoClient(options)`,便于链式调用。
214
+ *
215
+ * @param options - 客户端配置,参见 {@link ZentaoClientOptions}。
216
+ * @returns 新建的客户端实例。
217
+ * @throws {ZentaoError} 同 {@link ZentaoClient | 构造函数}:`E_INVALID_BASE_URL` 等。
218
+ */
148
219
  static create(options) {
149
220
  return new ZentaoClient(options);
150
221
  }
151
- /** 创建客户端并写入全局选项,供高阶 `request()` 默认使用。 */
222
+ /**
223
+ * 创建客户端并写入全局选项,作为 {@link request} 默认使用的实例。
224
+ *
225
+ * 适合应用入口处一次性完成初始化,后续 `request("module/action", params)` 可省略 `options.client`。
226
+ * 多次调用会覆盖上一次的全局客户端。
227
+ *
228
+ * @param options - 客户端配置,参见 {@link ZentaoClientOptions}。
229
+ * @returns 新建并已注册为全局默认的客户端实例。
230
+ * @throws {ZentaoError} 同 {@link ZentaoClient | 构造函数}:`E_INVALID_BASE_URL` 等。
231
+ */
152
232
  static init(options) {
153
233
  const client = new ZentaoClient(options);
154
234
  setGlobalOptions({ client });
155
235
  return client;
156
236
  }
157
- /** 根据本地持久化 profile 创建客户端;不传 key 时使用当前 profile。 */
237
+ /**
238
+ * 根据本地持久化 profile 创建客户端。
239
+ *
240
+ * 实际会调用 {@link switchProfile}:若 `profileKey` 存在则刷新其 `lastUsedTime` 并设为当前 profile;
241
+ * 不传 `profileKey` 时使用当前 profile。Profile 中保存的 `timeout` / `insecure` 偏好也会被带回到客户端实例。
242
+ *
243
+ * @param profileKey - 可选的 profile key,格式为 `account@server`;不传时使用当前 profile。
244
+ * @returns 用 profile 还原后的客户端实例。
245
+ * @throws {ZentaoError} `E_NO_PROFILE`(无任何 profile 且未传 key)、`E_PROFILE_NOT_FOUND`(指定 key 不存在)、
246
+ * `E_PROFILE_STORAGE_UNAVAILABLE`(运行时无法访问持久化存储)。
247
+ */
158
248
  static async fromProfile(profileKey) {
159
249
  // switchProfile 会在内部读取存储、校验 key 并刷新 lastUsedTime 后写回,
160
250
  // 若 key 不存在会抛出 E_PROFILE_NOT_FOUND;不传 key 时由 switchCurrentProfile 处理。
@@ -167,6 +257,3 @@ export class ZentaoClient {
167
257
  });
168
258
  }
169
259
  }
170
- export function createClient(options) {
171
- return ZentaoClient.create(options);
172
- }
package/dist/index.d.ts CHANGED
@@ -2,7 +2,7 @@ export { ZentaoClient } from './client/index.js';
2
2
  export { ERRORS, ZentaoError, type ErrorCode } from './misc/errors.js';
3
3
  export { getGlobalOptions, setGlobalOptions } from './misc/global-options.js';
4
4
  export { ZENTAO_PROFILES_STORAGE_KEY, addProfile, deleteProfile, getAllProfiles, getProfile, getProfileKey, switchProfile, } from './profiles/index.js';
5
- export { defineModuleActions, defineModules, type DefineModulesOptions, getModule, getModuleAction, } from './modules/registry.js';
5
+ export { defineModuleActions, defineModules, type DefineModulesOptions, getModuleNames, getModule, getModuleAction, } from './modules/registry.js';
6
6
  export { request } from './request/index.js';
7
7
  export { BUILD, VERSION } from './version.js';
8
8
  export type { ApiListResponse, ApiResponse, ClientRequestOptions, GlobalOptions, HttpMethod, ListPagerInfo, LoginResponse, ModuleAction, ModuleActionMethod, ModuleActionName, ModuleActionPagerGetterMap, ModuleActionParam, ModuleActionParamOption, ModuleActionRequestBody, ModuleActionResponse, ModuleActionResultRender, ModuleActionResultRenderType, ModuleActionResultType, ModuleActionType, ModuleDefinition, ModuleName, Pager, RequestOptions, ResolvedModuleCommand, ResponseData, ServerConfig, ZentaoProfile, ZentaoProfileConfig, ZentaoProfileRecord, ZentaoProfilesStore, ZentaoClientOptions, } from './types/index.js';
package/dist/index.js CHANGED
@@ -2,6 +2,6 @@ export { ZentaoClient } from './client/index.js';
2
2
  export { ERRORS, ZentaoError } from './misc/errors.js';
3
3
  export { getGlobalOptions, setGlobalOptions } from './misc/global-options.js';
4
4
  export { ZENTAO_PROFILES_STORAGE_KEY, addProfile, deleteProfile, getAllProfiles, getProfile, getProfileKey, switchProfile, } from './profiles/index.js';
5
- export { defineModuleActions, defineModules, getModule, getModuleAction, } from './modules/registry.js';
5
+ export { defineModuleActions, defineModules, getModuleNames, getModule, getModuleAction, } from './modules/registry.js';
6
6
  export { request } from './request/index.js';
7
7
  export { BUILD, VERSION } from './version.js';
@@ -3,9 +3,11 @@ import { ZentaoError } from './errors.js';
3
3
  export function isNodeRuntime() {
4
4
  return typeof process !== 'undefined' && Boolean(process.versions?.node);
5
5
  }
6
- async function importNodeModule(specifier) {
7
- const dynamicImport = new Function('specifier', 'return import(specifier)');
8
- return dynamicImport(specifier);
6
+ // 通过函数参数间接化 `import(specifier)`,让打包器无法在静态分析阶段把
7
+ // `node:*` 拉进浏览器 bundle;同时不依赖 `new Function`/`eval`,
8
+ // 在严格 CSP 下也能正常加载。
9
+ function importNodeModule(specifier) {
10
+ return import(specifier);
9
11
  }
10
12
  function toNodeRequestHeaders(headers) {
11
13
  const result = {};
@@ -1,3 +1,9 @@
1
+ /**
2
+ * SDK 已知错误码到默认消息的映射表。
3
+ *
4
+ * 每条消息允许带 `{key}` 占位符,由 {@link ZentaoError} 构造时使用 `replacements`
5
+ * 进行字面量替换。此对象使用 `as const`,可直接作为类型来源约束错误码。
6
+ */
1
7
  export declare const ERRORS: {
2
8
  readonly E_INVALID_BASE_URL: "Invalid ZenTao baseUrl.";
3
9
  readonly E_NO_GLOBAL_CLIENT: "No global client configured. Call ZentaoClient.init() or pass options.client.";
@@ -16,15 +22,30 @@ export declare const ERRORS: {
16
22
  readonly E_INVALID_MODULE_DEFINITION: "Invalid module definition.";
17
23
  readonly E_INVALID_ACTION_DEFINITION: "Invalid module action definition.";
18
24
  readonly E_MISSING_PARAM: "Missing required parameter: {param}";
25
+ readonly E_INVALID_PARAM: "Invalid value for parameter {param}: {value}";
19
26
  readonly E_INVALID_REQUEST_NAME: "Request name must use the form \"moduleName/methodName\".";
27
+ readonly E_API_FAILED: "ZenTao API returned failure: {message}";
20
28
  };
29
+ /** SDK 已知错误码,对应 {@link ERRORS} 的 key。 */
21
30
  export type ErrorCode = keyof typeof ERRORS;
22
- /** SDK 统一错误类型,所有可预期错误都会携带稳定错误码。 */
31
+ /**
32
+ * SDK 统一错误类型。
33
+ *
34
+ * 所有可预期错误(参数缺失、HTTP/网络/超时、登录失败、profile 异常等)都通过
35
+ * `ZentaoError` 抛出并携带稳定 {@link ErrorCode},方便调用方按 `code` 区分处理。
36
+ * 错误消息默认来自 {@link ERRORS} 中的模板,并支持占位符替换。
37
+ */
23
38
  export declare class ZentaoError extends Error {
24
- /** 错误码,对应 {@link ERRORS} 的 key */
39
+ /** 错误码,对应 {@link ERRORS} 的 key;用于稳定地区分错误类型。 */
25
40
  readonly code: ErrorCode;
26
- /** 附加上下文,例如 HTTP 响应详情或原始异常。 */
41
+ /** 附加上下文,例如 HTTP 响应详情、原始异常或失败的禅道响应原文。 */
27
42
  readonly details?: unknown;
28
- /** 根据错误码和占位符替换值创建错误。 */
43
+ /**
44
+ * 构造 SDK 错误实例。
45
+ *
46
+ * @param code - 错误码,必须是 {@link ERRORS} 中已声明的 key。
47
+ * @param replacements - 可选的占位符替换值;遍历后会把 `{key}` 替换为字符串化的值。
48
+ * @param details - 可选的附加上下文(HTTP 响应详情、原始异常等),保存到 {@link details}。
49
+ */
29
50
  constructor(code: ErrorCode, replacements?: Record<string, string | number>, details?: unknown);
30
51
  }
@@ -1,3 +1,9 @@
1
+ /**
2
+ * SDK 已知错误码到默认消息的映射表。
3
+ *
4
+ * 每条消息允许带 `{key}` 占位符,由 {@link ZentaoError} 构造时使用 `replacements`
5
+ * 进行字面量替换。此对象使用 `as const`,可直接作为类型来源约束错误码。
6
+ */
1
7
  export const ERRORS = {
2
8
  E_INVALID_BASE_URL: 'Invalid ZenTao baseUrl.',
3
9
  E_NO_GLOBAL_CLIENT: 'No global client configured. Call ZentaoClient.init() or pass options.client.',
@@ -16,15 +22,29 @@ export const ERRORS = {
16
22
  E_INVALID_MODULE_DEFINITION: 'Invalid module definition.',
17
23
  E_INVALID_ACTION_DEFINITION: 'Invalid module action definition.',
18
24
  E_MISSING_PARAM: 'Missing required parameter: {param}',
25
+ E_INVALID_PARAM: 'Invalid value for parameter {param}: {value}',
19
26
  E_INVALID_REQUEST_NAME: 'Request name must use the form "moduleName/methodName".',
27
+ E_API_FAILED: 'ZenTao API returned failure: {message}',
20
28
  };
21
- /** SDK 统一错误类型,所有可预期错误都会携带稳定错误码。 */
29
+ /**
30
+ * SDK 统一错误类型。
31
+ *
32
+ * 所有可预期错误(参数缺失、HTTP/网络/超时、登录失败、profile 异常等)都通过
33
+ * `ZentaoError` 抛出并携带稳定 {@link ErrorCode},方便调用方按 `code` 区分处理。
34
+ * 错误消息默认来自 {@link ERRORS} 中的模板,并支持占位符替换。
35
+ */
22
36
  export class ZentaoError extends Error {
23
- /** 错误码,对应 {@link ERRORS} 的 key */
37
+ /** 错误码,对应 {@link ERRORS} 的 key;用于稳定地区分错误类型。 */
24
38
  code;
25
- /** 附加上下文,例如 HTTP 响应详情或原始异常。 */
39
+ /** 附加上下文,例如 HTTP 响应详情、原始异常或失败的禅道响应原文。 */
26
40
  details;
27
- /** 根据错误码和占位符替换值创建错误。 */
41
+ /**
42
+ * 构造 SDK 错误实例。
43
+ *
44
+ * @param code - 错误码,必须是 {@link ERRORS} 中已声明的 key。
45
+ * @param replacements - 可选的占位符替换值;遍历后会把 `{key}` 替换为字符串化的值。
46
+ * @param details - 可选的附加上下文(HTTP 响应详情、原始异常等),保存到 {@link details}。
47
+ */
28
48
  constructor(code, replacements, details) {
29
49
  let message = ERRORS[code];
30
50
  if (replacements) {
@@ -1,5 +1,18 @@
1
1
  import type { GlobalOptions } from '../types/index.js';
2
- /** 获取当前全局选项快照;返回副本,避免调用方直接改写内部状态。 */
2
+ /**
3
+ * 获取当前全局选项的快照。
4
+ *
5
+ * 返回的是浅拷贝副本,对返回值的修改不会影响内部状态;如需更新请使用 {@link setGlobalOptions}。
6
+ *
7
+ * @returns 当前生效的全局选项快照,参见 {@link GlobalOptions}。
8
+ */
3
9
  export declare function getGlobalOptions(): GlobalOptions;
4
- /** 合并设置全局选项;传入 `undefined` 可清空对应字段。 */
10
+ /**
11
+ * 以浅合并的方式更新全局选项。
12
+ *
13
+ * 仅覆盖传入 `options` 中显式声明的字段;其余字段保留原值。若希望清空某个字段,
14
+ * 显式传入 `undefined` 即可(会写入 `undefined` 并在后续读取时返回 `undefined`)。
15
+ *
16
+ * @param options - 要合并到全局选项中的字段子集。
17
+ */
5
18
  export declare function setGlobalOptions(options: Partial<GlobalOptions>): void;
@@ -1,9 +1,22 @@
1
1
  let globalOptions = {};
2
- /** 获取当前全局选项快照;返回副本,避免调用方直接改写内部状态。 */
2
+ /**
3
+ * 获取当前全局选项的快照。
4
+ *
5
+ * 返回的是浅拷贝副本,对返回值的修改不会影响内部状态;如需更新请使用 {@link setGlobalOptions}。
6
+ *
7
+ * @returns 当前生效的全局选项快照,参见 {@link GlobalOptions}。
8
+ */
3
9
  export function getGlobalOptions() {
4
10
  return { ...globalOptions };
5
11
  }
6
- /** 合并设置全局选项;传入 `undefined` 可清空对应字段。 */
12
+ /**
13
+ * 以浅合并的方式更新全局选项。
14
+ *
15
+ * 仅覆盖传入 `options` 中显式声明的字段;其余字段保留原值。若希望清空某个字段,
16
+ * 显式传入 `undefined` 即可(会写入 `undefined` 并在后续读取时返回 `undefined`)。
17
+ *
18
+ * @param options - 要合并到全局选项中的字段子集。
19
+ */
7
20
  export function setGlobalOptions(options) {
8
21
  globalOptions = { ...globalOptions, ...options };
9
22
  }
@@ -2,21 +2,84 @@ import type { ModuleAction, ModuleDefinition } from '../types/index.js';
2
2
  import { BUILTIN_MODULES } from './generated.js';
3
3
  export { BUILTIN_MODULES };
4
4
  export declare const MODULES: ModuleDefinition[];
5
+ /** {@link defineModules} 的选项。 */
5
6
  export interface DefineModulesOptions {
6
- /** 同名模块是否整体替换;默认合并模块定义和动作。 */
7
+ /**
8
+ * 同名模块的写入策略。
9
+ *
10
+ * - `false`(默认):合并模块的元数据,并按动作名对动作做"同名替换、未知追加"。
11
+ * - `true`:整体替换已存在的模块定义,原有动作会被丢弃。
12
+ */
7
13
  replace?: boolean;
8
14
  }
9
- /** 定义或扩展模块;同名模块默认合并动作,`replace` 为真时整体替换,未知模块追加。 */
15
+ /**
16
+ * 注册或扩展模块定义。
17
+ *
18
+ * 行为细节:
19
+ * - 模块名匹配大小写不敏感。
20
+ * - 未知模块直接追加到注册表末尾。
21
+ * - 已存在的模块默认按 `mergeModule` 合并:模块元数据浅合并、动作按名同名替换/未知追加;
22
+ * `options.replace` 为 `true` 时整体替换。
23
+ * - 所有写入都会做深克隆 + 深冻结:调用方后续修改自己的对象不会污染注册表,注册表也不可被外部改写。
24
+ *
25
+ * @param input - 单个或一组模块定义。
26
+ * @param options - 写入策略,参见 {@link DefineModulesOptions}。
27
+ * @throws {ZentaoError} `E_INVALID_MODULE_DEFINITION` —— 缺少 `name` 或 `actions` 字段。
28
+ */
10
29
  export declare function defineModules(input: ModuleDefinition | ModuleDefinition[], options?: DefineModulesOptions): void;
11
- /** 为已存在模块定义或覆盖动作;同名动作替换,未知动作追加。 */
30
+ /**
31
+ * 为已存在的模块追加或覆盖动作。
32
+ *
33
+ * 不做深度合并:同名动作整体替换,未知动作追加。这避免在 schema、参数数组等字段上出现隐式合并规则。
34
+ *
35
+ * @param moduleName - 目标模块名(大小写不敏感)。
36
+ * @param input - 单个或一组动作定义。
37
+ * @throws {ZentaoError} `E_INVALID_MODULE`(模块未注册)或 `E_INVALID_ACTION_DEFINITION`
38
+ * (动作缺少 `name` / `path` / `method` 等必填字段)。
39
+ */
12
40
  export declare function defineModuleActions(moduleName: string, input: ModuleAction | ModuleAction[]): void;
13
- /** 获取模块定义;模块不存在时抛出 {@link ZentaoError}。 */
41
+ /**
42
+ * 获取模块定义。
43
+ *
44
+ * 模块名匹配大小写不敏感。返回值是注册表内部的已深冻结引用(O(1) 查询、零拷贝),
45
+ * 任何写入尝试在严格模式下会抛 `TypeError`;如需修改请使用 {@link defineModules}。
46
+ *
47
+ * @param moduleName - 模块名。
48
+ * @returns 已注册的模块定义。
49
+ * @throws {ZentaoError} `E_INVALID_MODULE` —— 模块未注册。
50
+ */
14
51
  export declare function getModule(moduleName: string): ModuleDefinition;
15
- /** 获取指定模块动作;`ls` 会作为 `list` 的别名处理。 */
52
+ /**
53
+ * 获取指定模块下的某个动作。
54
+ *
55
+ * 解析顺序:
56
+ * 1. `actionName === 'ls'` 时映射为 `list`(仅作为别名,不会修改注册表)。
57
+ * 2. 在该模块的动作中按名称大小写不敏感匹配。
58
+ * 3. 当请求的动作不是基础 CRUD(`list`/`get`/`create`/`update`/`delete`)时,
59
+ * 额外允许命中 `type === 'action'` 的自定义动作(即使名字不在基础 CRUD 中)。
60
+ *
61
+ * 返回值同样是已深冻结的引用,请勿尝试修改。
62
+ *
63
+ * @param moduleName - 模块名(大小写不敏感)。
64
+ * @param actionName - 动作名(大小写不敏感);支持 `ls` 作为 `list` 的别名。
65
+ * @returns 匹配到的动作定义。
66
+ * @throws {ZentaoError} `E_INVALID_MODULE`(模块未注册)或 `E_INVALID_ACTION`(动作不存在)。
67
+ */
16
68
  export declare function getModuleAction(moduleName: string, actionName: string): ModuleAction;
17
- /** 返回当前运行时注册表中的所有模块名。 */
69
+ /**
70
+ * 返回当前运行时注册表中的所有模块名。
71
+ *
72
+ * 顺序与模块写入注册表的顺序一致;包括内置模块和通过 {@link defineModules} 追加的用户模块。
73
+ *
74
+ * @returns 模块名数组(保留原始大小写)。
75
+ */
18
76
  export declare function getModuleNames(): string[];
19
- /** 判断模块名是否已注册,大小写不敏感。 */
77
+ /**
78
+ * 判断模块名是否已注册。
79
+ *
80
+ * @param moduleName - 模块名;匹配大小写不敏感。
81
+ * @returns 已注册返回 `true`,否则 `false`。
82
+ */
20
83
  export declare function isModuleName(moduleName: string): boolean;
21
84
  /** @internal */
22
85
  export declare function resetModuleDefinitions(): void;