@lark-apaas/client-toolkit 1.2.1-alpha.21 → 1.2.1-alpha.23

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.
@@ -0,0 +1 @@
1
+ export * from '../../integrations/services';
@@ -0,0 +1 @@
1
+ export * from "../../integrations/services/index.js";
@@ -3,8 +3,7 @@ import { useCallback, useEffect, useMemo, useRef } from "react";
3
3
  import { useLocation, useNavigate } from "react-router-dom";
4
4
  import { connectToParent } from "penpal";
5
5
  import { useUpdatingRef } from "../../hooks/useUpdatingRef.js";
6
- import { submitPostMessage } from "../../utils/postMessage.js";
7
- import { getPreviewParentOrigin } from "../../utils/getParentOrigin.js";
6
+ import { resolveParentOrigin, submitPostMessage } from "../../utils/postMessage.js";
8
7
  import { childApi } from "./utils/childApi.js";
9
8
  import "./utils/listenHot.js";
10
9
  var IframeBridge_RouteMessageType = /*#__PURE__*/ function(RouteMessageType) {
@@ -21,8 +20,10 @@ async function connectParent() {
21
20
  type: 'PreviewReady',
22
21
  data: {}
23
22
  });
23
+ const parentOrigin = resolveParentOrigin();
24
+ if (!parentOrigin) return;
24
25
  const connection = connectToParent({
25
- parentOrigin: getPreviewParentOrigin(),
26
+ parentOrigin,
26
27
  methods: {
27
28
  ...childApi
28
29
  }
@@ -35,13 +36,13 @@ function IframeBridge() {
35
36
  const navigate = useNavigate();
36
37
  const navigateRef = useUpdatingRef(navigate);
37
38
  const isActive = useRef(false);
38
- const historyBack = useCallback(()=>{
39
+ const historyBack = useCallback((_payload)=>{
39
40
  navigateRef.current(-1);
40
41
  isActive.current = true;
41
42
  }, [
42
43
  navigateRef
43
44
  ]);
44
- const historyForward = useCallback(()=>{
45
+ const historyForward = useCallback((_payload)=>{
45
46
  navigateRef.current(1);
46
47
  isActive.current = true;
47
48
  }, [
@@ -69,8 +70,8 @@ function IframeBridge() {
69
70
  location
70
71
  ]);
71
72
  const handleMessage = useCallback((event)=>{
72
- const { data } = event;
73
- if (isRouteMessageType(data?.type)) operatorMessage[data?.type](data?.data);
73
+ const data = event.data ?? {};
74
+ if ('string' == typeof data.type && isRouteMessageType(data.type)) operatorMessage[data.type](data.data);
74
75
  }, [
75
76
  operatorMessage
76
77
  ]);
@@ -0,0 +1,10 @@
1
+ import type { SearchDepartmentsParams, SearchDepartmentsResponse } from './types';
2
+ export type DepartmentServiceConfig = {
3
+ getAppId?: () => string | null | undefined;
4
+ searchDepartmentUrl?: (appId: string) => string;
5
+ };
6
+ export declare class DepartmentService {
7
+ private config;
8
+ constructor(config?: DepartmentServiceConfig);
9
+ searchDepartments(params: SearchDepartmentsParams): Promise<SearchDepartmentsResponse>;
10
+ }
@@ -0,0 +1,29 @@
1
+ import { getAppId } from "../../utils/getAppId.js";
2
+ const DEFAULT_CONFIG = {
3
+ getAppId: ()=>getAppId(window.location.pathname),
4
+ searchDepartmentUrl: (appId)=>`/af/app/${appId}/runtime/api/v1/account/search_department`
5
+ };
6
+ class DepartmentService {
7
+ config;
8
+ constructor(config = {}){
9
+ this.config = {
10
+ ...DEFAULT_CONFIG,
11
+ ...config
12
+ };
13
+ }
14
+ async searchDepartments(params) {
15
+ const appId = this.config.getAppId();
16
+ if (!appId) throw new Error('Failed to get appId');
17
+ const response = await fetch(this.config.searchDepartmentUrl(appId), {
18
+ method: 'POST',
19
+ headers: {
20
+ 'Content-Type': 'application/json'
21
+ },
22
+ body: JSON.stringify(params),
23
+ credentials: 'include'
24
+ });
25
+ if (!response.ok) throw new Error('Failed to search departments');
26
+ return response.json();
27
+ }
28
+ }
29
+ export { DepartmentService };
@@ -0,0 +1,14 @@
1
+ import type { AccountType, UserProfileData } from './types';
2
+ /**
3
+ * 获取 CDN 资源 URL
4
+ */
5
+ export declare function getAssetsUrl(path: string): string;
6
+ export type UserProfileServiceConfig = {
7
+ getAppId?: () => string | null | undefined;
8
+ userProfileUrl?: (appId: string) => string;
9
+ };
10
+ export declare class UserProfileService {
11
+ private config;
12
+ constructor(config?: UserProfileServiceConfig);
13
+ getUserProfile(userId: string, accountType?: AccountType, signal?: AbortSignal): Promise<UserProfileData>;
14
+ }
@@ -0,0 +1,36 @@
1
+ import { getAppId } from "../../utils/getAppId.js";
2
+ const CDN_HOST = 'https://lf3-static.bytednsdoc.com';
3
+ function getAssetsUrl(path) {
4
+ return `${CDN_HOST}${path}`;
5
+ }
6
+ const DEFAULT_CONFIG = {
7
+ getAppId: ()=>getAppId(window.location.pathname),
8
+ userProfileUrl: (appId)=>`/af/app/${appId}/runtime/api/v1/account/user_profile`
9
+ };
10
+ class UserProfileService {
11
+ config;
12
+ constructor(config = {}){
13
+ this.config = {
14
+ ...DEFAULT_CONFIG,
15
+ ...config
16
+ };
17
+ }
18
+ async getUserProfile(userId, accountType = 'apaas', signal) {
19
+ const appId = this.config.getAppId();
20
+ if (!appId) throw new Error('Failed to get appId');
21
+ const params = new URLSearchParams();
22
+ if ('lark' === accountType) params.append('larkUserID', userId);
23
+ else params.append('userID', userId);
24
+ const response = await fetch(`${this.config.userProfileUrl(appId)}?${params.toString()}`, {
25
+ signal,
26
+ headers: {
27
+ 'Content-Type': 'application/json'
28
+ },
29
+ credentials: 'include'
30
+ });
31
+ if (!response.ok) throw new Error(`Failed to fetch user profile: ${response.status}`);
32
+ const data = await response.json();
33
+ return data.data;
34
+ }
35
+ }
36
+ export { UserProfileService, getAssetsUrl };
@@ -0,0 +1,12 @@
1
+ import type { BatchGetUsersResponse, SearchUsersParams, SearchUsersResponse } from './types';
2
+ export type UserServiceConfig = {
3
+ getAppId?: () => string | null | undefined;
4
+ searchUserUrl?: (appId: string) => string;
5
+ listUsersUrl?: (appId: string) => string;
6
+ };
7
+ export declare class UserService {
8
+ private config;
9
+ constructor(config?: UserServiceConfig);
10
+ searchUsers(params: SearchUsersParams): Promise<SearchUsersResponse>;
11
+ listUsersByIds(userIds: string[]): Promise<BatchGetUsersResponse>;
12
+ }
@@ -0,0 +1,46 @@
1
+ import { getAppId } from "../../utils/getAppId.js";
2
+ const DEFAULT_CONFIG = {
3
+ getAppId: ()=>getAppId(window.location.pathname),
4
+ searchUserUrl: (appId)=>`/af/app/${appId}/runtime/api/v1/account/search_user`,
5
+ listUsersUrl: (appId)=>`/af/app/${appId}/runtime/api/v1/account/list_users`
6
+ };
7
+ class UserService {
8
+ config;
9
+ constructor(config = {}){
10
+ this.config = {
11
+ ...DEFAULT_CONFIG,
12
+ ...config
13
+ };
14
+ }
15
+ async searchUsers(params) {
16
+ const appId = this.config.getAppId();
17
+ if (!appId) throw new Error('Failed to get appId');
18
+ const response = await fetch(this.config.searchUserUrl(appId), {
19
+ method: 'POST',
20
+ headers: {
21
+ 'Content-Type': 'application/json'
22
+ },
23
+ body: JSON.stringify(params),
24
+ credentials: 'include'
25
+ });
26
+ if (!response.ok) throw new Error('Failed to search users');
27
+ return response.json();
28
+ }
29
+ async listUsersByIds(userIds) {
30
+ const appId = this.config.getAppId();
31
+ if (!appId) throw new Error('Failed to get appId');
32
+ const response = await fetch(this.config.listUsersUrl(appId), {
33
+ method: 'POST',
34
+ headers: {
35
+ 'Content-Type': 'application/json'
36
+ },
37
+ body: JSON.stringify({
38
+ userIDList: userIds.map((id)=>Number(id))
39
+ }),
40
+ credentials: 'include'
41
+ });
42
+ if (!response.ok) throw new Error('Failed to fetch users by ids');
43
+ return response.json();
44
+ }
45
+ }
46
+ export { UserService };
@@ -0,0 +1,4 @@
1
+ export * from './types';
2
+ export * from './UserService';
3
+ export * from './DepartmentService';
4
+ export * from './UserProfileService';
@@ -0,0 +1,4 @@
1
+ export * from "./types.js";
2
+ export * from "./UserService.js";
3
+ export * from "./DepartmentService.js";
4
+ export * from "./UserProfileService.js";
@@ -0,0 +1,91 @@
1
+ export type I18nText = {
2
+ zh_cn: string;
3
+ en_us?: string;
4
+ ja_jp?: string;
5
+ };
6
+ export type DepartmentBasic = {
7
+ departmentID: string;
8
+ name: I18nText;
9
+ };
10
+ export type UserInfo = {
11
+ userID: string;
12
+ larkUserID: string;
13
+ name: I18nText;
14
+ avatar: string;
15
+ userType: '_employee' | '_externalUser' | '_anonymousUser';
16
+ department: DepartmentBasic;
17
+ };
18
+ export type DepartmentInfo = {
19
+ departmentID: string;
20
+ larkDepartmentID: string;
21
+ name: I18nText;
22
+ };
23
+ export type AccountType = 'apaas' | 'lark';
24
+ export type SearchAvatar = {
25
+ avatar: {
26
+ image: {
27
+ large: string;
28
+ };
29
+ };
30
+ };
31
+ export type SearchUsersParams = {
32
+ query?: string;
33
+ offset?: number;
34
+ pageSize?: number;
35
+ };
36
+ export type SearchUsersResponse = {
37
+ data: {
38
+ userList: UserInfo[];
39
+ total: number;
40
+ };
41
+ status_code: string;
42
+ };
43
+ export type BatchGetUsersResponse = {
44
+ data: {
45
+ userInfoMap: Record<string, UserInfo & SearchAvatar>;
46
+ baseResp?: {
47
+ statusCode?: number;
48
+ statusMessage?: string;
49
+ };
50
+ };
51
+ };
52
+ export type SearchDepartmentsParams = {
53
+ query?: string;
54
+ offset?: number;
55
+ pageSize?: number;
56
+ };
57
+ export type SearchDepartmentsResponse = {
58
+ data: {
59
+ departmentList: DepartmentInfo[];
60
+ total: number;
61
+ };
62
+ status_code: string;
63
+ };
64
+ export type UserProfileAccountStatus = 0 | 1 | 2 | 3 | 4;
65
+ export type SimpleUserProfileInfo = {
66
+ name?: string;
67
+ avatar?: string;
68
+ email?: string;
69
+ userStatus: UserProfileAccountStatus;
70
+ userType: '_employee' | '_externalUser';
71
+ };
72
+ export type LarkCardParam = {
73
+ needRedirect?: boolean;
74
+ redirectURL?: string;
75
+ larkAppID: string;
76
+ jsAPITicket: string;
77
+ larkOpenID: string;
78
+ targetLarkOpenID: string;
79
+ };
80
+ export type SimpleUserProfileData = {
81
+ useLarkCard: false;
82
+ userProfileInfo: SimpleUserProfileInfo;
83
+ };
84
+ export type OfficialUserProfileData = {
85
+ useLarkCard: true;
86
+ larkCardParam: LarkCardParam;
87
+ };
88
+ export type UserProfileData = SimpleUserProfileData | OfficialUserProfileData;
89
+ export type UserProfileResponse = {
90
+ data: UserProfileData;
91
+ };
File without changes
@@ -14,4 +14,4 @@ declare module 'axios' {
14
14
  * @param originalInstance 原始的 axios 实例
15
15
  */
16
16
  export declare function wrapAxiosWithTrace(originalInstance: AxiosInstance): AxiosInstance;
17
- export declare function initAxiosConfig(axiosInstance?: AxiosInstance): AxiosInstance;
17
+ export declare function initAxiosConfig(axiosInstance?: AxiosInstance): void;
@@ -140,9 +140,9 @@ function wrapAxiosWithTrace(originalInstance) {
140
140
  let span;
141
141
  try {
142
142
  if (!finalConfig.__span) {
143
- const url = finalConfig.url || '';
143
+ const fullUrl = originalInstance.getUri(finalConfig);
144
144
  const actualMethod = (finalConfig.method || 'GET').toUpperCase();
145
- const cleanedPath = url.split('?')[0].replace(/^\/spark\/p\/app_\w+/, '') || '/';
145
+ const cleanedPath = fullUrl.split('?')[0].replace(/^https?:\/\/[^/]+/, '') || '/';
146
146
  span = observable.startSpan(`${actualMethod} ${cleanedPath}`);
147
147
  if (span) {
148
148
  const spanContext = span.spanContext();
@@ -176,7 +176,7 @@ function wrapAxiosWithTrace(originalInstance) {
176
176
  const startTime = cfg._startTime;
177
177
  const url = response?.request?.responseURL || errorResponse?.request?.responseURL || cfg.url || "";
178
178
  const method = (cfg.method || 'GET').toUpperCase();
179
- const path = url.split('?')[0] || '/';
179
+ const path = url.split('?')[0].replace(/^https?:\/\/[^/]+/, '') || '/';
180
180
  const logData = {
181
181
  method,
182
182
  path,
@@ -186,7 +186,13 @@ function wrapAxiosWithTrace(originalInstance) {
186
186
  };
187
187
  if (error) logData.error_message = errorMessage;
188
188
  if ('undefined' != typeof navigator) logData.user_agent = navigator.userAgent;
189
- if (cfg.data) logData.request_body = cfg.data;
189
+ if (cfg.data) try {
190
+ const parsed = JSON.parse(cfg.data);
191
+ if (parsed) logData.request_body = parsed;
192
+ else logData.request_body = cfg.data;
193
+ } catch {
194
+ logData.request_body = cfg.data;
195
+ }
190
196
  const responseData = response?.data || errorResponse?.data;
191
197
  if (responseData) logData.response = responseData;
192
198
  const level = error ? 'ERROR' : 'INFO';
@@ -209,9 +215,8 @@ function wrapAxiosWithTrace(originalInstance) {
209
215
  });
210
216
  }
211
217
  function initAxiosConfig(axiosInstance) {
212
- const isGlobal = !axiosInstance;
213
- const instance = axiosInstance || axios;
214
- instance.interceptors.request.use((config)=>{
218
+ if (!axiosInstance) axiosInstance = axios;
219
+ axiosInstance.interceptors.request.use((config)=>{
215
220
  const requestUUID = crypto.randomUUID();
216
221
  if ('production' !== process.env.NODE_ENV) {
217
222
  const stacktrace = getStacktrace();
@@ -223,29 +228,12 @@ function initAxiosConfig(axiosInstance) {
223
228
  if (csrfToken) config.headers['X-Suda-Csrf-Token'] = csrfToken;
224
229
  return config;
225
230
  }, (error)=>Promise.reject(error));
226
- 'production' !== process.env.NODE_ENV && instance.interceptors.response.use((response)=>{
231
+ 'production' !== process.env.NODE_ENV && axiosInstance.interceptors.response.use((response)=>{
227
232
  logResponse('success', response);
228
233
  return response;
229
234
  }, (error)=>{
230
235
  logResponse('error', error.response || error);
231
236
  return Promise.reject(error);
232
237
  });
233
- const proxied = wrapAxiosWithTrace(instance);
234
- if (isGlobal) {
235
- const methods = [
236
- 'request',
237
- 'get',
238
- 'post',
239
- 'put',
240
- 'delete',
241
- 'patch',
242
- 'head',
243
- 'options'
244
- ];
245
- methods.forEach((method)=>{
246
- instance[method] = proxied[method];
247
- });
248
- }
249
- return proxied;
250
238
  }
251
239
  export { initAxiosConfig, wrapAxiosWithTrace };
@@ -1,6 +1 @@
1
1
  export declare function getEnv(): 'BOE' | 'PRE' | 'ONLINE';
2
- /**
3
- * @internal
4
- * 获取预览环境父级域名
5
- */
6
- export declare function getPreviewParentOrigin(): "https://force.feishu.cn" | "https://force.feishu-pre.cn" | "https://force.feishu-boe.cn" | "https://miaoda.feishu.cn" | "https://miaoda.feishu-pre.cn" | "https://miaoda.feishu-boe.cn";
@@ -4,13 +4,4 @@ function getEnv() {
4
4
  if (origin.includes('fsapp.kundou.cn') || origin.includes('miaoda-pre.feishuapp.net')) return 'PRE';
5
5
  return 'BOE';
6
6
  }
7
- function getPreviewParentOrigin() {
8
- const { origin } = window.location;
9
- if (origin.includes('force.feishuapp.net')) return 'https://force.feishu.cn';
10
- if (origin.includes('force-pre.feishuapp.net')) return 'https://force.feishu-pre.cn';
11
- if (origin.includes('force.byted.org')) return 'https://force.feishu-boe.cn';
12
- if (origin.includes('feishuapp.cn') || origin.includes('miaoda.feishuapp.net')) return 'https://miaoda.feishu.cn';
13
- if (origin.includes('fsapp.kundou.cn') || origin.includes('miaoda-pre.feishuapp.net')) return 'https://miaoda.feishu-pre.cn';
14
- return 'https://miaoda.feishu-boe.cn';
15
- }
16
- export { getEnv, getPreviewParentOrigin };
7
+ export { getEnv };
@@ -1,6 +1,7 @@
1
1
  import type { IncomingMessage, OutgoingMessage } from '../types/iframe-events';
2
+ export declare function resolveParentOrigin(): string;
2
3
  export declare function submitPostMessage<T extends OutgoingMessage>(message: T, targetOrigin?: string): void;
3
- export declare function submitSlardarEvent(event: any): void;
4
+ export declare function submitSlardarEvent(event: unknown): void;
4
5
  export declare function isOutgoingMessage<T extends OutgoingMessage['type']>(msg: OutgoingMessage, type: T): msg is Extract<OutgoingMessage, {
5
6
  type: T;
6
7
  }>;
@@ -1,14 +1,28 @@
1
- import { getPreviewParentOrigin } from "./getParentOrigin.js";
1
+ function getLegacyParentOrigin() {
2
+ const { origin } = window.location;
3
+ if (origin.includes('force.feishuapp.net')) return 'https://force.feishu.cn';
4
+ if (origin.includes('force-pre.feishuapp.net')) return 'https://force.feishu-pre.cn';
5
+ if (origin.includes('force.byted.org')) return 'https://force.feishu-boe.cn';
6
+ if (origin.includes('feishuapp.cn') || origin.includes('miaoda.feishuapp.net')) return 'https://miaoda.feishu.cn';
7
+ if (origin.includes('fsapp.kundou.cn') || origin.includes('miaoda-pre.feishuapp.net')) return 'https://miaoda.feishu-pre.cn';
8
+ return 'https://miaoda.feishu-boe.cn';
9
+ }
10
+ function resolveParentOrigin() {
11
+ return process.env?.FORCE_FRAMEWORK_DOMAIN_MAIN ?? getLegacyParentOrigin();
12
+ }
2
13
  function submitPostMessage(message, targetOrigin) {
3
14
  try {
4
- window.parent.postMessage(message, targetOrigin ?? getPreviewParentOrigin());
15
+ const parentOrigin = resolveParentOrigin();
16
+ const origin = targetOrigin ?? parentOrigin;
17
+ if (!origin) return;
18
+ window.parent.postMessage(message, origin);
5
19
  } catch (e) {
6
20
  console.error('postMessage error', e);
7
21
  }
8
22
  }
9
23
  function submitSlardarEvent(event) {
10
- const slardar = window['KSlardarWeb'];
11
- if (slardar && 'function' == typeof slardar) slardar('sendEvent', event);
24
+ const slardar = window.KSlardarWeb;
25
+ if ('function' == typeof slardar) slardar('sendEvent', event);
12
26
  else console.warn('hmr listen function not found');
13
27
  }
14
28
  function isOutgoingMessage(msg, type) {
@@ -17,4 +31,4 @@ function isOutgoingMessage(msg, type) {
17
31
  function isIncomingMessage(msg, type) {
18
32
  return msg.type === type;
19
33
  }
20
- export { isIncomingMessage, isOutgoingMessage, submitPostMessage, submitSlardarEvent };
34
+ export { isIncomingMessage, isOutgoingMessage, resolveParentOrigin, submitPostMessage, submitSlardarEvent };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lark-apaas/client-toolkit",
3
- "version": "1.2.1-alpha.21",
3
+ "version": "1.2.1-alpha.23",
4
4
  "types": "./lib/index.d.ts",
5
5
  "main": "./lib/index.js",
6
6
  "files": [