zentao-api 0.6.5 → 0.6.7
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/README.md +29 -2
- package/dist/browser/zentao-api.global.js +26 -18
- package/dist/client/index.d.ts +30 -3
- package/dist/client/index.js +303 -27
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/misc/environment.d.ts +0 -2
- package/dist/misc/environment.js +43 -19
- package/dist/misc/errors.d.ts +6 -0
- package/dist/misc/errors.js +9 -0
- package/dist/misc/zentao-version.d.ts +11 -0
- package/dist/misc/zentao-version.js +35 -0
- package/dist/modules/define.d.ts +4 -3
- package/dist/modules/define.js +4 -3
- package/dist/modules/generated.d.ts +2240 -59
- package/dist/modules/generated.js +1230 -50
- package/dist/modules/override.d.ts +2 -0
- package/dist/modules/override.js +39 -66
- package/dist/modules/query.d.ts +13 -10
- package/dist/modules/query.js +30 -26
- package/dist/modules/registry-store.js +3 -0
- package/dist/modules/registry.d.ts +0 -9702
- package/dist/modules/registry.js +0 -4
- package/dist/modules/resolve.js +1 -1
- package/dist/profiles/index.d.ts +3 -1
- package/dist/profiles/index.js +13 -4
- package/dist/request/index.d.ts +15 -4
- package/dist/request/index.js +39 -7
- package/dist/types/client.d.ts +8 -3
- package/dist/types/module.d.ts +13 -2
- package/dist/types/options.d.ts +9 -0
- package/dist/types/profile.d.ts +2 -0
- package/dist/types/response.d.ts +9 -0
- package/dist/utils/doc-helper/index.d.ts +1 -2
- package/dist/utils/doc-helper/index.js +0 -1
- package/dist/utils/doc-helper/inline.d.ts +0 -1
- package/dist/utils/doc-helper/inline.js +3 -81
- package/dist/utils/doc-helper/shared.d.ts +0 -5
- package/dist/utils/doc-helper/shared.js +0 -34
- package/dist/utils/doc-helper/types.d.ts +1 -13
- package/dist/version.js +2 -2
- package/package.json +9 -8
- package/dist/utils/doc-helper/html.d.ts +0 -3
- package/dist/utils/doc-helper/html.js +0 -442
package/dist/modules/registry.js
CHANGED
|
@@ -4,13 +4,9 @@
|
|
|
4
4
|
// - ./define —— 定义/写入模块(defineModules / defineModuleActions / resetModuleDefinitions)。
|
|
5
5
|
// - ./override —— 内置覆盖/扩展(基于 define,随 SDK 发布并在加载/重置时自动应用)。
|
|
6
6
|
// - ./query —— 获取模块信息(getModule / getModuleAction / getModuleNames / isModuleName)。
|
|
7
|
-
import { BUILTIN_MODULES } from './generated.js';
|
|
8
7
|
import { applyBuiltinOverrides } from './override.js';
|
|
9
8
|
import { setPostResetHook } from './registry-store.js';
|
|
10
|
-
export { BUILTIN_MODULES };
|
|
11
|
-
export const MODULES = BUILTIN_MODULES;
|
|
12
9
|
export { defineModules, defineModuleActions, extendModuleAction, resetModuleDefinitions, } from './define.js';
|
|
13
|
-
export { applyBuiltinOverrides };
|
|
14
10
|
// 内置覆盖接线:避免 define ↔ override 之间的循环依赖。
|
|
15
11
|
// - 加载时应用一次;
|
|
16
12
|
// - 注册为 store 的「重置后钩子」,使 resetModuleDefinitions 还原内置基线后自动重新应用。
|
package/dist/modules/resolve.js
CHANGED
|
@@ -162,7 +162,7 @@ function buildQuery(action, params) {
|
|
|
162
162
|
value = params.page;
|
|
163
163
|
}
|
|
164
164
|
if (value === undefined) {
|
|
165
|
-
value = param.defaultValue
|
|
165
|
+
value = param.defaultValue;
|
|
166
166
|
}
|
|
167
167
|
if (value === undefined && param.required) {
|
|
168
168
|
throw new ZentaoError('E_MISSING_PARAM', { param: param.name });
|
package/dist/profiles/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ZentaoProfile, ZentaoProfileRecord } from '../types/index.js';
|
|
1
|
+
import type { ServerConfig, ZentaoProfile, ZentaoProfileRecord } from '../types/index.js';
|
|
2
2
|
/**
|
|
3
3
|
* 浏览器环境下用于在 `localStorage` 中保存 profile 数据的 key。
|
|
4
4
|
*
|
|
@@ -50,6 +50,8 @@ export declare function getProfile(profileKey?: string): Promise<ZentaoProfileRe
|
|
|
50
50
|
* `E_INVALID_BASE_URL`、`E_PROFILE_STORAGE_INVALID`、`E_PROFILE_STORAGE_UNAVAILABLE`。
|
|
51
51
|
*/
|
|
52
52
|
export declare function addProfile(profile: ZentaoProfile): Promise<ZentaoProfileRecord>;
|
|
53
|
+
/** 只刷新已有 profile 的服务器配置,不切换当前账号或重建已删除的记录。 @internal */
|
|
54
|
+
export declare function updateProfileServerConfig(profileKey: string, serverConfig: ServerConfig, fetchedAt: string): Promise<void>;
|
|
53
55
|
/**
|
|
54
56
|
* 删除指定 profile。
|
|
55
57
|
*
|
package/dist/profiles/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ZentaoError } from '../misc/errors.js';
|
|
2
2
|
import { isNodeRuntime } from '../misc/environment.js';
|
|
3
|
-
import { normalizeSiteUrl } from '../utils/index.js';
|
|
3
|
+
import { isRecord, normalizeSiteUrl } from '../utils/index.js';
|
|
4
4
|
/**
|
|
5
5
|
* 浏览器环境下用于在 `localStorage` 中保存 profile 数据的 key。
|
|
6
6
|
*
|
|
@@ -8,9 +8,6 @@ import { normalizeSiteUrl } from '../utils/index.js';
|
|
|
8
8
|
*/
|
|
9
9
|
export const ZENTAO_PROFILES_STORAGE_KEY = 'ZENTAO_PROFILES';
|
|
10
10
|
const PROFILE_FILE_PARTS = ['.config', 'zentao', 'zentao.json'];
|
|
11
|
-
function isRecord(value) {
|
|
12
|
-
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
13
|
-
}
|
|
14
11
|
function cloneJson(value) {
|
|
15
12
|
if (value === undefined)
|
|
16
13
|
return value;
|
|
@@ -241,6 +238,18 @@ export function addProfile(profile) {
|
|
|
241
238
|
return toRecord(normalized);
|
|
242
239
|
});
|
|
243
240
|
}
|
|
241
|
+
/** 只刷新已有 profile 的服务器配置,不切换当前账号或重建已删除的记录。 @internal */
|
|
242
|
+
export function updateProfileServerConfig(profileKey, serverConfig, fetchedAt) {
|
|
243
|
+
return withStoreMutex(async () => {
|
|
244
|
+
const store = await readStore();
|
|
245
|
+
const profile = findProfile(store, profileKey);
|
|
246
|
+
if (!profile)
|
|
247
|
+
return;
|
|
248
|
+
profile.serverConfig = cloneJson(serverConfig);
|
|
249
|
+
profile.serverConfigFetchedAt = fetchedAt;
|
|
250
|
+
await writeStore(store);
|
|
251
|
+
});
|
|
252
|
+
}
|
|
244
253
|
/**
|
|
245
254
|
* 删除指定 profile。
|
|
246
255
|
*
|
package/dist/request/index.d.ts
CHANGED
|
@@ -27,17 +27,28 @@ export type RequestResultFor<Name extends BuiltinRequestName> = ActionMetaOf<Nam
|
|
|
27
27
|
* 选项优先级为:本次调用 options > 全局 options > 客户端默认值。
|
|
28
28
|
* 当响应 `status` 为 `"fail"` 时,默认按原样返回;若 `options.throwOnFail`
|
|
29
29
|
* 或全局 `throwOnFail` 为真,则改为抛出 `E_API_FAILED`。
|
|
30
|
+
* 请求前按 Action 的 `minVersion` 检查服务器版本;全局 `version` 可避免配置探测,
|
|
31
|
+
* `forceRefreshConfig` 则强制使用实际版本。无法获取配置时默认抛错,
|
|
32
|
+
* `skipVersionCheckOnConfigError` 可允许本次跳过检查,但不能忽略明确的版本不匹配。
|
|
30
33
|
*
|
|
31
34
|
* 对 `update` 动作,当 `options.autoFill` 或全局 `autoFill` 为真时,会先 GET 当前对象,
|
|
32
35
|
* 用现值补齐用户未显式传入的 body 字段后再 PUT,避免禅道覆盖未提交字段。详见 {@link RequestOptions.autoFill}。
|
|
33
36
|
*
|
|
34
|
-
* @typeParam T
|
|
37
|
+
* @typeParam T 归一化响应中期望的 `data` 字段类型;不传时为 `unknown`,调用方需要自行收窄。
|
|
35
38
|
* @param name - 请求名,例如 `product`、`product/list` 或 `product/1`。
|
|
36
39
|
* @param params - 请求参数。
|
|
37
40
|
* @param options - 请求选项。
|
|
38
|
-
* @returns
|
|
41
|
+
* @returns 默认返回归一化后的禅道 API 响应;`options.raw` 为 true 时返回未经归一化的原始响应。
|
|
39
42
|
* @throws {ZentaoError} 传输层错误、参数缺失或 `throwOnFail` 启用时的业务失败。
|
|
40
43
|
*/
|
|
41
|
-
export declare function request
|
|
42
|
-
|
|
44
|
+
export declare function request(name: string, params: Record<string, unknown> | undefined, options: RequestOptions & {
|
|
45
|
+
raw: true;
|
|
46
|
+
}): Promise<unknown>;
|
|
47
|
+
export declare function request<Name extends BuiltinRequestName>(name: Name, params?: RequestParamsFor<Name>, options?: RequestOptions & {
|
|
48
|
+
raw?: false;
|
|
49
|
+
}): Promise<ResponseData<RequestResultFor<Name>>>;
|
|
50
|
+
export declare function request<T = unknown>(name: string, params?: Record<string, unknown>, options?: RequestOptions & {
|
|
51
|
+
raw?: false;
|
|
52
|
+
}): Promise<ResponseData<T>>;
|
|
53
|
+
export declare function request(name: string, params?: Record<string, unknown>, options?: RequestOptions): Promise<unknown>;
|
|
43
54
|
export {};
|
package/dist/request/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { ZentaoError } from '../misc/errors.js';
|
|
1
|
+
import { isZentaoConfigFetchError, ZentaoError } from '../misc/errors.js';
|
|
2
|
+
import { parseZentaoVersion, supportsZentaoVersion } from '../misc/zentao-version.js';
|
|
2
3
|
import { getGlobalOptions } from '../misc/global-options.js';
|
|
3
4
|
import { getModule, getModuleAction } from '../modules/registry.js';
|
|
4
5
|
import { extractPager, extractResult, resolveActionRequest } from '../modules/resolve.js';
|
|
@@ -50,24 +51,24 @@ function getExplicitDataKeys(data) {
|
|
|
50
51
|
/**
|
|
51
52
|
* 在执行 `update` 动作前,用当前对象的现值填充用户未显式传入的 body 字段。
|
|
52
53
|
*
|
|
53
|
-
*
|
|
54
|
+
* 仅当模块存在与 update 路径相同的 `type: 'get'` 动作,且 update 动作声明了对象类型 body schema 时生效;
|
|
54
55
|
* 否则原样返回参数。GET 返回失败状态时会抛出 `E_API_FAILED`,避免继续发送未补齐的 PUT;
|
|
55
56
|
* GET 成功但返回非对象时跳过填充,交由后续 PUT 正常处理。
|
|
56
57
|
*
|
|
57
58
|
* 字段归属判断同时覆盖平铺 `params` 字段与 `params.data` 中的字段;只有 schema 中声明、
|
|
58
59
|
* 用户未传且当前对象存在的字段才会被补齐,避免覆盖用户本次想修改的字段。
|
|
59
60
|
*/
|
|
60
|
-
async function autoFillUpdateParams(module, action, params, options) {
|
|
61
|
+
async function autoFillUpdateParams(module, action, params, options, version) {
|
|
61
62
|
const properties = action.requestBody?.schema?.properties;
|
|
62
|
-
const getAction = module.actions.find((candidate) => candidate.type === 'get');
|
|
63
|
+
const getAction = module.actions.find((candidate) => candidate.type === 'get' && candidate.path === action.path);
|
|
63
64
|
if (!properties || !getAction)
|
|
64
65
|
return params;
|
|
65
|
-
const current = (await
|
|
66
|
+
const current = (await requestInternal(`${module.name}/${getAction.name}`, params, {
|
|
66
67
|
client: options.client,
|
|
67
68
|
timeout: options.timeout,
|
|
68
69
|
insecure: options.insecure,
|
|
69
70
|
throwOnFail: true,
|
|
70
|
-
})).data;
|
|
71
|
+
}, version)).data;
|
|
71
72
|
if (!isRecord(current))
|
|
72
73
|
return params;
|
|
73
74
|
const explicitDataKeys = getExplicitDataKeys(params.data);
|
|
@@ -172,6 +173,10 @@ function normalizeResponse(command, raw, options) {
|
|
|
172
173
|
return response;
|
|
173
174
|
}
|
|
174
175
|
export async function request(name, params = {}, options = {}) {
|
|
176
|
+
return requestInternal(name, params, options);
|
|
177
|
+
}
|
|
178
|
+
/** version 为 undefined 时解析一次,null 表示本次调用已选择跳过版本检查。 */
|
|
179
|
+
async function requestInternal(name, params, options, version) {
|
|
175
180
|
const globals = getGlobalOptions();
|
|
176
181
|
const client = options.client ?? globals.client;
|
|
177
182
|
if (!client) {
|
|
@@ -195,8 +200,35 @@ export async function request(name, params = {}, options = {}) {
|
|
|
195
200
|
if (!action) {
|
|
196
201
|
throw new ZentaoError('E_INVALID_ACTION', { module: moduleName, action: actionName });
|
|
197
202
|
}
|
|
203
|
+
if (version === undefined) {
|
|
204
|
+
if (!options.forceRefreshConfig && globals.version !== undefined) {
|
|
205
|
+
version = globals.version;
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
try {
|
|
209
|
+
version = (await client.getZentaoConfig({
|
|
210
|
+
forceRefresh: options.forceRefreshConfig,
|
|
211
|
+
timeout: options.timeout ?? globals.timeout,
|
|
212
|
+
insecure: options.insecure ?? globals.insecure,
|
|
213
|
+
})).version;
|
|
214
|
+
}
|
|
215
|
+
catch (error) {
|
|
216
|
+
if (!(options.skipVersionCheckOnConfigError ?? globals.skipVersionCheckOnConfigError)
|
|
217
|
+
|| !isZentaoConfigFetchError(error))
|
|
218
|
+
throw error;
|
|
219
|
+
version = null;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
if (version !== null && !supportsZentaoVersion(parseZentaoVersion(version), action.minVersion)) {
|
|
224
|
+
throw new ZentaoError('E_UNSUPPORTED_ZENTAO_VERSION', {
|
|
225
|
+
action: `${module.name}/${action.name}`,
|
|
226
|
+
version,
|
|
227
|
+
minVersion: action.minVersion.join(', '),
|
|
228
|
+
}, { action: `${module.name}/${action.name}`, version, minVersion: action.minVersion });
|
|
229
|
+
}
|
|
198
230
|
const finalParams = action.type === 'update' && (options.autoFill ?? globals.autoFill)
|
|
199
|
-
? await autoFillUpdateParams(module, action, mergedParams, options)
|
|
231
|
+
? await autoFillUpdateParams(module, action, mergedParams, { ...options, client }, version)
|
|
200
232
|
: mergedParams;
|
|
201
233
|
const command = resolveActionRequest(module, actionName, finalParams);
|
|
202
234
|
const preparedBody = await prepareActionBody(command, {
|
package/dist/types/client.d.ts
CHANGED
|
@@ -23,12 +23,12 @@ export interface ClientRequestOptions {
|
|
|
23
23
|
body?: unknown;
|
|
24
24
|
/** 请求体序列化方式。默认 `json`;传入 `FormData` 等原生 body 时会自动按 `raw` 处理。 */
|
|
25
25
|
bodyType?: ClientRequestBodyType;
|
|
26
|
-
/** 响应体解析方式。默认 `auto`,会优先尝试 JSON
|
|
26
|
+
/** 响应体解析方式。默认 `auto`,会优先尝试 JSON,失败后回落为文本;`response` 会让 signal 和 timeout 继续控制响应体。 */
|
|
27
27
|
responseType?: ClientResponseType;
|
|
28
28
|
/** 额外请求头;会与 SDK 自动注入的 `Token` / `Content-Type` 合并。 */
|
|
29
29
|
headers?: HeadersInit;
|
|
30
|
-
/** URL
|
|
31
|
-
query?: Record<string,
|
|
30
|
+
/** URL 查询参数;对象和数组使用 deepObject 风格序列化,`undefined` 值会被跳过。 */
|
|
31
|
+
query?: Record<string, unknown>;
|
|
32
32
|
/** 外部取消信号;会与 SDK 自身的超时控制合并。 */
|
|
33
33
|
signal?: AbortSignal;
|
|
34
34
|
/** 单次请求超时时间,优先级高于全局和客户端默认值。 */
|
|
@@ -36,3 +36,8 @@ export interface ClientRequestOptions {
|
|
|
36
36
|
/** 单次请求 TLS 跳过证书验证选项;仅 Node.js 运行时支持。 */
|
|
37
37
|
insecure?: boolean;
|
|
38
38
|
}
|
|
39
|
+
/** {@link ZentaoClient.getZentaoConfig} 的选项。 */
|
|
40
|
+
export interface GetZentaoConfigOptions extends Pick<ClientRequestOptions, 'timeout' | 'insecure' | 'signal'> {
|
|
41
|
+
/** 忽略已有缓存并重新获取配置,默认 false;同一客户端的并发获取会合并。 */
|
|
42
|
+
forceRefresh?: boolean;
|
|
43
|
+
}
|
package/dist/types/module.d.ts
CHANGED
|
@@ -27,9 +27,13 @@ export interface ModuleActionParam {
|
|
|
27
27
|
/** 未显式传入时使用的默认值。 */
|
|
28
28
|
defaultValue?: unknown;
|
|
29
29
|
/** 参数值类型,用于基础类型转换。 */
|
|
30
|
-
type?: 'string' | 'number' | 'boolean';
|
|
30
|
+
type?: 'string' | 'number' | 'boolean' | 'array' | 'object';
|
|
31
31
|
/** OpenAPI schema format,例如 `binary` 或 `int32`。 */
|
|
32
32
|
format?: string;
|
|
33
|
+
/** OpenAPI 查询参数序列化样式,例如 `deepObject`。 */
|
|
34
|
+
style?: string;
|
|
35
|
+
/** OpenAPI 查询参数是否展开序列化。 */
|
|
36
|
+
explode?: boolean;
|
|
33
37
|
/** 参数可选值。 */
|
|
34
38
|
options?: readonly ModuleActionParamOption[];
|
|
35
39
|
}
|
|
@@ -86,6 +90,8 @@ export type ModuleActionGetterFn<T, O = RequestProcessOptions> = (data: unknown,
|
|
|
86
90
|
export interface ModuleAction {
|
|
87
91
|
/** 动作名称,例如 `list`、`get`、`close`。 */
|
|
88
92
|
name: ModuleActionName;
|
|
93
|
+
/** 各系列支持的最低禅道正式版本,例如 `['22.0', 'biz13.0', 'max8.0', 'ipd5.0']`;不能为空或重复系列,未列出的系列不支持。 */
|
|
94
|
+
minVersion: readonly string[];
|
|
89
95
|
/** 动作类型,决定高阶 request 的路径/参数解析策略,并在 `method`、`resultType` 省略时作为推导依据。 */
|
|
90
96
|
type: ModuleActionType;
|
|
91
97
|
/** 面向用户展示的动作名称。 */
|
|
@@ -136,6 +142,11 @@ export interface ModuleDefinition {
|
|
|
136
142
|
/** 模块支持的动作集合。 */
|
|
137
143
|
actions: readonly ModuleAction[];
|
|
138
144
|
}
|
|
145
|
+
/** 注册库查询选项;不传版本时返回完整的当前定义,不使用全局版本。 */
|
|
146
|
+
export interface ModuleQueryOptions {
|
|
147
|
+
/** 仅返回该禅道版本支持的动作,以及至少含有一个支持动作的模块。 */
|
|
148
|
+
version?: string;
|
|
149
|
+
}
|
|
139
150
|
/** 将模块动作和参数解析后的可执行请求描述。 */
|
|
140
151
|
export interface ModuleActionRequest {
|
|
141
152
|
/** 模块名称。 */
|
|
@@ -147,7 +158,7 @@ export interface ModuleActionRequest {
|
|
|
147
158
|
/** 已替换路径参数后的 API 路径。 */
|
|
148
159
|
path: string;
|
|
149
160
|
/** 已组装的查询参数。 */
|
|
150
|
-
query?: Record<string,
|
|
161
|
+
query?: Record<string, unknown>;
|
|
151
162
|
/** 已组装的请求体。 */
|
|
152
163
|
data?: Record<string, unknown>;
|
|
153
164
|
/** 从 `id` 或 `{module}ID` 推断出的对象 ID。 */
|
package/dist/types/options.d.ts
CHANGED
|
@@ -2,6 +2,10 @@ import type { ZentaoClient } from '../client/index.js';
|
|
|
2
2
|
import type { ProcessListOptions, ProcessSingleOptions } from './data.js';
|
|
3
3
|
/** SDK 进程级全局默认选项,供高阶 {@link request} 调用复用。 */
|
|
4
4
|
export interface GlobalOptions {
|
|
5
|
+
/** 当前禅道正式版本,例如 `biz13.5`;普通高阶请求直接使用,单次强制刷新时使用实际版本。不会自动写入 profile。 */
|
|
6
|
+
version?: string;
|
|
7
|
+
/** 配置网络或响应获取失败时,允许登录继续或高阶请求跳过版本检查,默认 false;不忽略版本不匹配、格式错误或存储错误。 */
|
|
8
|
+
skipVersionCheckOnConfigError?: boolean;
|
|
5
9
|
/** 默认客户端;通常由 `ZentaoClient.init()` 设置。 */
|
|
6
10
|
client?: ZentaoClient;
|
|
7
11
|
/** 默认每页记录数,会映射到模块动作的 `recPerPage` 参数。 */
|
|
@@ -25,6 +29,10 @@ export interface GlobalOptions {
|
|
|
25
29
|
}
|
|
26
30
|
/** 高阶 `request("moduleName")` / `request("moduleName/methodName")` / `request("moduleName/<objectID>")` 的单次调用选项。 */
|
|
27
31
|
export interface RequestOptions extends ProcessListOptions {
|
|
32
|
+
/** 强制刷新服务器配置并用实际版本校验本次请求,优先于全局 version;不会改写全局版本。 */
|
|
33
|
+
forceRefreshConfig?: boolean;
|
|
34
|
+
/** 配置网络或响应获取失败时跳过本次版本检查;优先于全局设置,默认 false,不忽略版本不匹配或格式错误。 */
|
|
35
|
+
skipVersionCheckOnConfigError?: boolean;
|
|
28
36
|
/** 本次调用使用的客户端;优先级高于全局客户端。 */
|
|
29
37
|
client?: ZentaoClient;
|
|
30
38
|
/** 本次调用使用的每页记录数,优先级高于全局 `recPerPage`。 */
|
|
@@ -49,6 +57,7 @@ export interface RequestOptions extends ProcessListOptions {
|
|
|
49
57
|
* 动作 body schema 中声明的字段用现值补齐,再发起 PUT,避免禅道用空值覆盖未提交字段。
|
|
50
58
|
* 因此只需传想修改的字段即可。仅对 `type: 'update'` 且模块存在 `type: 'get'` 动作时生效。
|
|
51
59
|
*
|
|
60
|
+
* 只有模块中存在与 update 路径相同的 GET 动作时才会预取;否则直接发送更新请求。
|
|
52
61
|
* 不传时回落到全局 `autoFill`,默认 false。
|
|
53
62
|
*/
|
|
54
63
|
autoFill?: boolean;
|
package/dist/types/profile.d.ts
CHANGED
|
@@ -36,6 +36,8 @@ export interface ZentaoProfile {
|
|
|
36
36
|
lastUsedTime?: string;
|
|
37
37
|
/** 禅道服务端配置。 */
|
|
38
38
|
serverConfig?: ServerConfig;
|
|
39
|
+
/** 成功从 `?mode=getconfig` 获取配置的本地 ISO 时间;缺失时缓存需要刷新。 */
|
|
40
|
+
serverConfigFetchedAt?: string;
|
|
39
41
|
/** 客户端自定义配置。 */
|
|
40
42
|
config?: ZentaoProfileConfig;
|
|
41
43
|
/** 允许上层应用保存额外字段。 */
|
package/dist/types/response.d.ts
CHANGED
|
@@ -58,6 +58,13 @@ export interface LoginResponse extends ApiResponse {
|
|
|
58
58
|
}
|
|
59
59
|
/** 禅道 `?mode=getconfig` 返回的服务端配置。 */
|
|
60
60
|
export interface ServerConfig {
|
|
61
|
+
/**
|
|
62
|
+
* 禅道版本,不同系列以不同的前缀表示,下面为例子:
|
|
63
|
+
* - `22.5`:开源版 22.5
|
|
64
|
+
* - `biz13.5`:企业版 13.5
|
|
65
|
+
* - `max8.5`:旗舰版 8.5
|
|
66
|
+
* - `ipd5.5`:IPD 5.5
|
|
67
|
+
*/
|
|
61
68
|
version: string;
|
|
62
69
|
systemMode: string;
|
|
63
70
|
sprintConcept: string;
|
|
@@ -67,4 +74,6 @@ export interface ServerConfig {
|
|
|
67
74
|
methodVar: string;
|
|
68
75
|
viewVar: string;
|
|
69
76
|
sessionVar: string;
|
|
77
|
+
/** 保留服务端返回的其他配置字段。 */
|
|
78
|
+
[key: string]: unknown;
|
|
70
79
|
}
|
|
@@ -1,3 +1,2 @@
|
|
|
1
|
-
export { snapshotToHtml } from './html.js';
|
|
2
1
|
export { snapshotToMarkdown } from './markdown.js';
|
|
3
|
-
export type { BlockRenderer, BlockSnapshot, ConvertOptions, DeltaInsert, DocSnapshot, DocSnapshotMeta, DocumentReference,
|
|
2
|
+
export type { BlockRenderer, BlockSnapshot, ConvertOptions, DeltaInsert, DocSnapshot, DocSnapshotMeta, DocumentReference, JsonRecord, MarkdownOptions, RenderBlockContext, SliceSnapshot, Snapshot, SnapshotEnvelope, SnapshotInput, TextSnapshot, UnknownBlockStrategy, } from './types.js';
|
|
@@ -1,3 +1,2 @@
|
|
|
1
1
|
import type { BlockSnapshot, ConvertOptions } from './types.js';
|
|
2
2
|
export declare function deltaToMarkdown(value: unknown, options: ConvertOptions, block?: BlockSnapshot): string;
|
|
3
|
-
export declare function deltaToHtml(value: unknown, options: ConvertOptions, block?: BlockSnapshot): string;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { deltaFrom, escapeMarkdownMath,
|
|
1
|
+
import { deltaFrom, escapeMarkdownMath, resolveDocumentUrl, sanitizeUrl, toRecord, toStringValue, } from './shared.js';
|
|
2
2
|
function escapeMarkdownText(value) {
|
|
3
3
|
return value
|
|
4
4
|
.replace(/\\/g, '\\\\')
|
|
@@ -48,9 +48,8 @@ function renderMarkdownDelta(delta, options, block) {
|
|
|
48
48
|
const latex = toStringValue(attributes.latex);
|
|
49
49
|
const reference = referenceFrom(delta);
|
|
50
50
|
const rawLink = toStringValue(attributes.link);
|
|
51
|
-
// The
|
|
52
|
-
//
|
|
53
|
-
// readable holder representation below.
|
|
51
|
+
// The Markdown adapter has no holder matcher and therefore keeps the
|
|
52
|
+
// underlying insert (normally a single space).
|
|
54
53
|
let raw = mention ?? delta.insert;
|
|
55
54
|
const linkIsPlainText = !reference && (raw === '' || raw === rawLink);
|
|
56
55
|
if (!reference && raw === '' && rawLink)
|
|
@@ -89,80 +88,3 @@ export function deltaToMarkdown(value, options, block) {
|
|
|
89
88
|
.map(delta => renderMarkdownDelta(delta, options, block))
|
|
90
89
|
.join('');
|
|
91
90
|
}
|
|
92
|
-
function htmlDataAttributes(data) {
|
|
93
|
-
if (!isRecord(data))
|
|
94
|
-
return '';
|
|
95
|
-
return ` data-holder-data="${escapeHtmlAttribute(JSON.stringify(data))}"`;
|
|
96
|
-
}
|
|
97
|
-
function renderHtmlDelta(delta, options, block) {
|
|
98
|
-
if (typeof delta.insert !== 'string')
|
|
99
|
-
return '';
|
|
100
|
-
const attributes = toRecord(delta.attributes);
|
|
101
|
-
const holder = toRecord(attributes.holder);
|
|
102
|
-
if (Object.keys(holder).length) {
|
|
103
|
-
const text = toStringValue(holder.text) || toStringValue(holder.name) || delta.insert;
|
|
104
|
-
const id = toStringValue(holder.id);
|
|
105
|
-
const name = toStringValue(holder.name);
|
|
106
|
-
const hint = toStringValue(holder.hint);
|
|
107
|
-
return `<code class="affine-zui-holder"${id ? ` data-holder-id="${escapeHtmlAttribute(id)}"` : ''}${name ? ` data-holder-name="${escapeHtmlAttribute(name)}"` : ''}${hint ? ` title="${escapeHtmlAttribute(hint)}"` : ''}${htmlDataAttributes(holder.data)}>${escapeHtml(text)}</code>`;
|
|
108
|
-
}
|
|
109
|
-
const mention = toRecord(attributes.mention);
|
|
110
|
-
if (Object.keys(mention).length) {
|
|
111
|
-
const label = toStringValue(mention.label) || toStringValue(mention.name);
|
|
112
|
-
const id = toStringValue(mention.id);
|
|
113
|
-
const type = toStringValue(mention.type);
|
|
114
|
-
return `<span class="affine-zui-mention-label"${id ? ` data-id="${escapeHtmlAttribute(id)}"` : ''}${type ? ` data-type="${escapeHtmlAttribute(type)}"` : ''}>@${escapeHtml(label)}</span>`;
|
|
115
|
-
}
|
|
116
|
-
const latex = toStringValue(attributes.latex);
|
|
117
|
-
if (latex) {
|
|
118
|
-
return `<span class="affine-inline-latex" data-latex="${escapeHtmlAttribute(latex)}">${escapeHtml(latex)}</span>`;
|
|
119
|
-
}
|
|
120
|
-
const reference = referenceFrom(delta);
|
|
121
|
-
const visibleText = reference
|
|
122
|
-
? options.resolveDocTitle?.(reference.pageId) ||
|
|
123
|
-
delta.insert.trim() ||
|
|
124
|
-
reference.pageId
|
|
125
|
-
: delta.insert;
|
|
126
|
-
let output = escapeHtml(visibleText).replace(/\n/g, '<br>');
|
|
127
|
-
if (attributes.bold)
|
|
128
|
-
output = `<strong>${output}</strong>`;
|
|
129
|
-
if (attributes.italic)
|
|
130
|
-
output = `<em>${output}</em>`;
|
|
131
|
-
if (attributes.strike)
|
|
132
|
-
output = `<del>${output}</del>`;
|
|
133
|
-
if (attributes.underline)
|
|
134
|
-
output = `<u>${output}</u>`;
|
|
135
|
-
if (attributes.code)
|
|
136
|
-
output = `<code>${output}</code>`;
|
|
137
|
-
if (attributes.sub)
|
|
138
|
-
output = `<sub>${output}</sub>`;
|
|
139
|
-
if (attributes.sup)
|
|
140
|
-
output = `<sup>${output}</sup>`;
|
|
141
|
-
const styles = [];
|
|
142
|
-
const color = safeCssColor(attributes.color);
|
|
143
|
-
const background = safeCssColor(attributes.background);
|
|
144
|
-
const fontSize = safeFontSize(attributes.fontSize);
|
|
145
|
-
if (color)
|
|
146
|
-
styles.push(`color:${color}`);
|
|
147
|
-
if (background)
|
|
148
|
-
styles.push(`background-color:${background}`);
|
|
149
|
-
if (fontSize)
|
|
150
|
-
styles.push(`font-size:${fontSize}`);
|
|
151
|
-
if (styles.length)
|
|
152
|
-
output = `<span style="${styles.join(';')}">${output}</span>`;
|
|
153
|
-
let url = sanitizeUrl(attributes.link);
|
|
154
|
-
if (reference) {
|
|
155
|
-
url = resolveDocumentUrl(options, {
|
|
156
|
-
...reference,
|
|
157
|
-
title: options.resolveDocTitle?.(reference.pageId),
|
|
158
|
-
}, block);
|
|
159
|
-
}
|
|
160
|
-
return url
|
|
161
|
-
? `<a href="${escapeHtmlAttribute(url)}">${output}</a>`
|
|
162
|
-
: output;
|
|
163
|
-
}
|
|
164
|
-
export function deltaToHtml(value, options, block) {
|
|
165
|
-
return deltaFrom(value)
|
|
166
|
-
.map(delta => renderHtmlDelta(delta, options, block))
|
|
167
|
-
.join('');
|
|
168
|
-
}
|
|
@@ -22,9 +22,6 @@ export declare function validateSnapshotTree(blocks: readonly BlockSnapshot[], o
|
|
|
22
22
|
export declare function deltaFrom(value: unknown): DeltaInsert[];
|
|
23
23
|
export declare function plainText(value: unknown): string;
|
|
24
24
|
export declare function pageTitleDelta(normalized: NormalizedSnapshot): DeltaInsert[];
|
|
25
|
-
export declare function pageTitleText(normalized: NormalizedSnapshot): string;
|
|
26
|
-
export declare function escapeHtml(value: unknown): string;
|
|
27
|
-
export declare function escapeHtmlAttribute(value: unknown): string;
|
|
28
25
|
export declare function sanitizeUrl(value: unknown, kind?: 'link' | 'image'): string;
|
|
29
26
|
export declare function resolveAssetUrl(options: ConvertOptions, block: BlockSnapshot, kind: 'image' | 'attachment'): string;
|
|
30
27
|
export declare function resolveZuiImageUrl(options: ConvertOptions, block: BlockSnapshot): string;
|
|
@@ -33,5 +30,3 @@ export declare function formatFileSize(value: unknown): string;
|
|
|
33
30
|
export declare function isEdgelessOnly(block: BlockSnapshot): boolean;
|
|
34
31
|
export declare function safeLanguage(value: unknown): string;
|
|
35
32
|
export declare function escapeMarkdownMath(value: unknown): string;
|
|
36
|
-
export declare function safeCssColor(value: unknown): string;
|
|
37
|
-
export declare function safeFontSize(value: unknown): string;
|
|
@@ -160,23 +160,6 @@ export function pageTitleDelta(normalized) {
|
|
|
160
160
|
const metaTitle = normalized.meta?.title;
|
|
161
161
|
return typeof metaTitle === 'string' ? [{ insert: metaTitle }] : [];
|
|
162
162
|
}
|
|
163
|
-
export function pageTitleText(normalized) {
|
|
164
|
-
const metaTitle = normalized.meta?.title;
|
|
165
|
-
if (typeof metaTitle === 'string')
|
|
166
|
-
return metaTitle;
|
|
167
|
-
return plainText(toRecord(normalized.root?.props).title);
|
|
168
|
-
}
|
|
169
|
-
export function escapeHtml(value) {
|
|
170
|
-
return String(value ?? '')
|
|
171
|
-
.replace(/&/g, '&')
|
|
172
|
-
.replace(/</g, '<')
|
|
173
|
-
.replace(/>/g, '>');
|
|
174
|
-
}
|
|
175
|
-
export function escapeHtmlAttribute(value) {
|
|
176
|
-
return escapeHtml(value)
|
|
177
|
-
.replace(/"/g, '"')
|
|
178
|
-
.replace(/'/g, ''');
|
|
179
|
-
}
|
|
180
163
|
export function sanitizeUrl(value, kind = 'link') {
|
|
181
164
|
if (typeof value !== 'string')
|
|
182
165
|
return '';
|
|
@@ -286,20 +269,3 @@ export function escapeMarkdownMath(value) {
|
|
|
286
269
|
return `${prefix}${target.replace(/:|&(?:#0*58|#x0*3a|colon);/i, '%3A')}`;
|
|
287
270
|
});
|
|
288
271
|
}
|
|
289
|
-
export function safeCssColor(value) {
|
|
290
|
-
if (typeof value !== 'string')
|
|
291
|
-
return '';
|
|
292
|
-
const color = value.trim();
|
|
293
|
-
if (/^(#[\da-f]{3,8}|(?:rgb|hsl)a?\([\d\s.,%+-]+\)|var\(--[\w-]+\)|[a-z]+)$/i.test(color)) {
|
|
294
|
-
return color;
|
|
295
|
-
}
|
|
296
|
-
return '';
|
|
297
|
-
}
|
|
298
|
-
export function safeFontSize(value) {
|
|
299
|
-
if (typeof value !== 'string')
|
|
300
|
-
return '';
|
|
301
|
-
const size = value.trim();
|
|
302
|
-
return /^(?:\d+(?:\.\d+)?(?:px|em|rem|%|pt)|var\(--[\w-]+\))$/i.test(size)
|
|
303
|
-
? size
|
|
304
|
-
: '';
|
|
305
|
-
}
|
|
@@ -37,7 +37,6 @@ export interface SnapshotEnvelope {
|
|
|
37
37
|
snapshot: Snapshot | readonly BlockSnapshot[];
|
|
38
38
|
}
|
|
39
39
|
export type SnapshotInput = Snapshot | readonly BlockSnapshot[] | SnapshotEnvelope | string;
|
|
40
|
-
export type OutputFormat = 'markdown' | 'html';
|
|
41
40
|
export type UnknownBlockStrategy = 'children' | 'omit' | 'throw';
|
|
42
41
|
export interface DocumentReference {
|
|
43
42
|
pageId: string;
|
|
@@ -45,7 +44,7 @@ export interface DocumentReference {
|
|
|
45
44
|
title?: string;
|
|
46
45
|
}
|
|
47
46
|
export interface RenderBlockContext {
|
|
48
|
-
format:
|
|
47
|
+
format: 'markdown';
|
|
49
48
|
renderChildren(children?: readonly BlockSnapshot[]): string;
|
|
50
49
|
renderInline(text: unknown): string;
|
|
51
50
|
}
|
|
@@ -73,14 +72,3 @@ export interface ConvertOptions {
|
|
|
73
72
|
unknownBlock?: UnknownBlockStrategy;
|
|
74
73
|
}
|
|
75
74
|
export type MarkdownOptions = ConvertOptions;
|
|
76
|
-
export interface HtmlOptions extends ConvertOptions {
|
|
77
|
-
/**
|
|
78
|
-
* Defaults to true for a DocSnapshot/page root and false for block/slice input.
|
|
79
|
-
*/
|
|
80
|
-
fullDocument?: boolean;
|
|
81
|
-
/**
|
|
82
|
-
* Render stored HTML from affine:embed-zui-html or zui-custom as trusted HTML.
|
|
83
|
-
* Disabled by default.
|
|
84
|
-
*/
|
|
85
|
-
allowUnsafeHtml?: boolean;
|
|
86
|
-
}
|
package/dist/version.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zentao-api",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.7",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Browser and Node.js SDK for ZenTao API",
|
|
6
6
|
"license": "MIT",
|
|
@@ -47,6 +47,7 @@
|
|
|
47
47
|
"typecheck": "tsc --noEmit",
|
|
48
48
|
"typecheck:tests": "tsc -p tsconfig.test.json --noEmit",
|
|
49
49
|
"registry:check": "bun run scripts/update-registry.ts --check",
|
|
50
|
+
"registry:audit": "bun run .agents/skills/update-openapi-registry/scripts/audit-generated-registry.ts",
|
|
50
51
|
"build": "rm -rf dist && tsc -p tsconfig.json && bun run scripts/build-browser.ts",
|
|
51
52
|
"smoke:node": "node scripts/smoke-node.mjs",
|
|
52
53
|
"smoke:browser": "bun run scripts/smoke-browser-bundler.ts",
|
|
@@ -57,16 +58,16 @@
|
|
|
57
58
|
"docs:dev": "bun run docs:generate && vitepress dev docs",
|
|
58
59
|
"docs:build": "bun run docs:generate && vitepress build docs",
|
|
59
60
|
"docs:preview": "bun run docs:build && vitepress preview docs",
|
|
60
|
-
"check": "bun run test:coverage && bun run typecheck && bun run typecheck:tests && bun run registry:check && bun run build && bun run smoke:node && bun run smoke:browser && bun run smoke:package",
|
|
61
|
+
"check": "bun run test:coverage && bun run typecheck && bun run typecheck:tests && bun run registry:check && bun run registry:audit && bun run build && bun run smoke:node && bun run smoke:browser && bun run smoke:package",
|
|
61
62
|
"prepublishOnly": "bun run check"
|
|
62
63
|
},
|
|
63
64
|
"devDependencies": {
|
|
64
|
-
"@types/bun": "^1.
|
|
65
|
-
"@types/node": "^24.
|
|
66
|
-
"typedoc": "^0.28.
|
|
67
|
-
"typedoc-plugin-markdown": "^4.
|
|
68
|
-
"typedoc-vitepress-theme": "^1.1.
|
|
69
|
-
"typescript": "^5.
|
|
65
|
+
"@types/bun": "^1.4.0",
|
|
66
|
+
"@types/node": "^24.13.3",
|
|
67
|
+
"typedoc": "^0.28.20",
|
|
68
|
+
"typedoc-plugin-markdown": "^4.12.0",
|
|
69
|
+
"typedoc-vitepress-theme": "^1.1.3",
|
|
70
|
+
"typescript": "^5.9.3",
|
|
70
71
|
"vitepress": "^1.6.4"
|
|
71
72
|
},
|
|
72
73
|
"engines": {
|