zentao-api 0.1.0 → 0.2.0-beta.2

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.
Files changed (53) hide show
  1. package/LICENSE +2 -2
  2. package/README.md +221 -131
  3. package/dist/browser/zentao-api.global.js +2 -0
  4. package/dist/browser.d.ts +1 -0
  5. package/dist/browser.js +1 -0
  6. package/dist/client/index.d.ts +39 -0
  7. package/dist/client/index.js +172 -0
  8. package/dist/index.d.ts +8 -4
  9. package/dist/index.js +7 -8
  10. package/dist/misc/browser-global.d.ts +1 -0
  11. package/dist/misc/browser-global.js +8 -0
  12. package/dist/misc/environment.d.ts +8 -0
  13. package/dist/misc/environment.js +124 -0
  14. package/dist/misc/errors.d.ts +30 -0
  15. package/dist/misc/errors.js +40 -0
  16. package/dist/misc/global-options.d.ts +5 -0
  17. package/dist/misc/global-options.js +9 -0
  18. package/dist/modules/generated.d.ts +8 -0
  19. package/dist/modules/generated.js +4226 -0
  20. package/dist/modules/registry.d.ts +22 -0
  21. package/dist/modules/registry.js +142 -0
  22. package/dist/modules/resolve.d.ts +7 -0
  23. package/dist/modules/resolve.js +206 -0
  24. package/dist/profiles/index.d.ts +14 -0
  25. package/dist/profiles/index.js +205 -0
  26. package/dist/request/index.d.ts +7 -0
  27. package/dist/request/index.js +65 -0
  28. package/dist/types/index.d.ts +296 -0
  29. package/dist/types/index.js +1 -0
  30. package/dist/utils/index.d.ts +6 -0
  31. package/dist/utils/index.js +26 -0
  32. package/dist/version.d.ts +2 -0
  33. package/dist/version.js +4 -0
  34. package/package.json +52 -76
  35. package/dist/types.d.ts +0 -70
  36. package/dist/utils.d.ts +0 -93
  37. package/dist/zentao-api.cjs.development.js +0 -3619
  38. package/dist/zentao-api.cjs.development.js.map +0 -1
  39. package/dist/zentao-api.cjs.production.min.js +0 -2
  40. package/dist/zentao-api.cjs.production.min.js.map +0 -1
  41. package/dist/zentao-api.esm.js +0 -3611
  42. package/dist/zentao-api.esm.js.map +0 -1
  43. package/dist/zentao-config.d.ts +0 -93
  44. package/dist/zentao-request-builder.d.ts +0 -120
  45. package/dist/zentao.d.ts +0 -175
  46. package/dist/zentao12.d.ts +0 -676
  47. package/src/index.ts +0 -5
  48. package/src/types.ts +0 -88
  49. package/src/utils.ts +0 -216
  50. package/src/zentao-config.ts +0 -150
  51. package/src/zentao-request-builder.ts +0 -227
  52. package/src/zentao.ts +0 -596
  53. package/src/zentao12.ts +0 -1272
