@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,5 +1,6 @@
1
1
  import { getAppId } from "./getAppId.js";
2
2
  import { getCsrfToken } from "./getCsrfToken.js";
3
+ import { isNewPathEnabled } from "./apiPath.js";
3
4
  async function getAppPublished() {
4
5
  try {
5
6
  const headers = {
@@ -8,25 +9,33 @@ async function getAppPublished() {
8
9
  'x-miaoda-token': window.MIAODA_BUILTIN_TTT,
9
10
  'X-Suda-Csrf-Token': getCsrfToken()
10
11
  };
11
- const appId = getAppId(window.location.pathname) || window.appId;
12
- const url = `${window.location.origin}/spark/b/${appId}/get_published`;
12
+ const appId = getAppId();
13
+ const path = isNewPathEnabled() ? `/app/${appId}/__runtime__/api/v1/studio/get_published` : `/spark/b/${appId}/get_published`;
14
+ const url = `${window.location.origin}${path}`;
13
15
  const response = await fetch(url, {
14
16
  method: 'GET',
15
17
  headers,
16
18
  credentials: 'include'
17
19
  });
18
20
  const res = await response.json();
19
- if (0 === res.code) return res.data;
21
+ if (0 === res.code || '0' === res.status_code) return res.data;
20
22
  console.error('Error fetching published app info:', res);
21
23
  } catch (error) {
22
24
  console.error('Error fetching published app info:', error);
23
25
  }
24
26
  }
25
27
  let initialInfo;
26
- async function getInitialInfo(refresh = false) {
27
- if (initialInfo && !refresh) return initialInfo;
28
- initialInfo = await getAppPublished();
29
- if (initialInfo) window._bucket_id = initialInfo.app_runtime_extra?.bucket?.default_bucket_id;
30
- return initialInfo;
28
+ let pendingPromise = null;
29
+ function getInitialInfo(refresh = false) {
30
+ if (initialInfo && !refresh) return Promise.resolve(initialInfo);
31
+ if (pendingPromise && !refresh) return pendingPromise;
32
+ pendingPromise = getAppPublished().then((info)=>{
33
+ initialInfo = info;
34
+ if (initialInfo) window._bucket_id = initialInfo.app_runtime_extra?.bucket?.default_bucket_id;
35
+ return initialInfo;
36
+ }).finally(()=>{
37
+ pendingPromise = null;
38
+ });
39
+ return pendingPromise;
31
40
  }
32
41
  export { getInitialInfo };
@@ -1,9 +1,8 @@
1
1
  import { getAppId } from "./getAppId.js";
2
2
  import { getCsrfToken } from "./getCsrfToken.js";
3
- import { getEnvPath } from "./getEnvPath.js";
4
- import { isSparkRuntime } from "./utils.js";
3
+ import { isNewPathEnabled } from "./apiPath.js";
5
4
  async function getUserProfile(request, headers = {}) {
6
- const appId = getAppId(window.location.pathname);
5
+ const appId = getAppId();
7
6
  if (!appId) return {
8
7
  code: 1,
9
8
  msg: 'appId is required',
@@ -15,17 +14,10 @@ async function getUserProfile(request, headers = {}) {
15
14
  const mergedHeaders = {
16
15
  ...defaultHeaders,
17
16
  ...headers,
18
- 'X-Kunlun-Token': window.token,
19
- 'x-miaoda-token': window.MIAODA_BUILTIN_TTT,
20
- 'x-lgw-csrf-token': window.lgw_csrf_token,
21
17
  ...window.CSRF_HEADERS || {}
22
18
  };
23
- const envPath = getEnvPath();
24
- let endpoint;
25
- if (isSparkRuntime()) {
26
- endpoint = `/spark/b/${appId}/user/profile`;
27
- mergedHeaders['X-Suda-Csrf-Token'] = getCsrfToken();
28
- } else endpoint = `/ai/api/${envPath}/v1/apps/${appId}/user/profile`;
19
+ const endpoint = isNewPathEnabled() ? `/app/${appId}/__runtime__/api/v1/studio/user/profile` : `/spark/b/${appId}/user/profile`;
20
+ mergedHeaders['X-Suda-Csrf-Token'] = getCsrfToken();
29
21
  const response = await fetch(`${window.location.origin}${endpoint}`, {
30
22
  method: 'POST',
31
23
  headers: mergedHeaders,
@@ -0,0 +1,45 @@
1
+ /**
2
+ * 统一 HMR API
3
+ *
4
+ * 提供统一的 HMR 成功/失败回调接口,屏蔽 Webpack 和 Vite 的差异。
5
+ *
6
+ * - Webpack/Rspack: 通过 import.meta.webpackHot 或 module.hot
7
+ * - Vite: 通过 error-overlay 注入的 window.__VITE_HMR__
8
+ *
9
+ * @see docs/RFC_HMR_API.md
10
+ */
11
+ export interface HmrApi {
12
+ /**
13
+ * 注册 HMR 更新前回调(模块替换之前触发)
14
+ * @param callback 回调函数
15
+ * @returns cleanup 函数,用于取消注册
16
+ */
17
+ onBeforeApply?(callback: () => void): () => void;
18
+ /**
19
+ * 注册 HMR 成功回调
20
+ * @param callback 成功回调函数
21
+ * @returns cleanup 函数,用于取消注册
22
+ */
23
+ onSuccess(callback: () => void): () => void;
24
+ /**
25
+ * 注册 HMR 失败回调
26
+ * @param callback 失败回调函数,参数为错误信息
27
+ * @returns cleanup 函数,用于取消注册
28
+ */
29
+ onError(callback: (error: unknown) => void): () => void;
30
+ }
31
+ declare global {
32
+ interface Window {
33
+ __VITE_HMR__?: Required<HmrApi>;
34
+ }
35
+ }
36
+ /**
37
+ * 获取 HMR API 实例
38
+ *
39
+ * 自动检测当前构建工具(Webpack/Vite)并返回对应实现:
40
+ * - Webpack/Rspack: 使用内置适配器
41
+ * - Vite: 使用 error-overlay 注入的 window.__VITE_HMR__
42
+ *
43
+ * @returns HmrApi 实例,如果不在开发环境或不支持 HMR 则返回 null
44
+ */
45
+ export declare function getHmrApi(): HmrApi | null;
@@ -0,0 +1,36 @@
1
+ function createWebpackHmrApi(hot) {
2
+ return {
3
+ onSuccess (callback) {
4
+ let lastStatus = null;
5
+ const handler = (status)=>{
6
+ if ('idle' === status && 'apply' === lastStatus) try {
7
+ callback();
8
+ } catch (e) {
9
+ console.error('[HMR] Success callback error:', e);
10
+ }
11
+ lastStatus = status;
12
+ };
13
+ hot.addStatusHandler(handler);
14
+ return ()=>hot.removeStatusHandler(handler);
15
+ },
16
+ onError (callback) {
17
+ const handler = (status)=>{
18
+ if ('fail' === status || 'abort' === status) try {
19
+ callback(new Error(`HMR ${status}`));
20
+ } catch (e) {
21
+ console.error('[HMR] Error callback error:', e);
22
+ }
23
+ };
24
+ hot.addStatusHandler(handler);
25
+ return ()=>hot.removeStatusHandler(handler);
26
+ }
27
+ };
28
+ }
29
+ function getHmrApi() {
30
+ if ('production' === process.env.NODE_ENV) return null;
31
+ if (import.meta.webpackHot) return createWebpackHmrApi(import.meta.webpackHot);
32
+ 'undefined' != typeof module && module.hot;
33
+ if (window.__VITE_HMR__) return window.__VITE_HMR__;
34
+ return null;
35
+ }
36
+ export { getHmrApi };
@@ -10,16 +10,20 @@
10
10
  * - abort: An update was aborted, but the system is still in its previous state
11
11
  * - fail: An update has thrown an exception and the system's state has been compromised
12
12
  */
13
- type ModuleHotType = 'idle' | 'check' | 'prepare' | 'ready' | 'dispose' | 'apply' | 'abort' | 'fail';
14
- interface ModuleHotInstance {
13
+ export type ModuleHotType = 'idle' | 'check' | 'prepare' | 'ready' | 'dispose' | 'apply' | 'abort' | 'fail';
14
+ export interface ModuleHotInstance {
15
15
  addStatusHandler: (handler: (status: ModuleHotType) => void) => void;
16
16
  removeStatusHandler: (handler: (status: ModuleHotType) => void) => void;
17
17
  }
18
+ /**
19
+ * 获取 Webpack HMR 实例
20
+ * 仅支持 Webpack/Rspack 的 HMR API
21
+ */
18
22
  export declare function getModuleHot(): ModuleHotInstance | null;
19
23
  /**
20
24
  * 创建模块热更成功处理函数
21
- * 监听模块热更状态,当状态为apply时,调用回调函数并传入true
22
- * @param callback 热更成功回调函数,参数为是否成功
25
+ * 监听模块热更状态,检测 apply -> idle 转换表示热更成功
26
+ *
27
+ * @param callback 热更回调函数,参数为是否成功和当前状态
23
28
  */
24
29
  export declare function createApplyHandle(callback: (success: boolean, status: ModuleHotType) => void): (status: ModuleHotType) => void;
25
- export {};
@@ -1,22 +1,21 @@
1
1
  function getModuleHot() {
2
2
  if ('production' === process.env.NODE_ENV) return null;
3
- return import.meta.webpackHot || module.hot;
3
+ if (import.meta.webpackHot) return import.meta.webpackHot;
4
+ 'undefined' != typeof module && module.hot;
5
+ return null;
4
6
  }
5
7
  function createApplyHandle(callback) {
6
- let hasApply = false;
8
+ let lastStatus = null;
7
9
  return (status)=>{
8
10
  if ('fail' === status || 'abort' === status) {
9
- hasApply = false;
11
+ lastStatus = status;
10
12
  return callback(false, status);
11
13
  }
12
- if (hasApply) {
13
- if ('idle' === status) {
14
- hasApply = false;
15
- callback(true, status);
16
- }
17
- return;
14
+ if ('idle' === status && 'apply' === lastStatus) {
15
+ lastStatus = status;
16
+ return callback(true, status);
18
17
  }
19
- hasApply = 'apply' === status;
18
+ lastStatus = status;
20
19
  };
21
20
  }
22
21
  export { createApplyHandle, getModuleHot };
@@ -1,7 +1,6 @@
1
1
  import type { IncomingMessage, OutgoingMessage } from '../types/iframe-events';
2
2
  export declare function resolveParentOrigin(): string;
3
3
  export declare function submitPostMessage<T extends OutgoingMessage>(message: T, targetOrigin?: string): void;
4
- export declare function submitSlardarEvent(event: unknown): void;
5
4
  export declare function isOutgoingMessage<T extends OutgoingMessage['type']>(msg: OutgoingMessage, type: T): msg is Extract<OutgoingMessage, {
6
5
  type: T;
7
6
  }>;
@@ -1,3 +1,19 @@
1
+ const PARENT_ORIGIN_KEY = '__parentOrigin';
2
+ function getParentOriginFromParams() {
3
+ try {
4
+ const params = new URLSearchParams(window.location.search);
5
+ const origin = params.get(PARENT_ORIGIN_KEY);
6
+ if (origin) {
7
+ sessionStorage.setItem(PARENT_ORIGIN_KEY, origin);
8
+ return origin;
9
+ }
10
+ } catch {}
11
+ try {
12
+ return sessionStorage.getItem(PARENT_ORIGIN_KEY) || void 0;
13
+ } catch {
14
+ return;
15
+ }
16
+ }
1
17
  function getLegacyParentOrigin() {
2
18
  const { origin } = window.location;
3
19
  if (origin.includes('force.feishuapp.net')) return 'https://force.feishu.cn';
@@ -8,6 +24,8 @@ function getLegacyParentOrigin() {
8
24
  return 'https://miaoda.feishu-boe.cn';
9
25
  }
10
26
  function resolveParentOrigin() {
27
+ const paramOrigin = getParentOriginFromParams();
28
+ if (paramOrigin) return paramOrigin;
11
29
  return process.env?.FORCE_FRAMEWORK_DOMAIN_MAIN ?? getLegacyParentOrigin();
12
30
  }
13
31
  function submitPostMessage(message, targetOrigin) {
@@ -20,15 +38,10 @@ function submitPostMessage(message, targetOrigin) {
20
38
  console.error('postMessage error', e);
21
39
  }
22
40
  }
23
- function submitSlardarEvent(event) {
24
- const slardar = window.KSlardarWeb;
25
- if ('function' == typeof slardar) slardar('sendEvent', event);
26
- else console.warn('hmr listen function not found');
27
- }
28
41
  function isOutgoingMessage(msg, type) {
29
42
  return msg.type === type;
30
43
  }
31
44
  function isIncomingMessage(msg, type) {
32
45
  return msg.type === type;
33
46
  }
34
- export { isIncomingMessage, isOutgoingMessage, resolveParentOrigin, submitPostMessage, submitSlardarEvent };
47
+ export { isIncomingMessage, isOutgoingMessage, resolveParentOrigin, submitPostMessage };
@@ -4,9 +4,7 @@ async function fetchUserProfilesByIds(ids) {
4
4
  try {
5
5
  const dataloom = await getDataloom();
6
6
  if (!dataloom) throw new Error('Dataloom client is unavailable');
7
- const numericIds = ids.map(Number).filter(Number.isFinite);
8
- if (0 === numericIds.length) return [];
9
- const response = await dataloom.service.user.getByIds(numericIds);
7
+ const response = await dataloom.service.user.getByIds(ids);
10
8
  return Array.isArray(response?.data) ? response.data : response?.data?.user_list || [];
11
9
  } catch (error) {
12
10
  console.error(`Failed to fetch profiles for user IDs ${ids.join(', ')}:`, error);
@@ -0,0 +1,27 @@
1
+ /**
2
+ * 解析应用内路径,自动补全 CLIENT_BASE_PATH 前缀,返回完整 URL
3
+ * 适用于生成分享链接、二维码等需要完整 URL 的场景
4
+ *
5
+ * - 相对路径:补全前缀 + 当前域名
6
+ * - 当前域名的完整 URL:修正路径部分,补全前缀
7
+ * - 其他域名的 URL:原样返回,不做处理
8
+ *
9
+ * @param path - 应用内相对路径或完整 URL
10
+ * @returns 完整的 URL 字符串
11
+ *
12
+ * @example
13
+ * // 假设 CLIENT_BASE_PATH = '/app/abc',当前域名为 https://example.com
14
+ *
15
+ * resolveAppUrl('/detail/123')
16
+ * // => 'https://example.com/app/abc/detail/123'
17
+ *
18
+ * resolveAppUrl('https://example.com/detail/123')
19
+ * // => 'https://example.com/app/abc/detail/123'
20
+ *
21
+ * resolveAppUrl('https://example.com/app/abc/detail/123')
22
+ * // => 'https://example.com/app/abc/detail/123' (已有前缀,不重复添加)
23
+ *
24
+ * resolveAppUrl('https://other-site.com/page')
25
+ * // => 'https://other-site.com/page' (非当前域名,原样返回)
26
+ */
27
+ export declare function resolveAppUrl(path: string): string;
@@ -0,0 +1,19 @@
1
+ import { normalizeBasePath } from "./utils.js";
2
+ function ensureBasePath(pathname, basePath) {
3
+ if (!basePath) return pathname;
4
+ if (pathname.startsWith(basePath)) return pathname;
5
+ return `${basePath}${pathname.startsWith('/') ? '' : '/'}${pathname}`;
6
+ }
7
+ function resolveAppUrl(path) {
8
+ const basePath = normalizeBasePath(process.env.CLIENT_BASE_PATH);
9
+ try {
10
+ const url = new URL(path);
11
+ if (url.origin !== window.location.origin) return path;
12
+ url.pathname = ensureBasePath(url.pathname, basePath);
13
+ return url.toString();
14
+ } catch {}
15
+ const normalizedPath = path.startsWith('/') ? path : `/${path}`;
16
+ const resolvedPath = ensureBasePath(normalizedPath, basePath);
17
+ return `${window.location.origin}${resolvedPath}`;
18
+ }
19
+ export { resolveAppUrl };
@@ -12,6 +12,11 @@ function safeStringify(obj) {
12
12
  if (value instanceof Set) return Array.from(value);
13
13
  if (void 0 === value) return 'undefined';
14
14
  if ('symbol' == typeof value) return value.toString();
15
+ if (value instanceof Error) return {
16
+ name: value.name,
17
+ message: value.message,
18
+ stack: value.stack
19
+ };
15
20
  return value;
16
21
  });
17
22
  } catch {
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,125 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { mapLogLevel, processLogParams, safeStringify } from "./safeStringify.js";
3
+ describe('safeStringify', ()=>{
4
+ it('should serialize a plain string', ()=>{
5
+ expect(safeStringify('hello')).toBe('"hello"');
6
+ });
7
+ it('should serialize a plain object', ()=>{
8
+ expect(safeStringify({
9
+ a: 1
10
+ })).toBe('{"a":1}');
11
+ });
12
+ it('should handle circular references', ()=>{
13
+ const obj = {
14
+ a: 1
15
+ };
16
+ obj.self = obj;
17
+ expect(safeStringify(obj)).toBe('{"a":1,"self":"[Circular]"}');
18
+ });
19
+ it('should serialize BigInt as string', ()=>{
20
+ expect(safeStringify(BigInt(123))).toBe('"123"');
21
+ });
22
+ it('should serialize Date as ISO string', ()=>{
23
+ const date = new Date('2024-01-01T00:00:00.000Z');
24
+ expect(safeStringify(date)).toBe('"2024-01-01T00:00:00.000Z"');
25
+ });
26
+ it('should serialize Map as object', ()=>{
27
+ const map = new Map([
28
+ [
29
+ 'key',
30
+ 'value'
31
+ ]
32
+ ]);
33
+ expect(safeStringify(map)).toBe('{"key":"value"}');
34
+ });
35
+ it('should serialize Set as array', ()=>{
36
+ const set = new Set([
37
+ 1,
38
+ 2,
39
+ 3
40
+ ]);
41
+ expect(safeStringify(set)).toBe('[1,2,3]');
42
+ });
43
+ it('should serialize undefined as string', ()=>{
44
+ expect(safeStringify({
45
+ a: void 0
46
+ })).toBe('{"a":"undefined"}');
47
+ });
48
+ it('should serialize Symbol as string', ()=>{
49
+ expect(safeStringify({
50
+ s: Symbol('test')
51
+ })).toBe('{"s":"Symbol(test)"}');
52
+ });
53
+ it('should serialize Error with name, message and stack', ()=>{
54
+ const err = new Error('something went wrong');
55
+ const result = JSON.parse(safeStringify(err));
56
+ expect(result.name).toBe('Error');
57
+ expect(result.message).toBe('something went wrong');
58
+ expect(result.stack).toContain('something went wrong');
59
+ });
60
+ it('should serialize Error subclass with correct name', ()=>{
61
+ const err = new TypeError('bad type');
62
+ const result = JSON.parse(safeStringify(err));
63
+ expect(result.name).toBe('TypeError');
64
+ expect(result.message).toBe('bad type');
65
+ });
66
+ it('should serialize Error nested in object', ()=>{
67
+ const err = new Error('inner error');
68
+ const result = JSON.parse(safeStringify({
69
+ err
70
+ }));
71
+ expect(result.err.name).toBe('Error');
72
+ expect(result.err.message).toBe('inner error');
73
+ });
74
+ it('should return empty string on unexpected failure', ()=>{
75
+ const bad = {
76
+ toJSON () {
77
+ throw new Error('fail');
78
+ }
79
+ };
80
+ expect(safeStringify(bad)).toBe('');
81
+ });
82
+ });
83
+ describe('processLogParams', ()=>{
84
+ it('should serialize single argument directly', ()=>{
85
+ expect(processLogParams([
86
+ 'hello'
87
+ ])).toBe('"hello"');
88
+ });
89
+ it('should serialize single object argument', ()=>{
90
+ expect(processLogParams([
91
+ {
92
+ a: 1
93
+ }
94
+ ])).toBe('{"a":1}');
95
+ });
96
+ it('should serialize multiple arguments as indexed object', ()=>{
97
+ const result = JSON.parse(processLogParams([
98
+ 'msg',
99
+ {
100
+ key: 'val'
101
+ }
102
+ ]));
103
+ expect(result['0']).toBe('msg');
104
+ expect(result['1']).toEqual({
105
+ key: 'val'
106
+ });
107
+ });
108
+ });
109
+ describe('mapLogLevel', ()=>{
110
+ it('should map error to ERROR', ()=>{
111
+ expect(mapLogLevel('error')).toBe('ERROR');
112
+ });
113
+ it('should map info to INFO', ()=>{
114
+ expect(mapLogLevel('info')).toBe('INFO');
115
+ });
116
+ it('should map success to INFO', ()=>{
117
+ expect(mapLogLevel('success')).toBe('INFO');
118
+ });
119
+ it('should map warn to WARN', ()=>{
120
+ expect(mapLogLevel('warn')).toBe('WARN');
121
+ });
122
+ it('should default unknown level to INFO', ()=>{
123
+ expect(mapLogLevel('debug')).toBe('INFO');
124
+ });
125
+ });
@@ -0,0 +1,5 @@
1
+ /**
2
+ * 按 appId 隔离的 localStorage 封装
3
+ * API 与原生 localStorage 保持一致
4
+ */
5
+ export declare const scopedStorage: Storage;
@@ -0,0 +1,46 @@
1
+ import { getAppId } from "./getAppId.js";
2
+ function getPrefix() {
3
+ const appId = getAppId();
4
+ const namespace = appId || '__global__';
5
+ return `__miaoda_${namespace}__:`;
6
+ }
7
+ function getScopedKeys() {
8
+ const prefix = getPrefix();
9
+ const keys = [];
10
+ for(let i = 0; i < localStorage.length; i++){
11
+ const key = localStorage.key(i);
12
+ if (key && key.startsWith(prefix)) keys.push(key.substring(prefix.length));
13
+ }
14
+ return keys;
15
+ }
16
+ const scopedStorage = {
17
+ getItem (key) {
18
+ const prefixedKey = getPrefix() + key;
19
+ return localStorage.getItem(prefixedKey);
20
+ },
21
+ setItem (key, value) {
22
+ const prefixedKey = getPrefix() + key;
23
+ localStorage.setItem(prefixedKey, value);
24
+ },
25
+ removeItem (key) {
26
+ const prefixedKey = getPrefix() + key;
27
+ localStorage.removeItem(prefixedKey);
28
+ },
29
+ clear () {
30
+ const prefix = getPrefix();
31
+ const keysToRemove = [];
32
+ for(let i = 0; i < localStorage.length; i++){
33
+ const key = localStorage.key(i);
34
+ if (key && key.startsWith(prefix)) keysToRemove.push(key);
35
+ }
36
+ keysToRemove.forEach((key)=>localStorage.removeItem(key));
37
+ },
38
+ key (index) {
39
+ const keys = getScopedKeys();
40
+ return keys[index] || null;
41
+ },
42
+ get length () {
43
+ return getScopedKeys().length;
44
+ }
45
+ };
46
+ export { scopedStorage };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lark-apaas/client-toolkit",
3
- "version": "1.2.9",
3
+ "version": "1.2.34",
4
4
  "types": "./lib/index.d.ts",
5
5
  "main": "./lib/index.js",
6
6
  "files": [
@@ -71,6 +71,11 @@
71
71
  "require": "./lib/apis/utils/*.js",
72
72
  "types": "./lib/apis/utils/*.d.ts"
73
73
  },
74
+ "./aily-chat": {
75
+ "import": "./lib/apis/aily-chat.js",
76
+ "require": "./lib/apis/aily-chat.js",
77
+ "types": "./lib/apis/aily-chat.d.ts"
78
+ },
74
79
  "./auth": {
75
80
  "import": "./lib/auth.js",
76
81
  "require": "./lib/auth.js",
@@ -93,11 +98,13 @@
93
98
  "dependencies": {
94
99
  "@ant-design/colors": "^7.2.1",
95
100
  "@ant-design/cssinjs": "^1.24.0",
96
- "@data-loom/js": "0.4.4-alpha.10",
97
- "@lark-apaas/auth-sdk": "^0.1.0",
98
- "@lark-apaas/client-capability": "^0.1.3",
99
- "@lark-apaas/miaoda-inspector": "^1.0.13",
100
- "@lark-apaas/observable-web": "^1.0.1",
101
+ "@data-loom/js": "0.4.13",
102
+ "@lark-apaas/aily-web-sdk": "^0.0.9",
103
+ "@lark-apaas/auth-sdk": "^0.1.4",
104
+ "@lark-apaas/client-capability": "^0.1.6",
105
+ "@lark-apaas/internal-slardar": "^0.0.3",
106
+ "@lark-apaas/miaoda-inspector": "^1.0.23",
107
+ "@lark-apaas/observable-web": "^1.0.6",
101
108
  "@radix-ui/react-avatar": "^1.1.10",
102
109
  "@radix-ui/react-popover": "^1.1.15",
103
110
  "@radix-ui/react-slot": "^1.2.3",
@@ -1 +0,0 @@
1
- export * from '../../integrations/generateImage';
@@ -1 +0,0 @@
1
- export * from "../../integrations/generateImage.js";
@@ -1 +0,0 @@
1
- export * from '../../integrations/generateTextStream';
@@ -1 +0,0 @@
1
- export * from "../../integrations/generateTextStream.js";
@@ -1 +0,0 @@
1
- export { defaultUIConfig, type UIComponentConfig, } from '@lark-apaas/miaoda-inspector';
@@ -1,2 +0,0 @@
1
- import { defaultUIConfig } from "@lark-apaas/miaoda-inspector";
2
- export { defaultUIConfig };
@@ -1 +0,0 @@
1
- export declare function generateImage(prompt: string, size?: string, headers?: Record<string, string>): Promise<any>;
@@ -1,47 +0,0 @@
1
- import { getAppId } from "../utils/getAppId.js";
2
- import { getCsrfToken } from "../utils/getCsrfToken.js";
3
- import { getEnvPath } from "../utils/getEnvPath.js";
4
- import { isSparkRuntime } from "../utils/utils.js";
5
- async function generateImage(prompt, size = '1024x1024', headers = {}) {
6
- const appId = getAppId(window.location.pathname);
7
- if (!appId) return {
8
- code: 1,
9
- msg: 'appId is required',
10
- data: {}
11
- };
12
- const defaultHeaders = {
13
- 'Content-Type': 'application/json'
14
- };
15
- const mergedHeaders = {
16
- ...defaultHeaders,
17
- ...headers,
18
- 'X-Kunlun-Token': window.token,
19
- 'x-miaoda-token': window.MIAODA_BUILTIN_TTT,
20
- 'x-lgw-csrf-token': window.lgw_csrf_token,
21
- ...window.CSRF_HEADERS || {}
22
- };
23
- if (isSparkRuntime()) {
24
- mergedHeaders['X-Suda-Csrf-Token'] = getCsrfToken();
25
- const response = await fetch(`${window.location.origin}/spark/b/${appId}/text2image`, {
26
- method: 'POST',
27
- headers: mergedHeaders,
28
- credentials: 'include',
29
- body: JSON.stringify({
30
- prompt,
31
- size
32
- })
33
- });
34
- return await response.json();
35
- }
36
- const response = await fetch(`${window.location.origin}/ai/api/${getEnvPath()}/v1/apps/${appId}/text2image`, {
37
- method: 'POST',
38
- headers: mergedHeaders,
39
- credentials: 'include',
40
- body: JSON.stringify({
41
- prompt,
42
- size
43
- })
44
- });
45
- return await response.json();
46
- }
47
- export { generateImage };