@turinhub/tale-js-sdk 2.0.0 → 2.1.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.
package/README.md CHANGED
@@ -23,16 +23,90 @@ yarn add @turinhub/tale-js-sdk
23
23
  本 SDK 采用 ESM(`type: module`)。在 TypeScript 或现代 Node.js/前端环境中可直接导入:
24
24
 
25
25
  ```ts
26
- // 具体导出以实际 SDK 为准(后续文档将补充)
27
- import * as Tale from "@turinhub/tale-js-sdk";
26
+ import {
27
+ getAppToken,
28
+ getCurrentUser,
29
+ listAppsByOrg,
30
+ listFiles,
31
+ } from "@turinhub/tale-js-sdk";
32
+
33
+ const appToken = await getAppToken({
34
+ baseUrl: "https://api.tale.example",
35
+ appKey: process.env.TALE_APP_KEY,
36
+ appSecret: process.env.TALE_APP_SECRET,
37
+ });
38
+
39
+ const files = await listFiles({
40
+ baseUrl: "https://api.tale.example",
41
+ appToken,
42
+ folderId: "folder-id",
43
+ });
44
+
45
+ const currentUser = await getCurrentUser({
46
+ baseUrl: "https://api.tale.example",
47
+ taleToken: "tale-user-token",
48
+ });
49
+
50
+ const apps = await listAppsByOrg(currentUser.app.orgId, {
51
+ baseUrl: "https://api.tale.example",
52
+ taleToken: "tale-user-token",
53
+ });
54
+ ```
55
+
56
+ ## 认证与 Token
57
+
58
+ SDK 中常见的鉴权选项都通过 `x-t-token` 发送到 Tale 后端:
59
+
60
+ - `appToken`:应用作用域 Token,适用于 CMS、RBAC、ACL、Task、Attachment 等应用内资源 API。
61
+ - `taleToken`:Tale 用户 Token,适用于平台级 App 管理 API,例如获取当前用户、应用列表、应用详情、App Secret,以及按 App Key 换发 App Token。
62
+ - `baseUrl`:Tale 后端服务基础 URL;未显式传入时会读取环境变量。
63
+
64
+ 服务端可使用 `getAppToken()` 通过 `TALE_APP_KEY` 与 `TALE_APP_SECRET` 获取应用 Token。浏览器端的 Cookie、缓存、自动续期和多应用切换策略不由 SDK 核心管理,调用方可基于 `issueAppToken()` 自行封装。
65
+
66
+ ## App 与 App Token API
28
67
 
29
- // 示例:初始化客户端并调用 API(占位示例)
30
- // const client = Tale.createClient({ baseUrl: 'https://api.tale.example' })
31
- // const result = await client.health()
32
- // console.log(result)
68
+ AppTS 模块提供平台应用相关方法:
69
+
70
+ ```ts
71
+ import {
72
+ getCurrentUser,
73
+ listAppsByOrg,
74
+ createApp,
75
+ getAppDetail,
76
+ updateApp,
77
+ } from "@turinhub/tale-js-sdk";
78
+ ```
79
+
80
+ AppTokenTS 模块提供 App Token 相关接口封装:
81
+
82
+ ```ts
83
+ import {
84
+ listAppTokens,
85
+ getAppSecret,
86
+ issueAppToken,
87
+ } from "@turinhub/tale-js-sdk";
88
+
89
+ const page = await listAppTokens({
90
+ baseUrl: "https://api.tale.example",
91
+ appToken: "app-token",
92
+ page: 0,
93
+ size: 20,
94
+ isValid: true,
95
+ });
96
+
97
+ const secret = await getAppSecret("oa_xxx", {
98
+ baseUrl: "https://api.tale.example",
99
+ taleToken: "tale-user-token",
100
+ });
101
+
102
+ const issued = await issueAppToken({
103
+ baseUrl: "https://api.tale.example",
104
+ taleToken: "tale-user-token",
105
+ appKey: "oa_xxx",
106
+ });
33
107
  ```
34
108
 
35
- > 提示:由于 SDK 仍在迭代中,具体 API 名称与调用方式请以后续文档或源码为准。
109
+ 公开类型、入参和响应字段保持 camelCase。SDK 会处理 Java 后端可能返回的 `{ code, msg, data }` envelope 与直接 payload,但不再兼容历史 snake_case 响应字段。
36
110
 