@@ -0,0 +1,124 @@
1
+ import { ZentaoError } from './errors.js';
2
+ /** 判断当前运行时是否为 Node.js。 */
3
+ export function isNodeRuntime() {
4
+ return typeof process !== 'undefined' && Boolean(process.versions?.node);
5
+ }
6
+ async function importNodeModule(specifier) {
7
+ const dynamicImport = new Function('specifier', 'return import(specifier)');
8
+ return dynamicImport(specifier);
9
+ }
10
+ function toNodeRequestHeaders(headers) {
11
+ const result = {};
12
+ new Headers(headers).forEach((value, key) => {
13
+ result[key] = value;
14
+ });
15
+ return result;
16
+ }
17
+ function toResponseHeaders(headers) {
18
+ const result = new Headers();
19
+ for (const [key, value] of Object.entries(headers)) {
20
+ if (value === undefined)
21
+ continue;
22
+ result.set(key, Array.isArray(value) ? value.join(', ') : String(value));
23
+ }
24
+ return result;
25
+ }
26
+ async function toNodeBody(body) {
27
+ if (body === undefined || body === null)
28
+ return undefined;
29
+ if (typeof body === 'string')
30
+ return body;
31
+ if (body instanceof Uint8Array)
32
+ return body;
33
+ if (body instanceof ArrayBuffer)
34
+ return new Uint8Array(body);
35
+ if (ArrayBuffer.isView(body)) {
36
+ return new Uint8Array(body.buffer, body.byteOffset, body.byteLength);
37
+ }
38
+ if (body instanceof Blob) {
39
+ return new Uint8Array(await body.arrayBuffer());
40
+ }
41
+ return String(body);
42
+ }
43
+ function abortError() {
44
+ return new DOMException('The operation was aborted.', 'AbortError');
45
+ }
46
+ function concatenateChunks(chunks) {
47
+ const totalLength = chunks.reduce((total, chunk) => total + chunk.byteLength, 0);
48
+ const result = new Uint8Array(totalLength);
49
+ let offset = 0;
50
+ for (const chunk of chunks) {
51
+ result.set(chunk, offset);
52
+ offset += chunk.byteLength;
53
+ }
54
+ return result.buffer;
55
+ }
56
+ async function nodeFetchWithTlsOptions(url, init, rejectUnauthorized) {
57
+ const parsed = new URL(url);
58
+ const transport = parsed.protocol === 'https:'
59
+ ? await importNodeModule('node:https')
60
+ : await importNodeModule('node:http');
61
+ const body = await toNodeBody(init.body);
62
+ return new Promise((resolve, reject) => {
63
+ if (init.signal?.aborted) {
64
+ reject(abortError());
65
+ return;
66
+ }
67
+ const request = transport.request(parsed, {
68
+ method: init.method ?? 'GET',
69
+ headers: toNodeRequestHeaders(init.headers),
70
+ rejectUnauthorized,
71
+ }, (response) => {
72
+ const chunks = [];
73
+ response.on('data', (chunk) => {
74
+ chunks.push(typeof chunk === 'string' ? new TextEncoder().encode(chunk) : chunk);
75
+ });
76
+ response.on('end', () => {
77
+ cleanup();
78
+ const responseBody = chunks.length > 0 ? concatenateChunks(chunks) : undefined;
79
+ const fetchResponse = new Response(responseBody, {
80
+ status: response.statusCode ?? 200,
81
+ statusText: response.statusMessage ?? '',
82
+ headers: toResponseHeaders(response.headers),
83
+ });
84
+ Object.defineProperty(fetchResponse, 'url', { value: url });
85
+ resolve(fetchResponse);
86
+ });
87
+ });
88
+ const cleanup = () => {
89
+ init.signal?.removeEventListener('abort', abortHandler);
90
+ };
91
+ const abortHandler = () => {
92
+ cleanup();
93
+ request.destroy(abortError());
94
+ };
95
+ request.on('error', (error) => {
96
+ cleanup();
97
+ reject(error);
98
+ });
99
+ init.signal?.addEventListener('abort', abortHandler, { once: true });
100
+ if (body !== undefined)
101
+ request.write(body);
102
+ request.end();
103
+ });
104
+ }
105
+ /** 浏览器无法跳过 TLS 校验,因此在发起请求前提前失败。 */
106
+ export function assertInsecureSupported(enabled) {
107
+ if (enabled && !isNodeRuntime()) {
108
+ throw new ZentaoError('E_INSECURE_BROWSER');
109
+ }
110
+ }
111
+ /** 发起 fetch 请求;Node.js 下的 `insecure` 只作用于当前 HTTPS 请求。 */
112
+ export async function fetchWithInsecureTls(enabled, url, init) {
113
+ if (!enabled)
114
+ return fetch(url, init);
115
+ assertInsecureSupported(enabled);
116
+ return nodeFetchWithTlsOptions(url, init, false);
117
+ }
118
+ /** 保留给内部测试和兼容调用:校验 TLS 选项,但不再改写进程级环境变量。 */
119
+ export async function withInsecureTls(enabled, fn) {
120
+ if (!enabled)
121
+ return fn();
122
+ assertInsecureSupported(enabled);
123
+ return fn();
124
+ }
@@ -0,0 +1,30 @@
1
+ export declare const ERRORS: {
2
+ readonly E_INVALID_BASE_URL: "Invalid ZenTao baseUrl.";
3
+ readonly E_NO_GLOBAL_CLIENT: "No global client configured. Call ZentaoClient.init() or pass options.client.";
4
+ readonly E_HTTP_ERROR: "HTTP request failed: {status} {statusText}";
5
+ readonly E_NETWORK_ERROR: "Network request failed: {message}";
6
+ readonly E_TIMEOUT: "Request timed out.";
7
+ readonly E_INSECURE_BROWSER: "The insecure option is only supported in Node.js runtimes.";
8
+ readonly E_LOGIN_FAILED: "ZenTao login failed.";
9
+ readonly E_INVALID_PROFILE: "Invalid ZenTao profile.";
10
+ readonly E_NO_PROFILE: "No ZenTao profile is configured.";
11
+ readonly E_PROFILE_NOT_FOUND: "ZenTao profile not found: {profileKey}";
12
+ readonly E_PROFILE_STORAGE_INVALID: "ZenTao profile storage is not valid JSON.";
13
+ readonly E_PROFILE_STORAGE_UNAVAILABLE: "ZenTao profile storage is unavailable in this runtime.";
14
+ readonly E_INVALID_MODULE: "Unknown module: {module}";
15
+ readonly E_INVALID_ACTION: "Unknown action: {module}-{action}";
16
+ readonly E_INVALID_MODULE_DEFINITION: "Invalid module definition.";
17
+ readonly E_INVALID_ACTION_DEFINITION: "Invalid module action definition.";
18
+ readonly E_MISSING_PARAM: "Missing required parameter: {param}";
19
+ readonly E_INVALID_REQUEST_NAME: "Request name must use the form \"moduleName/methodName\".";
20
+ };
21
+ export type ErrorCode = keyof typeof ERRORS;
22
+ /** SDK 统一错误类型,所有可预期错误都会携带稳定错误码。 */
23
+ export declare class ZentaoError extends Error {
24
+ /** 错误码,对应 {@link ERRORS} 的 key。 */
25
+ readonly code: ErrorCode;
26
+ /** 附加上下文,例如 HTTP 响应详情或原始异常。 */
27
+ readonly details?: unknown;
28
+ /** 根据错误码和占位符替换值创建错误。 */
29
+ constructor(code: ErrorCode, replacements?: Record<string, string | number>, details?: unknown);
30
+ }
@@ -0,0 +1,40 @@
1
+ export const ERRORS = {
2
+ E_INVALID_BASE_URL: 'Invalid ZenTao baseUrl.',
3
+ E_NO_GLOBAL_CLIENT: 'No global client configured. Call ZentaoClient.init() or pass options.client.',
4
+ E_HTTP_ERROR: 'HTTP request failed: {status} {statusText}',
5
+ E_NETWORK_ERROR: 'Network request failed: {message}',
6
+ E_TIMEOUT: 'Request timed out.',
7
+ E_INSECURE_BROWSER: 'The insecure option is only supported in Node.js runtimes.',
8
+ E_LOGIN_FAILED: 'ZenTao login failed.',
9
+ E_INVALID_PROFILE: 'Invalid ZenTao profile.',
10
+ E_NO_PROFILE: 'No ZenTao profile is configured.',
11
+ E_PROFILE_NOT_FOUND: 'ZenTao profile not found: {profileKey}',
12
+ E_PROFILE_STORAGE_INVALID: 'ZenTao profile storage is not valid JSON.',
13
+ E_PROFILE_STORAGE_UNAVAILABLE: 'ZenTao profile storage is unavailable in this runtime.',
14
+ E_INVALID_MODULE: 'Unknown module: {module}',
15
+ E_INVALID_ACTION: 'Unknown action: {module}-{action}',
16
+ E_INVALID_MODULE_DEFINITION: 'Invalid module definition.',
17
+ E_INVALID_ACTION_DEFINITION: 'Invalid module action definition.',
18
+ E_MISSING_PARAM: 'Missing required parameter: {param}',
19
+ E_INVALID_REQUEST_NAME: 'Request name must use the form "moduleName/methodName".',
20
+ };
21
+ /** SDK 统一错误类型,所有可预期错误都会携带稳定错误码。 */
22
+ export class ZentaoError extends Error {
23
+ /** 错误码,对应 {@link ERRORS} 的 key。 */
24
+ code;
25
+ /** 附加上下文,例如 HTTP 响应详情或原始异常。 */
26
+ details;
27
+ /** 根据错误码和占位符替换值创建错误。 */
28
+ constructor(code, replacements, details) {
29
+ let message = ERRORS[code];
30
+ if (replacements) {
31
+ for (const [key, value] of Object.entries(replacements)) {
32
+ message = message.replaceAll(`{${key}}`, String(value));
33
+ }
34
+ }
35
+ super(message);
36
+ this.name = 'ZentaoError';
37
+ this.code = code;
38
+ this.details = details;
39
+ }
40
+ }
@@ -0,0 +1,5 @@
1
+ import type { GlobalOptions } from '../types/index.js';
2
+ /** 获取当前全局选项快照;返回副本,避免调用方直接改写内部状态。 */
3
+ export declare function getGlobalOptions(): GlobalOptions;
4
+ /** 合并设置全局选项;传入 `undefined` 可清空对应字段。 */
5
+ export declare function setGlobalOptions(options: Partial<GlobalOptions>): void;
@@ -0,0 +1,9 @@
1
+ let globalOptions = {};
2
+ /** 获取当前全局选项快照;返回副本,避免调用方直接改写内部状态。 */
3
+ export function getGlobalOptions() {
4
+ return { ...globalOptions };
5
+ }
6
+ /** 合并设置全局选项;传入 `undefined` 可清空对应字段。 */
7
+ export function setGlobalOptions(options) {
8
+ globalOptions = { ...globalOptions, ...options };
9
+ }
@@ -0,0 +1,8 @@
1
+ import type { ModuleDefinition } from '../types/index.js';
2
+ /**
3
+ * 内置模块注册表:key 为模块名(小写),value 为对应禅道 REST 资源元数据。
4
+ * 新增模块时优先更新 OpenAPI 数据并重新生成此文件。
5
+ *
6
+ * 此文件由 scripts/update-registry.ts 自动生成,请勿手动编辑。
7
+ */
8
+ export declare const BUILTIN_MODULES: ModuleDefinition[];