@lark-apaas/client-toolkit 1.2.9 → 1.2.34

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 (95) hide show
  1. package/lib/antd-table.d.ts +2 -2
  2. package/lib/apis/aily-chat.d.ts +2 -0
  3. package/lib/apis/aily-chat.js +2 -0
  4. package/lib/apis/hooks/useScrollReveal.d.ts +1 -0
  5. package/lib/apis/hooks/useScrollReveal.js +1 -0
  6. package/lib/apis/udt-types.d.ts +4 -0
  7. package/lib/apis/utils/resolveAppUrl.d.ts +1 -0
  8. package/lib/apis/utils/resolveAppUrl.js +2 -0
  9. package/lib/apis/utils/scopedStorage.d.ts +1 -0
  10. package/lib/apis/utils/scopedStorage.js +2 -0
  11. package/lib/auth.d.ts +2 -1
  12. package/lib/auth.js +2 -2
  13. package/lib/components/AppContainer/api-proxy/core.js +2 -1
  14. package/lib/components/AppContainer/index.d.ts +5 -1
  15. package/lib/components/AppContainer/index.js +41 -34
  16. package/lib/components/AppContainer/safety.js +27 -8
  17. package/lib/components/AppContainer/utils/childApi.js +1 -0
  18. package/lib/components/AppContainer/utils/getLarkUser.js +4 -2
  19. package/lib/components/AppContainer/utils/observable.js +7 -1
  20. package/lib/components/AppContainer/utils/tea.js +1 -1
  21. package/lib/components/ErrorRender/index.js +5 -11
  22. package/lib/components/User/UserSelect.js +1 -13
  23. package/lib/components/theme/index.d.ts +0 -1
  24. package/lib/components/theme/index.js +0 -1
  25. package/lib/components/theme/util.d.ts +0 -2
  26. package/lib/components/theme/util.js +0 -108
  27. package/lib/components/ui/badge.d.ts +1 -1
  28. package/lib/components/ui/button.d.ts +2 -2
  29. package/lib/components/ui/button.js +1 -1
  30. package/lib/components/ui/confirm.d.ts +28 -0
  31. package/lib/components/ui/confirm.js +83 -0
  32. package/lib/components/ui/toast.d.ts +2 -0
  33. package/lib/components/ui/toast.js +53 -0
  34. package/lib/hooks/index.d.ts +1 -0
  35. package/lib/hooks/index.js +1 -0
  36. package/lib/hooks/useCurrentUserProfile.d.ts +18 -3
  37. package/lib/hooks/useCurrentUserProfile.js +38 -29
  38. package/lib/hooks/useLogout.js +2 -17
  39. package/lib/hooks/useScrollReveal.d.ts +61 -0
  40. package/lib/hooks/useScrollReveal.js +37 -0
  41. package/lib/index.d.ts +2 -0
  42. package/lib/index.js +14 -2
  43. package/lib/integrations/dataloom.d.ts +3 -1
  44. package/lib/integrations/dataloom.js +18 -10
  45. package/lib/integrations/getCurrentUserProfile.d.ts +7 -1
  46. package/lib/integrations/services/ChatService.d.ts +12 -0
  47. package/lib/integrations/services/ChatService.js +67 -0
  48. package/lib/integrations/services/DepartmentService.js +3 -2
  49. package/lib/integrations/services/UserProfileService.js +3 -2
  50. package/lib/integrations/services/UserService.d.ts +3 -1
  51. package/lib/integrations/services/UserService.js +23 -3
  52. package/lib/integrations/services/index.d.ts +1 -0
  53. package/lib/integrations/services/index.js +1 -0
  54. package/lib/integrations/services/types.d.ts +45 -0
  55. package/lib/logger/intercept-global-error.js +34 -25
  56. package/lib/logger/log-types.d.ts +4 -4
  57. package/lib/logger/log-types.js +1 -1
  58. package/lib/runtime/index.d.ts +1 -0
  59. package/lib/runtime/index.js +1 -0
  60. package/lib/runtime/react-devtools-hook.d.ts +19 -0
  61. package/lib/runtime/react-devtools-hook.js +20 -0
  62. package/lib/theme-layer.css +2 -1
  63. package/lib/utils/apiPath.d.ts +5 -0
  64. package/lib/utils/apiPath.js +5 -0
  65. package/lib/utils/axiosConfig.js +163 -9
  66. package/lib/utils/getAppId.d.ts +2 -4
  67. package/lib/utils/getAppId.js +2 -9
  68. package/lib/utils/getInitialInfo.d.ts +4 -3
  69. package/lib/utils/getInitialInfo.js +17 -8
  70. package/lib/utils/getUserProfile.js +4 -12
  71. package/lib/utils/hmr-api.d.ts +45 -0
  72. package/lib/utils/hmr-api.js +36 -0
  73. package/lib/utils/module-hot.d.ts +9 -5
  74. package/lib/utils/module-hot.js +9 -10
  75. package/lib/utils/postMessage.d.ts +0 -1
  76. package/lib/utils/postMessage.js +19 -6
  77. package/lib/utils/requestManager.js +1 -3
  78. package/lib/utils/resolveAppUrl.d.ts +27 -0
  79. package/lib/utils/resolveAppUrl.js +19 -0
  80. package/lib/utils/safeStringify.js +5 -0
  81. package/lib/utils/safeStringify.spec.d.ts +1 -0
  82. package/lib/utils/safeStringify.spec.js +125 -0
  83. package/lib/utils/scopedStorage.d.ts +5 -0
  84. package/lib/utils/scopedStorage.js +46 -0
  85. package/package.json +13 -6
  86. package/lib/apis/tools/generateImage.d.ts +0 -1
  87. package/lib/apis/tools/generateImage.js +0 -1
  88. package/lib/apis/tools/generateTextStream.d.ts +0 -1
  89. package/lib/apis/tools/generateTextStream.js +0 -1
  90. package/lib/components/theme/ui-config.d.ts +0 -1
  91. package/lib/components/theme/ui-config.js +0 -2
  92. package/lib/integrations/generateImage.d.ts +0 -1
  93. package/lib/integrations/generateImage.js +0 -47
  94. package/lib/integrations/generateTextStream.d.ts +0 -21
  95. package/lib/integrations/generateTextStream.js +0 -98