37
111
  ## 开发与测试
38
112
 
@@ -40,7 +114,10 @@ import * as Tale from "@turinhub/tale-js-sdk";
40
114
 
41
115
  ```bash
42
116
  pnpm install # 或 npm install / yarn
43
- pnpm test # 对应 package.json 中的 "test": "jest"
117
+ pnpm run typecheck
118
+ pnpm run build
119
+ pnpm run test:unit
120
+ pnpm test
44
121
  ```
45
122
 
46
123
  ## 更新日志与版本
@@ -0,0 +1,10 @@
1
+ import type { AppDetail, AppInfo, AppUserLike, CommonOptions, CreateAppRequest, CreateAppResponse, CurrentAppUser, UpdateAppRequest } from "./types.js";
2
+ export type { AppDetail, AppDetailResponse, AppInfo, AppPrivilege, AppRole, AppUserLike, CommonOptions, CreateAppRequest, CreateAppResponse, CurrentAppUser, UpdateAppRequest, } from "./types.js";
3
+ export declare function normalizeAppInfo(value: unknown): AppInfo;
4
+ export declare function normalizeAppDetail(value: unknown): AppDetail;
5
+ export declare function normalizeCurrentAppUser(value: unknown): CurrentAppUser;
6
+ export declare function getCurrentUser(options?: CommonOptions): Promise<AppUserLike>;
7
+ export declare function listAppsByOrg(orgId: string, options?: CommonOptions): Promise<AppInfo[]>;
8
+ export declare function createApp(request: CreateAppRequest, options?: CommonOptions): Promise<CreateAppResponse>;
9
+ export declare function getAppDetail(appKey: string, options?: CommonOptions): Promise<AppDetail>;
10
+ export declare function updateApp(request: UpdateAppRequest, options?: CommonOptions): Promise<AppDetail>;
@@ -0,0 +1,198 @@
1
+ import { assertRequiredString, pickBoolean, pickString, requestJson, toRecord, } from "../common/http.js";
2
+ function normalizeRole(value) {
3
+ const role = toRecord(value);
4
+ return {
5
+ roleId: pickString(role.roleId),
6
+ roleName: pickString(role.roleName),
7
+ roleType: pickString(role.roleType) || undefined,
8
+ roleProperty: toRecord(role.roleProperty),
9
+ roleConfig: toRecord(role.roleConfig),
10
+ rolePrivileges: Array.isArray(role.rolePrivileges)
11
+ ? role.rolePrivileges.map(normalizePrivilege)
12
+ : undefined,
13
+ privilegeIds: Array.isArray(role.privilegeIds)
14
+ ? role.privilegeIds.map(String)
15
+ : undefined,
16
+ resourceIds: Array.isArray(role.resourceIds)
17
+ ? role.resourceIds.map(String)
18
+ : undefined,
19
+ expiredAt: pickString(role.expiredAt) || undefined,
20
+ remark: pickString(role.remark) || undefined,
21
+ };
22
+ }
23
+ function normalizePrivilege(value) {
24
+ const privilege = toRecord(value);
25
+ return {
26
+ privilegeId: pickString(privilege.privilegeId),
27
+ privilegeName: pickString(privilege.privilegeName),
28
+ privilegeType: pickString(privilege.privilegeType) || undefined,
29
+ privilegeProperty: toRecord(privilege.privilegeProperty),
30
+ privilegeConfig: toRecord(privilege.privilegeConfig),
31
+ resourceIds: Array.isArray(privilege.resourceIds)
32
+ ? privilege.resourceIds.map(String)
33
+ : undefined,
34
+ expiredAt: pickString(privilege.expiredAt) || undefined,
35
+ remark: pickString(privilege.remark) || undefined,
36
+ };
37
+ }
38
+ function normalizeUserGroup(value) {
39
+ const group = toRecord(value);
40
+ return {
41
+ groupId: pickString(group.groupId),
42
+ name: pickString(group.name, group.groupName),
43
+ description: pickString(group.description) || undefined,
44
+ type: pickString(group.type) || undefined,
45
+ remark: pickString(group.remark) || undefined,
46
+ };
47
+ }
48
+ export function normalizeAppInfo(value) {
49
+ const app = toRecord(value);
50
+ const appKey = pickString(app.appKey);
51
+ const appName = pickString(app.appName, app.name);
52
+ const appId = pickString(app.appId, app.id);
53
+ return {
54
+ appId,
55
+ appKey,
56
+ appName,
57
+ id: pickString(app.id, appId, appKey),
58
+ name: pickString(app.name, appName, appKey),
59
+ description: pickString(app.description, app.remark) || undefined,
60
+ icon: pickString(app.icon) || undefined,
61
+ plan: pickString(app.plan) || undefined,
62
+ orgId: pickString(app.orgId) || undefined,
63
+ appProperty: toRecord(app.appProperty),
64
+ appConfig: toRecord(app.appConfig),
65
+ appAdmins: toRecord(app.appAdmins),
66
+ roleConfig: toRecord(app.roleConfig),
67
+ userGroupConfig: toRecord(app.userGroupConfig),
68
+ createdAt: pickString(app.createdAt) || undefined,
69
+ updatedAt: pickString(app.updatedAt) || undefined,
70
+ remark: pickString(app.remark) || undefined,
71
+ };
72
+ }
73
+ function normalizeAppRole(value) {
74
+ return normalizeRole(value);
75
+ }
76
+ function normalizeAppPrivilege(value) {
77
+ return normalizePrivilege(value);
78
+ }
79
+ export function normalizeAppDetail(value) {
80
+ const app = toRecord(value);
81
+ const info = normalizeAppInfo(app);
82
+ return {
83
+ ...info,
84
+ appRoles: Array.isArray(app.appRoles)
85
+ ? app.appRoles.map(normalizeAppRole)
86
+ : [],
87
+ appPrivileges: Array.isArray(app.appPrivileges)
88
+ ? app.appPrivileges.map(normalizeAppPrivilege)
89
+ : [],
90
+ wechatConfig: toRecord(app.wechatConfig),
91
+ feishuConfig: toRecord(app.feishuConfig),
92
+ openaiConfig: toRecord(app.openaiConfig),
93
+ };
94
+ }
95
+ export function normalizeCurrentAppUser(value) {
96
+ const payload = toRecord(value);
97
+ const app = toRecord(payload.app);
98
+ const user = toRecord(payload.user);
99
+ return {
100
+ app: {
101
+ appName: pickString(app.appName),
102
+ appKey: pickString(app.appKey),
103
+ orgId: pickString(app.orgId, payload.orgId),
104
+ appId: pickString(app.appId),
105
+ },
106
+ user: {
107
+ latestLoginTime: pickString(user.latestLoginTime) || undefined,
108
+ registeredAt: pickString(user.registeredAt) || undefined,
109
+ isFrozen: pickBoolean(user.isFrozen),
110
+ userId: pickString(user.userId, payload.userId),
111
+ phone: pickString(user.phone) || undefined,
112
+ username: pickString(user.username) || undefined,
113
+ email: pickString(user.email) || undefined,
114
+ nickname: pickString(user.nickname) || undefined,
115
+ remark: pickString(user.remark) || undefined,
116
+ avatarUrl: pickString(user.avatarUrl) || undefined,
117
+ },
118
+ thirdParty: toRecord(payload.thirdParty),
119
+ userRoles: Array.isArray(payload.userRoles)
120
+ ? payload.userRoles.map(normalizeRole)
121
+ : [],
122
+ userPrivileges: Array.isArray(payload.userPrivileges)
123
+ ? payload.userPrivileges.map(normalizePrivilege)
124
+ : [],
125
+ userGroups: Array.isArray(payload.userGroups)
126
+ ? payload.userGroups.map(normalizeUserGroup)
127
+ : [],
128
+ };
129
+ }
130
+ function normalizeCreateAppResponse(value) {
131
+ const app = toRecord(value);
132
+ return {
133
+ appId: pickString(app.appId, app.id),
134
+ appName: pickString(app.appName, app.name),
135
+ orgId: pickString(app.orgId),
136
+ appKey: pickString(app.appKey),
137
+ createdAt: pickString(app.createdAt) || undefined,
138
+ };
139
+ }
140
+ function toUpdateAppPayload(request) {
141
+ return {
142
+ appId: request.appId,
143
+ appName: request.appName,
144
+ remark: request.remark,
145
+ appProperty: request.appProperty,
146
+ appConfig: request.appConfig,
147
+ wechatConfig: request.wechatConfig,
148
+ appAdmins: request.appAdmins,
149
+ roleConfig: request.roleConfig,
150
+ userGroupConfig: request.userGroupConfig,
151
+ feishuConfig: request.feishuConfig,
152
+ openaiConfig: request.openaiConfig,
153
+ };
154
+ }
155
+ export async function getCurrentUser(options) {
156
+ const payload = await requestJson("/account/v2/user", options, {
157
+ authMode: "taleToken",
158
+ errorMessage: "Failed to get current user",
159
+ });
160
+ return normalizeCurrentAppUser(payload);
161
+ }
162
+ export async function listAppsByOrg(orgId, options) {
163
+ assertRequiredString(orgId, "orgId");
164
+ const payload = await requestJson(`/app/v2/org/${encodeURIComponent(orgId)}/apps`, options, {
165
+ authMode: "taleToken",
166
+ errorMessage: "Failed to list apps",
167
+ });
168
+ return Array.isArray(payload) ? payload.map(normalizeAppInfo) : [];
169
+ }
170
+ export async function createApp(request, options) {
171
+ assertRequiredString(request?.appName, "appName");
172
+ assertRequiredString(request?.orgId, "orgId");
173
+ const payload = await requestJson("/app/v2/app", options, {
174
+ method: "POST",
175
+ body: request,
176
+ authMode: "taleToken",
177
+ errorMessage: "Failed to create app",
178
+ });
179
+ return normalizeCreateAppResponse(payload);
180
+ }
181
+ export async function getAppDetail(appKey, options) {
182
+ assertRequiredString(appKey, "appKey");
183
+ const payload = await requestJson(`/app/v2/app/${encodeURIComponent(appKey)}`, options, {
184
+ authMode: "taleToken",
185
+ errorMessage: "Failed to get app detail",
186
+ });
187
+ return normalizeAppDetail(payload);
188
+ }
189
+ export async function updateApp(request, options) {
190
+ assertRequiredString(request?.appId, "appId");
191
+ const payload = await requestJson("/app/v2/app", options, {
192
+ method: "PUT",
193
+ body: toUpdateAppPayload(request),
194
+ authMode: "taleToken",
195
+ errorMessage: "Failed to update app",
196
+ });
197
+ return normalizeAppDetail(payload);
198
+ }
@@ -0,0 +1,101 @@
1
+ import type { CommonOptions, Privilege, Role, UserGroup } from "../common/types.js";
2
+ export type { CommonOptions, Privilege, Role, UserGroup };
3
+ export interface AppInfo {
4
+ appId: string;
5
+ appKey: string;
6
+ appName: string;
7
+ id: string;
8
+ name: string;
9
+ description?: string;
10
+ icon?: string;
11
+ plan?: string;
12
+ orgId?: string;
13
+ appProperty?: Record<string, unknown>;
14
+ appConfig?: Record<string, unknown>;
15
+ appAdmins?: Record<string, unknown>;
16
+ roleConfig?: Record<string, unknown>;
17
+ userGroupConfig?: Record<string, unknown>;
18
+ createdAt?: string;
19
+ updatedAt?: string;
20
+ remark?: string;
21
+ }
22
+ export interface CreateAppRequest {
23
+ appName: string;
24
+ orgId: string;
25
+ remark?: string;
26
+ }
27
+ export interface CreateAppResponse {
28
+ appId: string;
29
+ appName: string;
30
+ orgId: string;
31
+ appKey: string;
32
+ createdAt?: string;
33
+ }
34
+ export interface UpdateAppRequest {
35
+ appId: string;
36
+ appName?: string;
37
+ remark?: string;
38
+ appProperty?: Record<string, unknown>;
39
+ appConfig?: Record<string, unknown>;
40
+ wechatConfig?: Record<string, unknown>;
41
+ appAdmins?: Record<string, unknown>;
42
+ roleConfig?: Record<string, unknown>;
43
+ userGroupConfig?: Record<string, unknown>;
44
+ feishuConfig?: Record<string, unknown>;
45
+ openaiConfig?: Record<string, unknown>;
46
+ }
47
+ export interface AppRole {
48
+ roleId: string;
49
+ roleName: string;
50
+ roleType?: string;
51
+ roleProperty?: Record<string, unknown>;
52
+ roleConfig?: Record<string, unknown>;
53
+ expiredAt?: string;
54
+ remark?: string;
55
+ }
56
+ export interface AppPrivilege {
57
+ privilegeId: string;
58
+ privilegeName: string;
59
+ privilegeType?: string;
60
+ privilegeProperty?: Record<string, unknown>;
61
+ privilegeConfig?: Record<string, unknown>;
62
+ expiredAt?: string;
63
+ remark?: string;
64
+ }
65
+ export interface AppDetail extends AppInfo {
66
+ appRoles: AppRole[];
67
+ appPrivileges: AppPrivilege[];
68
+ wechatConfig?: Record<string, unknown>;
69
+ feishuConfig?: Record<string, unknown>;
70
+ openaiConfig?: Record<string, unknown>;
71
+ }
72
+ export interface AppDetailResponse {
73
+ data: AppDetail;
74
+ code: number;
75
+ msg: string;
76
+ }
77
+ export interface CurrentAppUser {
78
+ app: {
79
+ appName: string;
80
+ appKey: string;
81
+ orgId: string;
82
+ appId: string;
83
+ };
84
+ user: {
85
+ latestLoginTime?: string;
86
+ registeredAt?: string;
87
+ isFrozen?: boolean;
88
+ userId: string;
89
+ phone?: string;
90
+ username?: string;
91
+ email?: string;
92
+ nickname?: string;
93
+ remark?: string;
94
+ avatarUrl?: string;
95
+ };
96
+ thirdParty: Record<string, unknown>;
97
+ userRoles: Role[];
98
+ userPrivileges: Privilege[];
99
+ userGroups: UserGroup[];
100
+ }
101
+ export type AppUserLike = CurrentAppUser;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,7 @@
1
+ import type { PageResponse } from "../common/types.js";
2
+ import type { AppToken, CommonOptions, IssueAppTokenRequest, IssueAppTokenResponse, ListAppTokensRequest } from "./types.js";
3
+ export type { AppSecretResponse, AppToken, CommonOptions, IssueAppTokenRequest, IssueAppTokenResponse, ListAppTokensRequest, PageResponse, } from "./types.js";
4
+ export declare function normalizeAppToken(value: unknown): AppToken;
5
+ export declare function listAppTokens(request?: ListAppTokensRequest & CommonOptions): Promise<PageResponse<AppToken>>;
6
+ export declare function getAppSecret(appKey: string, options?: CommonOptions): Promise<string>;
7
+ export declare function issueAppToken(request: IssueAppTokenRequest): Promise<IssueAppTokenResponse>;
@@ -0,0 +1,58 @@
1
+ import { assertRequiredString, normalizePageResponse, pickString, requestJson, toRecord, } from "../common/http.js";
2
+ export function normalizeAppToken(value) {
3
+ const token = toRecord(value);
4
+ return {
5
+ type: pickString(token.type),
6
+ appKey: pickString(token.appKey),
7
+ token: pickString(token.token),
8
+ status: pickString(token.status),
9
+ expiredAt: pickString(token.expiredAt),
10
+ };
11
+ }
12
+ function toListQuery(request) {
13
+ if (!request) {
14
+ return undefined;
15
+ }
16
+ const sortProperty = request.sort === "expiredAt" ? "expiredTime" : request.sort;
17
+ const sort = sortProperty && request.sortDirection
18
+ ? `${sortProperty},${request.sortDirection}`
19
+ : sortProperty;
20
+ return {
21
+ page: request.page,
22
+ size: request.size,
23
+ isValid: request.isValid,
24
+ sort,
25
+ search: request.search,
26
+ };
27
+ }
28
+ export async function listAppTokens(request) {
29
+ const { appToken, taleToken, baseUrl, ...listRequest } = request ?? {};
30
+ const payload = await requestJson("/app/v2/app/tokens", request, {
31
+ query: toListQuery(listRequest),
32
+ errorMessage: "Failed to list app tokens",
33
+ });
34
+ return normalizePageResponse(payload, normalizeAppToken);
35
+ }
36
+ export async function getAppSecret(appKey, options) {
37
+ assertRequiredString(appKey, "appKey");
38
+ const payload = await requestJson(`/app/v2/app/${encodeURIComponent(appKey)}/secret`, options, {
39
+ authMode: "taleToken",
40
+ errorMessage: "Failed to get app secret",
41
+ });
42
+ const response = toRecord(payload);
43
+ const appSecret = pickString(response.appSecret);
44
+ if (!appSecret) {
45
+ return pickString(payload);
46
+ }
47
+ return appSecret;
48
+ }
49
+ export async function issueAppToken(request) {
50
+ assertRequiredString(request?.appKey, "appKey");
51
+ const { appKey, ...options } = request;
52
+ const payload = await requestJson("/app/v2/token/by-appkey", options, {
53
+ query: { appKey },
54
+ authMode: "taleToken",
55
+ errorMessage: "Failed to issue app token",
56
+ });
57
+ return normalizeAppToken(payload);
58
+ }
@@ -0,0 +1,25 @@
1
+ import type { CommonOptions } from "../common/types.js";
2
+ export type { CommonOptions, PageResponse } from "../common/types.js";
3
+ export interface AppToken {
4
+ type: string;
5
+ appKey: string;
6
+ token: string;
7
+ status: string;
8
+ expiredAt: string;
9
+ }
10
+ export interface ListAppTokensRequest {
11
+ page?: number;
12
+ size?: number;
13
+ isValid?: boolean;
14
+ sort?: string;
15
+ sortDirection?: "asc" | "desc" | "ASC" | "DESC";
16
+ search?: string;
17
+ }
18
+ export interface AppSecretResponse {
19
+ appSecret: string;
20
+ }
21
+ export interface IssueAppTokenRequest extends CommonOptions {
22
+ appKey: string;
23
+ }
24
+ export interface IssueAppTokenResponse extends AppToken {
25
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,17 @@
1
+ import type { CommonOptions, PageResponse } from "./types.js";
2
+ type AuthMode = "appToken" | "taleToken";
3
+ export declare function toRecord(value: unknown): Record<string, unknown>;
4
+ export declare function pickString(...values: unknown[]): string;
5
+ export declare function pickBoolean(...values: unknown[]): boolean | undefined;
6
+ export declare function pickNumber(...values: unknown[]): number | undefined;
7
+ export declare function assertRequiredString(value: string | undefined, fieldName: string): asserts value is string;
8
+ export declare function getBaseUrl(options?: CommonOptions): string;
9
+ export declare function requestJson<T>(path: string, options: CommonOptions | undefined, config: {
10
+ method?: string;
11
+ body?: unknown;
12
+ query?: Record<string, unknown>;
13
+ authMode?: AuthMode;
14
+ errorMessage: string;
15
+ }): Promise<T>;
16
+ export declare function normalizePageResponse<T>(value: unknown, normalizeItem: (item: unknown) => T): PageResponse<T>;
17
+ export {};
@@ -0,0 +1,141 @@
1
+ import { getAppToken } from "../token.js";
2
+ import { ApiError, ConfigurationError, NetworkError } from "../errors.js";
3
+ export function toRecord(value) {
4
+ return value && typeof value === "object" && !Array.isArray(value)
5
+ ? value
6
+ : {};
7
+ }
8
+ export function pickString(...values) {
9
+ for (const value of values) {
10
+ if (typeof value === "string" && value.trim()) {
11
+ return value;
12
+ }
13
+ }
14
+ return "";
15
+ }
16
+ export function pickBoolean(...values) {
17
+ for (const value of values) {
18
+ if (typeof value === "boolean") {
19
+ return value;
20
+ }
21
+ }
22
+ return undefined;
23
+ }
24
+ export function pickNumber(...values) {
25
+ for (const value of values) {
26
+ if (typeof value === "number" && Number.isFinite(value)) {
27
+ return value;
28
+ }
29
+ }
30
+ return undefined;
31
+ }
32
+ export function assertRequiredString(value, fieldName) {
33
+ if (!value || value.trim() === "") {
34
+ throw new ApiError(`${fieldName} is required`, 400, "9400");
35
+ }
36
+ }
37
+ export function getBaseUrl(options) {
38
+ const env = globalThis?.process?.env ?? import.meta?.env ?? undefined;
39
+ const base = options?.baseUrl ??
40
+ env?.TALE_BASE_URL ??
41
+ env?.NEXT_PUBLIC_TALE_BASE_URL ??
42
+ undefined;
43
+ if (!base) {
44
+ throw new ConfigurationError("Missing required environment variable: TALE_BASE_URL");
45
+ }
46
+ return String(base).replace(/\/+$/, "");
47
+ }
48
+ function unwrapApiResponse(json, errorMessage, statusCode) {
49
+ if (typeof json !== "object" || json === null) {
50
+ throw new ApiError(`Invalid response: ${errorMessage} - not an object`, statusCode);
51
+ }
52
+ const response = json;
53
+ if ("code" in response && response.code !== 200 && response.code !== "200") {
54
+ const errorMsg = typeof response.msg === "string" ? response.msg : errorMessage;
55
+ throw new ApiError(errorMsg, statusCode, String(response.code));
56
+ }
57
+ if ("data" in response) {
58
+ if (response.data === undefined || response.data === null) {
59
+ throw new ApiError(`Invalid response: ${errorMessage} - missing data`, statusCode);
60
+ }
61
+ return response.data;
62
+ }
63
+ return json;
64
+ }
65
+ async function resolveToken(options, authMode) {
66
+ if (authMode === "taleToken") {
67
+ const taleToken = options?.taleToken;
68
+ if (!taleToken) {
69
+ throw new ConfigurationError("Missing required option: taleToken");
70
+ }
71
+ return taleToken;
72
+ }
73
+ return options?.appToken ?? (await getAppToken(options));
74
+ }
75
+ export async function requestJson(path, options, config) {
76
+ const token = await resolveToken(options, config.authMode ?? "appToken");
77
+ const url = new URL(getBaseUrl(options) + path);
78
+ Object.entries(config.query ?? {}).forEach(([key, value]) => {
79
+ if (value !== undefined && value !== null) {
80
+ url.searchParams.append(key, String(value));
81
+ }
82
+ });
83
+ let response;
84
+ try {
85
+ response = await globalThis.fetch(url.toString(), {
86
+ method: config.method ?? "GET",
87
+ headers: {
88
+ "Content-Type": "application/json",
89
+ "x-t-token": token,
90
+ },
91
+ body: config.body === undefined ? undefined : JSON.stringify(config.body),
92
+ });
93
+ }
94
+ catch (error) {
95
+ throw new NetworkError(`${config.errorMessage}: ${error instanceof Error ? error.message : "Unknown error"}`);
96
+ }
97
+ let json;
98
+ try {
99
+ json = await response.json();
100
+ }
101
+ catch (error) {
102
+ throw new ApiError(`Failed to parse response: ${error instanceof Error ? error.message : "Invalid JSON"}`, response.status);
103
+ }
104
+ return unwrapApiResponse(json, config.errorMessage, response.status);
105
+ }
106
+ export function normalizePageResponse(value, normalizeItem) {
107
+ const page = toRecord(value);
108
+ const sort = toRecord(page.sort);
109
+ const content = Array.isArray(page.content)
110
+ ? page.content.map(normalizeItem)
111
+ : [];
112
+ const size = pickNumber(page.size) ?? content.length;
113
+ const currentPage = pickNumber(page.page) ?? 0;
114
+ const total = pickNumber(page.total) ?? content.length;
115
+ const totalPages = pickNumber(page.totalPages) ??
116
+ (size > 0 ? Math.ceil(total / size) : 0);
117
+ const orders = Array.isArray(sort.orders)
118
+ ? sort.orders
119
+ : Array.isArray(page.sort)
120
+ ? page.sort
121
+ : [];
122
+ return {
123
+ content,
124
+ total,
125
+ page: currentPage,
126
+ totalPages,
127
+ size,
128
+ numberOfElements: pickNumber(page.numberOfElements) ?? content.length,
129
+ first: pickBoolean(page.first) ?? currentPage === 0,
130
+ last: pickBoolean(page.last) ?? currentPage + 1 >= totalPages,
131
+ empty: pickBoolean(page.empty) ?? content.length === 0,
132
+ sort: orders.map((order) => {
133
+ const item = toRecord(order);
134
+ const direction = pickString(item.direction).toUpperCase();
135
+ return {
136
+ property: pickString(item.property),
137
+ direction: direction === "ASC" ? "ASC" : "DESC",
138
+ };
139
+ }),
140
+ };
141
+ }
@@ -1,6 +1,7 @@
1
1
  export interface CommonOptions {
2
2
  baseUrl?: string;
3
3
  appToken?: string;
4
+ taleToken?: string;
4
5
  }
5
6
  /**
6
7
  * Pagination response format used by Tale backend (PageResponseVO)
@@ -53,12 +54,22 @@ export interface AppInfo {
53
54
  appId: string;
54
55
  appKey: string;
55
56
  appName: string;
57
+ id?: string;
58
+ name?: string;
59
+ description?: string;
60
+ icon?: string;
61
+ plan?: string;
62
+ orgId?: string;
56
63
  appProperty?: Record<string, unknown>;
64
+ appConfig?: Record<string, unknown>;
57
65
  appRoles?: unknown[];
58
66
  appPrivileges?: unknown[];
59
67
  appAdmins?: Record<string, unknown>;
60
68
  roleConfig?: Record<string, unknown>;
61
69
  userGroupConfig?: Record<string, unknown>;
70
+ createdAt?: string;
71
+ updatedAt?: string;
72
+ remark?: string;
62
73
  }
63
74
  export interface User {
64
75
  userId: string;
package/dist/index.d.ts CHANGED
@@ -11,6 +11,8 @@ export * from "./task/index.js";
11
11
  export * from "./cms/index.js";
12
12
  export * from "./attachment/index.js";
13
13
  export * from "./attachment-type/index.js";
14
+ export * from "./app/index.js";
15
+ export * from "./app-token/index.js";
14
16
  export * from "./errors.js";
15
17
  export type { CommonOptions, PageResponse, UserGroup, Role, Privilege, AppInfo, User, } from "./common/types.js";
16
18
  export type { Attachment, AttachmentRefType, ListAttachmentsByRefRequest, UploadAttachmentRequest, UploadAuthorizationRequest, CompleteUploadRequest, DeleteAttachmentRequest, DownloadAttachmentUrlRequest, DownloadUrlResponse, AttachmentOptions, } from "./attachment/types.js";
package/dist/index.js CHANGED
@@ -11,4 +11,6 @@ export * from "./task/index.js";
11
11
  export * from "./cms/index.js";
12
12
  export * from "./attachment/index.js";
13
13
  export * from "./attachment-type/index.js";
14
+ export * from "./app/index.js";
15
+ export * from "./app-token/index.js";
14
16
  export * from "./errors.js";
package/dist/status.js CHANGED
@@ -2,21 +2,21 @@ import pkg from "../package.json" with { type: "json" };
2
2
  import { getAppToken, ConfigurationError, ApiError, NetworkError, } from "./token.js";
3
3
  function normalizeStatusData(data) {
4
4
  return {
5
- serviceName: data?.serviceName ?? data?.service_name,
6
- hostName: data?.hostName ?? data?.host_name,
5
+ serviceName: data?.serviceName,
6
+ hostName: data?.hostName,
7
7
  };
8
8
  }
9
9
  function normalizeAppInfoData(data) {
10
10
  return {
11
- appId: data?.appId ?? data?.app_id,
12
- appKey: data?.appKey ?? data?.app_key,
13
- appName: data?.appName ?? data?.app_name,
14
- appProperty: data?.appProperty ?? data?.app_property ?? {},
15
- appRoles: data?.appRoles ?? data?.app_roles ?? [],
16
- appPrivileges: data?.appPrivileges ?? data?.app_privileges ?? [],
17
- appAdmins: data?.appAdmins ?? data?.app_admins ?? {},
18
- roleConfig: data?.roleConfig ?? data?.role_config ?? {},
19
- userGroupConfig: data?.userGroupConfig ?? data?.user_group_config ?? {},
11
+ appId: data?.appId,
12
+ appKey: data?.appKey,
13
+ appName: data?.appName,
14
+ appProperty: data?.appProperty ?? {},
15
+ appRoles: data?.appRoles ?? [],
16
+ appPrivileges: data?.appPrivileges ?? [],
17
+ appAdmins: data?.appAdmins ?? {},
18
+ roleConfig: data?.roleConfig ?? {},
19
+ userGroupConfig: data?.userGroupConfig ?? {},
20
20
  remark: data?.remark ?? "",
21
21
  };
22
22
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@turinhub/tale-js-sdk",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "description": "Official TypeScript SDK for Tale backend services",
5
5
  "keywords": [
6
6
  "tale",