@@ -1,8 +1,10 @@
1
1
  import { getAppId } from "../../utils/getAppId.js";
2
+ import { isNewPathEnabled } from "../../utils/apiPath.js";
2
3
  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`
4
+ getAppId: ()=>getAppId(),
5
+ searchUserUrl: (appId)=>isNewPathEnabled() ? `/app/${appId}/__runtime__/api/v1/account/search_user` : `/af/app/${appId}/runtime/api/v1/account/search_user`,
6
+ listUsersUrl: (appId)=>isNewPathEnabled() ? `/app/${appId}/__runtime__/api/v1/account/list_users` : `/af/app/${appId}/runtime/api/v1/account/list_users`,
7
+ convertExternalContactUrl: (appId)=>isNewPathEnabled() ? `/app/${appId}/__runtime__/api/v1/account/convert_lark_user` : `/af/app/${appId}/runtime/api/v1/account/convert_lark_user`
6
8
  };
7
9
  class UserService {
8
10
  config;
@@ -42,5 +44,23 @@ class UserService {
42
44
  if (!response.ok) throw new Error('Failed to fetch users by ids');
43
45
  return response.json();
44
46
  }
47
+ async convertExternalContact(larkUserID) {
48
+ const appId = this.config.getAppId();
49
+ if (!appId) throw new Error('Failed to get appId');
50
+ const response = await fetch(this.config.convertExternalContactUrl(appId), {
51
+ method: 'POST',
52
+ headers: {
53
+ 'Content-Type': 'application/json'
54
+ },
55
+ body: JSON.stringify({
56
+ larkUserID
57
+ }),
58
+ credentials: 'include'
59
+ });
60
+ if (!response.ok) throw new Error('Failed to convert external contact');
61
+ const data = await response.json();
62
+ if (!data?.data?.userInfo?.userID) throw new Error('Invalid response from convert external contact');
63
+ return data;
64
+ }
45
65
  }
46
66
  export { UserService };
@@ -1,4 +1,5 @@
1
1
  export * from './types';
2
2
  export * from './UserService';
3
3
  export * from './DepartmentService';
4
+ export * from './ChatService';
4
5
  export * from './UserProfileService';
@@ -1,4 +1,5 @@
1
1
  export * from "./types.js";
2
2
  export * from "./UserService.js";
3
3
  export * from "./DepartmentService.js";
4
+ export * from "./ChatService.js";
4
5
  export * from "./UserProfileService.js";
@@ -14,6 +14,8 @@ export type UserInfo = {
14
14
  avatar: string;
15
15
  userType: '_employee' | '_externalUser' | '_anonymousUser';
16
16
  department: DepartmentBasic;
17
+ email?: string;
18
+ tenantName?: string;
17
19
  };
18
20
  export type DepartmentInfo = {
19
21
  departmentID: string;
@@ -32,6 +34,7 @@ export type SearchUsersParams = {
32
34
  query?: string;
33
35
  offset?: number;
34
36
  pageSize?: number;
37
+ searchExternalContact?: boolean;
35
38
  };
36
39
  export type SearchUsersResponse = {
37
40
  data: {
@@ -40,6 +43,14 @@ export type SearchUsersResponse = {
40
43
  };
41
44
  status_code: string;
42
45
  };
46
+ export type ConvertExternalContactResponse = {
47
+ data: {
48
+ userInfo: {
49
+ tenantID: number;
50
+ userID: number;
51
+ };
52
+ };
53
+ };
43
54
  export type BatchGetUsersResponse = {
44
55
  data: {
45
56
  userInfoMap: Record<string, UserInfo & SearchAvatar>;
@@ -61,6 +72,40 @@ export type SearchDepartmentsResponse = {
61
72
  };
62
73
  status_code: string;
63
74
  };
75
+ export type ChatInfo = {
76
+ /** 群组 ID */
77
+ chatID: string;
78
+ /** 群组名称(国际化文本) */
79
+ name: I18nText;
80
+ /** 头像:URL 或 16 进制 RGB 颜色 */
81
+ avatar: string;
82
+ /** 是否是外部群 */
83
+ isExternal?: boolean;
84
+ /** 群成员数量(不包括机器人),搜索接口返回,批量查询接口不返回 */
85
+ userCount?: number;
86
+ };
87
+ export type SearchChatsParams = {
88
+ query?: string;
89
+ offset?: number;
90
+ pageSize?: number;
91
+ };
92
+ export type SearchChatsResponse = {
93
+ data: {
94
+ result: {
95
+ chatResult?: {
96
+ total: number;
97
+ items: ChatInfo[];
98
+ };
99
+ };
100
+ };
101
+ status_code: string;
102
+ };
103
+ export type BatchGetChatsResponse = {
104
+ data: {
105
+ chatInfoMap: Record<string, ChatInfo>;
106
+ };
107
+ status_code: string;
108
+ };
64
109
  export type UserProfileAccountStatus = 0 | 1 | 2 | 3 | 4;
65
110
  export type SimpleUserProfileInfo = {
66
111
  name?: string;
@@ -1,5 +1,6 @@
1
- import { createApplyHandle, getModuleHot } from "../utils/module-hot.js";
2
- import { submitPostMessage, submitSlardarEvent } from "../utils/postMessage.js";
1
+ import { slardar } from "@lark-apaas/internal-slardar";
2
+ import { getHmrApi } from "../utils/hmr-api.js";
3
+ import { submitPostMessage } from "../utils/postMessage.js";
3
4
  import { levelSchema } from "./log-types.js";
4
5
  import { logger } from "./logger.js";
5
6
  let devServerDisconnectInfo = null;
@@ -19,22 +20,26 @@ function processDevServerLog(log) {
19
20
  status: 'disconnected'
20
21
  }
21
22
  });
22
- submitSlardarEvent({
23
+ slardar.sendEvent({
23
24
  name: 'sandbox-devServer',
24
- categories: {
25
- type: 'disconnected',
25
+ metrics: {
26
26
  time
27
+ },
28
+ categories: {
29
+ type: 'disconnected'
27
30
  }
28
31
  });
29
32
  return;
30
33
  }
31
34
  if (!devServerDisconnectInfo) return;
32
35
  if (devFlag && log.includes('Trying to reconnect')) {
33
- if (retryCount) submitSlardarEvent({
36
+ if (retryCount) slardar.sendEvent({
34
37
  name: 'sandbox-devServer',
35
- categories: {
36
- type: 'reconnect-failed',
38
+ metrics: {
37
39
  retryCount: retryCount + 1
40
+ },
41
+ categories: {
42
+ type: 'reconnect-failed'
38
43
  }
39
44
  });
40
45
  retryCount++;
@@ -52,37 +57,41 @@ function processDevServerLog(log) {
52
57
  const startTime = devServerDisconnectInfo.time;
53
58
  const duration = Date.now() - startTime;
54
59
  devServerDisconnectInfo = null;
55
- submitSlardarEvent({
60
+ slardar.sendEvent({
56
61
  name: 'sandbox-devServer',
57
- categories: {
58
- type: 'devServer-reconnected',
62
+ metrics: {
59
63
  startTime,
60
64
  duration
65
+ },
66
+ categories: {
67
+ type: 'devServer-reconnected'
61
68
  }
62
69
  });
63
70
  }
64
71
  }
65
72
  function listenModuleHmr() {
66
- const hot = getModuleHot();
67
- if (hot) hot.addStatusHandler(createApplyHandle((success, status)=>{
68
- if (success) submitPostMessage({
69
- type: 'DevServerMessage',
70
- data: {
71
- type: 'devServer-status',
72
- status: 'hmr-apply-success'
73
- }
73
+ const hmr = getHmrApi();
74
+ if (hmr) {
75
+ hmr.onSuccess(()=>{
76
+ submitPostMessage({
77
+ type: 'DevServerMessage',
78
+ data: {
79
+ type: 'devServer-status',
80
+ status: 'hmr-apply-success'
81
+ }
82
+ });
74
83
  });
75
- else {
76
- console.warn('hmr apply failed', status);
77
- submitSlardarEvent({
84
+ hmr.onError((error)=>{
85
+ console.warn('hmr apply failed', error);
86
+ slardar.sendEvent({
78
87
  name: 'sandbox-devServer',
79
88
  categories: {
80
89
  type: 'hmr-apply-failed',
81
- status
90
+ error: String(error)
82
91
  }
83
92
  });
84
- }
85
- }));
93
+ });
94
+ }
86
95
  }
87
96
  function interceptErrors() {
88
97
  window.addEventListener('error', (event)=>{
@@ -1,7 +1,7 @@
1
1
  import z from 'zod';
2
2
  export declare const networkRequestLogSchema: z.ZodObject<{
3
3
  method: z.ZodString;
4
- status: z.ZodInt;
4
+ status: z.ZodNumber;
5
5
  statusText: z.ZodString;
6
6
  path: z.ZodString;
7
7
  requestData: z.ZodOptional<z.ZodUnknown>;
@@ -27,11 +27,11 @@ export declare const logStackFrameSchema: z.ZodObject<{
27
27
  columnNumber: z.ZodNumber;
28
28
  }, z.core.$strip>;
29
29
  export declare const levelSchema: z.ZodEnum<{
30
+ error: "error";
30
31
  success: "success";
31
32
  debug: "debug";
32
33
  info: "info";
33
34
  warn: "warn";
34
- error: "error";
35
35
  }>;
36
36
  export declare const logMeta: z.ZodObject<{
37
37
  stacktrace: z.ZodOptional<z.ZodArray<z.ZodObject<{
@@ -52,11 +52,11 @@ export declare const logMeta: z.ZodObject<{
52
52
  export declare const selectedLogSchema: z.ZodObject<{
53
53
  type: z.ZodLiteral<"typedLogV2">;
54
54
  level: z.ZodEnum<{
55
+ error: "error";
55
56
  success: "success";
56
57
  debug: "debug";
57
58
  info: "info";
58
59
  warn: "warn";
59
- error: "error";
60
60
  }>;
61
61
  id: z.ZodString;
62
62
  args: z.ZodArray<z.ZodUnknown>;
@@ -80,11 +80,11 @@ export declare const selectedLogSchema: z.ZodObject<{
80
80
  export type LogLevel = z.infer<typeof levelSchema>;
81
81
  export declare const logWithMetaSchema: z.ZodObject<{
82
82
  level: z.ZodEnum<{
83
+ error: "error";
83
84
  success: "success";
84
85
  debug: "debug";
85
86
  info: "info";
86
87
  warn: "warn";
87
- error: "error";
88
88
  }>;
89
89
  args: z.ZodArray<z.ZodUnknown>;
90
90
  meta: z.ZodOptional<z.ZodObject<{
@@ -1,7 +1,7 @@
1
1
  import zod from "zod";
2
2
  const networkRequestLogSchema = zod.object({
3
3
  method: zod.string(),
4
- status: zod.int(),
4
+ status: zod.number().int(),
5
5
  statusText: zod.string(),
6
6
  path: zod.string(),
7
7
  requestData: zod.optional(zod.unknown()),
@@ -15,6 +15,7 @@
15
15
  * - 使用 __FULLSTACK_RUNTIME_INITIALIZED__ 标志位防止重复初始化
16
16
  * - 旧版 AppContainer 和新版 runtime 可以共存
17
17
  */
18
+ import './react-devtools-hook';
18
19
  import './styles';
19
20
  declare global {
20
21
  interface Window {
@@ -1,3 +1,4 @@
1
+ import "./react-devtools-hook.js";
1
2
  import "./styles.js";
2
3
  import { registerDayjsPlugins } from "./dayjs.js";
3
4
  import { initAxiosConfig } from "./axios.js";
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Inject minimal __REACT_DEVTOOLS_GLOBAL_HOOK__ before React loads.
3
+ * React checks for this hook during module initialization and registers its
4
+ * renderer via hook.inject(). This gives us access to renderer.overrideProps()
5
+ * for live component prop modification in the inspector.
6
+ *
7
+ * Must execute before React's module-level code runs.
8
+ *
9
+ * When react-refresh-runtime runs first (as an rspack runtime module),
10
+ * it may create __REACT_DEVTOOLS_GLOBAL_HOOK__ with an inject() that
11
+ * doesn't populate the renderers Map. We patch the existing hook to
12
+ * ensure renderers are always tracked.
13
+ */
14
+ export {};
15
+ declare global {
16
+ interface Window {
17
+ __REACT_DEVTOOLS_GLOBAL_HOOK__?: any;
18
+ }
19
+ }
@@ -0,0 +1,20 @@
1
+ if ('undefined' != typeof window && 'production' !== process.env.NODE_ENV) if (window.__REACT_DEVTOOLS_GLOBAL_HOOK__) {
2
+ const hook = window.__REACT_DEVTOOLS_GLOBAL_HOOK__;
3
+ if (!(hook.renderers instanceof Map)) hook.renderers = new Map();
4
+ const originalInject = 'function' == typeof hook.inject ? hook.inject.bind(hook) : null;
5
+ hook.inject = function(renderer) {
6
+ const id = originalInject ? originalInject(renderer) : hook.renderers.size + 1;
7
+ if (null != id && !hook.renderers.has(id)) hook.renderers.set(id, renderer);
8
+ return id;
9
+ };
10
+ } else window.__REACT_DEVTOOLS_GLOBAL_HOOK__ = {
11
+ renderers: new Map(),
12
+ supportsFiber: true,
13
+ inject (renderer) {
14
+ const id = this.renderers.size + 1;
15
+ this.renderers.set(id, renderer);
16
+ return id;
17
+ },
18
+ onCommitFiberRoot () {},
19
+ onCommitFiberUnmount () {}
20
+ };
@@ -1,3 +1,4 @@
1
1
  @layer theme, base, antd, components, utilities;
2
- @import "tailwindcss";
2
+ @import 'tailwindcss' source(none);
3
+ @source "../**/*.{js,jsx,ts,tsx}";
3
4
 
@@ -0,0 +1,5 @@
1
+ /**
2
+ * 判断当前是否启用新的路径规范
3
+ * 新路径规范:CLIENT_BASE_PATH 以 /app/:appId 格式开头
4
+ */
5
+ export declare function isNewPathEnabled(): boolean;
@@ -0,0 +1,5 @@
1
+ function isNewPathEnabled() {
2
+ const basePath = process.env.CLIENT_BASE_PATH || '/';
3
+ return /^\/app\/[^/]/.test(basePath);
4
+ }
5
+ export { isNewPathEnabled };
@@ -3,6 +3,93 @@ import { observable } from "@lark-apaas/observable-web";
3
3
  import { logger } from "../apis/logger.js";
4
4
  import { getStacktrace } from "../logger/selected-logs.js";
5
5
  import { safeStringify } from "./safeStringify.js";
6
+ import { slardar } from "@lark-apaas/internal-slardar";
7
+ import { normalizeBasePath } from "./utils.js";
8
+ const APP_CLIENT_API_LOG_TYPE = 'app_client_api_log';
9
+ function stripBasePath(urlPath) {
10
+ const base = normalizeBasePath(process.env.CLIENT_BASE_PATH);
11
+ if (base && urlPath.startsWith(base)) return urlPath.slice(base.length) || '/';
12
+ return urlPath;
13
+ }
14
+ let _pageRoutes = null;
15
+ function getPageRouteDefinitions() {
16
+ if (_pageRoutes) return _pageRoutes;
17
+ try {
18
+ const raw = process.env.__PAGE_ROUTE_DEFINITIONS__;
19
+ if (raw) {
20
+ const parsed = JSON.parse(raw);
21
+ if (Array.isArray(parsed)) {
22
+ _pageRoutes = parsed;
23
+ return _pageRoutes;
24
+ }
25
+ }
26
+ } catch {}
27
+ _pageRoutes = [];
28
+ return _pageRoutes;
29
+ }
30
+ let _apiRoutes = null;
31
+ function getApiRouteDefinitions() {
32
+ if (_apiRoutes) return _apiRoutes;
33
+ try {
34
+ const raw = process.env.__API_ROUTE_DEFINITIONS__;
35
+ if (raw) {
36
+ const parsed = JSON.parse(raw);
37
+ if (Array.isArray(parsed)) {
38
+ _apiRoutes = parsed;
39
+ return _apiRoutes;
40
+ }
41
+ }
42
+ } catch {}
43
+ _apiRoutes = [];
44
+ return _apiRoutes;
45
+ }
46
+ function matchRoute(concretePath, routes) {
47
+ const segments = concretePath.split('/').filter(Boolean);
48
+ for (const route of routes){
49
+ const routeSegments = route.path.split('/').filter(Boolean);
50
+ if (routeSegments.length !== segments.length) continue;
51
+ let match = true;
52
+ for(let i = 0; i < routeSegments.length; i++)if (!routeSegments[i].startsWith(':')) {
53
+ if (routeSegments[i] !== segments[i]) {
54
+ match = false;
55
+ break;
56
+ }
57
+ }
58
+ if (match) return route.path;
59
+ }
60
+ return null;
61
+ }
62
+ function matchApiRoute(method, concretePath) {
63
+ const routes = getApiRouteDefinitions();
64
+ const segments = concretePath.split('/').filter(Boolean);
65
+ for (const route of routes){
66
+ if ('*' !== route.method && route.method !== method) continue;
67
+ const routeSegments = route.path.split('/').filter(Boolean);
68
+ if (routeSegments.length !== segments.length) continue;
69
+ let match = true;
70
+ for(let i = 0; i < routeSegments.length; i++)if (!routeSegments[i].startsWith(':')) {
71
+ if (routeSegments[i] !== segments[i]) {
72
+ match = false;
73
+ break;
74
+ }
75
+ }
76
+ if (match) return route.path;
77
+ }
78
+ return null;
79
+ }
80
+ function getRefererPath() {
81
+ try {
82
+ if ('undefined' == typeof window || !window.location?.pathname) return '/';
83
+ const rawPath = stripBasePath(window.location.pathname);
84
+ return matchRoute(rawPath, getPageRouteDefinitions()) || rawPath;
85
+ } catch {
86
+ return '/';
87
+ }
88
+ }
89
+ function getApiField(method, path) {
90
+ const matched = matchApiRoute(method, path);
91
+ return `${method} ${matched || path}`;
92
+ }
6
93
  const isValidResponse = (resp)=>null != resp && 'object' == typeof resp && 'config' in resp && null !== resp.config && void 0 !== resp.config && 'object' == typeof resp.config && 'status' in resp && 'number' == typeof resp.status && 'data' in resp;
7
94
  async function logResponse(ok, responseOrError) {
8
95
  if (isValidResponse(responseOrError)) {
@@ -54,7 +141,7 @@ async function logResponse(ok, responseOrError) {
54
141
  logTraceID
55
142
  };
56
143
  if (stacktrace) logMeta.stacktrace = stacktrace;
57
- logger.debug({
144
+ logger.log({
58
145
  level: ok,
59
146
  args: [
60
147
  parts.join(''),
@@ -110,7 +197,7 @@ async function logResponse(ok, responseOrError) {
110
197
  const stacktrace = await requestStacktraceMap.get(requestUUID);
111
198
  const logMeta = {};
112
199
  if (stacktrace) logMeta.stacktrace = stacktrace;
113
- logger.debug({
200
+ logger.log({
114
201
  level: 'error',
115
202
  args: [
116
203
  parts.join(''),
@@ -120,7 +207,7 @@ async function logResponse(ok, responseOrError) {
120
207
  });
121
208
  return;
122
209
  }
123
- logger.debug({
210
+ logger.log({
124
211
  level: 'error',
125
212
  args: [
126
213
  '请求失败:无响应对象或配置信息'
@@ -138,13 +225,20 @@ function handleSpanEnd(cfg, response, error) {
138
225
  const errorMessage = error?.message || '未知错误';
139
226
  const url = response?.request?.responseURL || errorResponse?.request?.responseURL || cfg.url || "";
140
227
  const method = (cfg.method || 'GET').toUpperCase();
141
- const path = url.split('?')[0].replace(/^https?:\/\/[^/]+/, '') || '/';
228
+ const rawPath = url.split('?')[0].replace(/^https?:\/\/[^/]+/, '') || '/';
229
+ const path = stripBasePath(rawPath);
230
+ const durationMs = startTime ? Date.now() - startTime : void 0;
231
+ const referer_path = getRefererPath();
232
+ const api = getApiField(method, path);
233
+ const type = APP_CLIENT_API_LOG_TYPE;
142
234
  const logData = {
143
235
  method,
144
236
  path,
145
237
  url,
146
- duration_ms: startTime ? Date.now() - startTime : void 0,
147
- status: response ? response.status : errorResponse.status || 0
238
+ duration_ms: durationMs,
239
+ status: response ? response.status : errorResponse.status || 0,
240
+ referer_path,
241
+ type
148
242
  };
149
243
  if (error) logData.error_message = errorMessage;
150
244
  if ('undefined' != typeof navigator) logData.user_agent = navigator.userAgent;
@@ -157,7 +251,19 @@ function handleSpanEnd(cfg, response, error) {
157
251
  const responseData = response?.data || errorResponse?.data;
158
252
  if (responseData) logData.response = responseData;
159
253
  const level = error ? 'ERROR' : 'INFO';
160
- observable.log(level, safeStringify(logData), {}, currentSpan);
254
+ observable.log(level, safeStringify(logData), {
255
+ referer_path,
256
+ api,
257
+ type,
258
+ duration_ms: durationMs
259
+ }, currentSpan);
260
+ if ('function' == typeof currentSpan.setAttributes) currentSpan.setAttributes({
261
+ referer_path,
262
+ api,
263
+ duration_ms: durationMs,
264
+ module: 'app_web',
265
+ source_type: 'platform'
266
+ });
161
267
  'function' == typeof currentSpan.end && currentSpan.end();
162
268
  } catch (e) {
163
269
  console.error('[AxiosTrace] Log span failed:', e);
@@ -193,6 +299,10 @@ AxiosProto.request = function(configOrUrl, config) {
193
299
  return response;
194
300
  }, (error)=>{
195
301
  handleSpanEnd(error.config || finalConfig, null, error);
302
+ slardar.captureException(error, {
303
+ source: 'toolkit',
304
+ module: 'axios-request'
305
+ });
196
306
  throw error;
197
307
  });
198
308
  };
@@ -234,6 +344,12 @@ function initAxiosConfig(axiosInstance) {
234
344
  config._startTime = config._startTime || Date.now();
235
345
  const csrfToken = window.csrfToken;
236
346
  if (csrfToken) config.headers['X-Suda-Csrf-Token'] = csrfToken;
347
+ if ('undefined' != typeof window && window.location?.pathname) config.headers['X-Page-Route'] = window.location.pathname;
348
+ const refererPath = getRefererPath();
349
+ config.headers['Rpc-Persist-Apaas-Observability-Referer-Path'] = refererPath;
350
+ const reqMethod = (config.method || 'GET').toUpperCase();
351
+ const requestPath = stripBasePath((config.url || '').split('?')[0]);
352
+ config.headers['Rpc-Persist-Apaas-Observability-Api'] = getApiField(reqMethod, requestPath);
237
353
  return config;
238
354
  }, (error)=>Promise.reject(error));
239
355
  instance.interceptors.response.use((response)=>response, (error)=>{
@@ -241,8 +357,8 @@ function initAxiosConfig(axiosInstance) {
241
357
  if (error.response?.status === 403) {
242
358
  const method = (error.config?.method || 'GET').toUpperCase();
243
359
  const url = (error.config?.url || '').split('?')[0];
244
- logger.debug({
245
- level: 'warning',
360
+ logger.log({
361
+ level: 'warn',
246
362
  args: [
247
363
  `请求被拒绝(403):${method} ${url}`,
248
364
  {
@@ -252,9 +368,47 @@ function initAxiosConfig(axiosInstance) {
252
368
  ],
253
369
  meta: {}
254
370
  });
371
+ slardar.sendEvent({
372
+ name: 'toolkit_axios_403_downgrade',
373
+ categories: {
374
+ url: String(error.config?.url || ''),
375
+ method: String(error.config?.method || ''),
376
+ ...'production' !== process.env.NODE_ENV && {
377
+ responseData: (()=>{
378
+ try {
379
+ const data = error.response?.data;
380
+ if (!data) return '';
381
+ return 'string' == typeof data ? data.slice(0, 512) : JSON.stringify(data).slice(0, 512);
382
+ } catch {
383
+ return '';
384
+ }
385
+ })()
386
+ }
387
+ }
388
+ });
255
389
  return error.response;
256
390
  }
257
391
  } catch (e) {}
392
+ slardar.sendEvent({
393
+ name: 'toolkit_axios_response_error',
394
+ categories: {
395
+ url: String(error.config?.url || ''),
396
+ method: String(error.config?.method || ''),
397
+ status: String(error.response?.status || ''),
398
+ ...'production' !== process.env.NODE_ENV && {
399
+ errorMessage: String(error.message || ''),
400
+ responseData: (()=>{
401
+ try {
402
+ const data = error.response?.data;
403
+ if (!data) return '';
404
+ return 'string' == typeof data ? data.slice(0, 512) : JSON.stringify(data).slice(0, 512);
405
+ } catch {
406
+ return '';
407
+ }
408
+ })()
409
+ }
410
+ }
411
+ });
258
412
  return Promise.reject(error);
259
413
  });
260
414
  'production' !== process.env.NODE_ENV && instance.interceptors.response.use((response)=>{
@@ -1,6 +1,4 @@
1
1
  /**
2
- * 提取固定前缀后的第一个目录名
3
- * @param {string} path - 输入路径字符串
4
- * @returns {string|null} - 提取的目录名或null
2
+ * 获取应用 ID
5
3
  */
6
- export declare function getAppId(path: string): string | null;
4
+ export declare function getAppId(): string | null;
@@ -1,12 +1,5 @@
1
- function getAppId(path) {
2
- if (window.MIAODA_APP_ID) return window.MIAODA_APP_ID;
3
- let prefix;
4
- prefix = path.includes('/ai/feida/runtime/') ? '/ai/feida/runtime/' : path.includes('/spark/r/') ? '/spark/r/' : '/ai/miaoda/';
1
+ function getAppId() {
5
2
  const windowAppId = window.appId || null;
6
- if (!path.startsWith(prefix)) return windowAppId;
7
- const remainder = path.substring(prefix.length);
8
- const nextSlashIndex = remainder.indexOf('/');
9
- if (-1 === nextSlashIndex) return remainder || windowAppId;
10
- return remainder.substring(0, nextSlashIndex) || windowAppId;
3
+ return windowAppId;
11
4
  }
12
5
  export { getAppId };
@@ -13,9 +13,10 @@ interface AppRuntimePublished {
13
13
  interface BucketConfig {
14
14
  default_bucket_id?: string;
15
15
  }
16
- /** 获取应用初始化信息(仅全栈沙箱模式下使用) */
17
- export declare function getInitialInfo(refresh?: boolean): Promise<{
16
+ declare let initialInfo: {
18
17
  app_info?: AppRuntimePublished;
19
18
  app_runtime_extra?: AppRuntimeExtra;
20
- }>;
19
+ } | undefined;
20
+ /** 获取应用初始化信息(仅全栈沙箱模式下使用) */
21
+ export declare function getInitialInfo(refresh?: boolean): Promise<typeof initialInfo>;
21
22
  export {};