@lark-apaas/client-toolkit 1.2.1-alpha.3 → 1.2.1-alpha.5

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.
@@ -4,6 +4,7 @@ import { useLocation, useNavigate } from "react-router-dom";
4
4
  import { connectToParent } from "penpal";
5
5
  import { useUpdatingRef } from "../../hooks/useUpdatingRef.js";
6
6
  import { submitPostMessage } from "../../utils/postMessage.js";
7
+ import { getPreviewParentOrigin } from "../../utils/getParentOrigin.js";
7
8
  import { childApi } from "./utils/childApi.js";
8
9
  import "./utils/listenHot.js";
9
10
  var IframeBridge_RouteMessageType = /*#__PURE__*/ function(RouteMessageType) {
@@ -20,10 +21,8 @@ async function connectParent() {
20
21
  type: 'PreviewReady',
21
22
  data: {}
22
23
  });
23
- const parentOrigin = process.env.FORCE_FRAMEWORK_DOMAIN_MAIN;
24
- if (!parentOrigin) return;
25
24
  const connection = connectToParent({
26
- parentOrigin,
25
+ parentOrigin: getPreviewParentOrigin(),
27
26
  methods: {
28
27
  ...childApi
29
28
  }
@@ -36,13 +35,13 @@ function IframeBridge() {
36
35
  const navigate = useNavigate();
37
36
  const navigateRef = useUpdatingRef(navigate);
38
37
  const isActive = useRef(false);
39
- const historyBack = useCallback((_payload)=>{
38
+ const historyBack = useCallback(()=>{
40
39
  navigateRef.current(-1);
41
40
  isActive.current = true;
42
41
  }, [
43
42
  navigateRef
44
43
  ]);
45
- const historyForward = useCallback((_payload)=>{
44
+ const historyForward = useCallback(()=>{
46
45
  navigateRef.current(1);
47
46
  isActive.current = true;
48
47
  }, [
@@ -70,8 +69,8 @@ function IframeBridge() {
70
69
  location
71
70
  ]);
72
71
  const handleMessage = useCallback((event)=>{
73
- const data = event.data ?? {};
74
- if ('string' == typeof data.type && isRouteMessageType(data.type)) operatorMessage[data.type](data.data);
72
+ const { data } = event;
73
+ if (isRouteMessageType(data?.type)) operatorMessage[data?.type](data?.data);
75
74
  }, [
76
75
  operatorMessage
77
76
  ]);
@@ -6,6 +6,7 @@ const initObservable = ()=>{
6
6
  env: 'development' === process.env.NODE_ENV ? AppEnv.Dev : AppEnv.Prod,
7
7
  collectorUrl: {
8
8
  log: `/spark/app/${window.appId}/runtime/api/v1/observability/logs/collect`,
9
+ trace: `/spark/app/${window.appId}/runtime/api/v1/observability/traces/collect`,
9
10
  metric: `/spark/app/${window.appId}/runtime/api/v1/observability/metrics/collect`
10
11
  }
11
12
  });
@@ -15,7 +15,7 @@ const PagePlaceholder = ({ title = '页面待开发', description = '页面暂
15
15
  children: title
16
16
  }),
17
17
  /*#__PURE__*/ jsx("div", {
18
- className: "text-center text-muted-foreground text-base leading-6 line-clamp-2 sm:max-w-[480px]",
18
+ className: "text-center text-muted-foreground text-base leading-6",
19
19
  children: description
20
20
  })
21
21
  ]
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,367 @@
1
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
2
+ import { BatchLogger, batchLogInfo, initBatchLogger } from "../batch-logger.js";
3
+ var __webpack_require__ = {};
4
+ (()=>{
5
+ __webpack_require__.g = (()=>{
6
+ if ('object' == typeof globalThis) return globalThis;
7
+ try {
8
+ return this || new Function('return this')();
9
+ } catch (e) {
10
+ if ('object' == typeof window) return window;
11
+ }
12
+ })();
13
+ })();
14
+ vi.mock('node-fetch', ()=>({
15
+ default: vi.fn()
16
+ }));
17
+ const mockConsole = {
18
+ debug: vi.fn(),
19
+ info: vi.fn(),
20
+ warn: vi.fn(),
21
+ error: vi.fn(),
22
+ log: vi.fn()
23
+ };
24
+ describe('BatchLogger', ()=>{
25
+ let batchLogger;
26
+ let mockFetch;
27
+ beforeEach(()=>{
28
+ vi.clearAllMocks();
29
+ mockFetch = vi.fn();
30
+ __webpack_require__.g.fetch = mockFetch;
31
+ const mockResponse = new Response(JSON.stringify({
32
+ success: true
33
+ }), {
34
+ status: 200,
35
+ statusText: 'OK',
36
+ headers: {
37
+ 'Content-Type': 'application/json'
38
+ }
39
+ });
40
+ mockFetch.mockResolvedValue(mockResponse);
41
+ if ('undefined' == typeof window) __webpack_require__.g.window = void 0;
42
+ });
43
+ afterEach(async ()=>{
44
+ if (batchLogger) await batchLogger.destroy();
45
+ });
46
+ afterAll(()=>{
47
+ vi.restoreAllMocks();
48
+ });
49
+ describe('Basic Functionality', ()=>{
50
+ it('should create a BatchLogger instance', ()=>{
51
+ batchLogger = new BatchLogger(mockConsole, {
52
+ userId: 'user123',
53
+ tenantId: 'tenant456',
54
+ appId: 'app789'
55
+ });
56
+ expect(batchLogger).toBeInstanceOf(BatchLogger);
57
+ });
58
+ it('should add logs to queue', ()=>{
59
+ batchLogger = new BatchLogger(mockConsole, {
60
+ userId: 'user123',
61
+ tenantId: 'tenant456',
62
+ appId: 'app789'
63
+ });
64
+ batchLogger.batchLog('info', 'Test message', 'test-source');
65
+ expect(batchLogger.getQueueSize()).toBe(1);
66
+ });
67
+ it('should respect batch size limit', async ()=>{
68
+ batchLogger = new BatchLogger(mockConsole, {
69
+ userId: 'user123',
70
+ tenantId: 'tenant456',
71
+ appId: 'app789',
72
+ sizeThreshold: 3,
73
+ flushInterval: 10000
74
+ });
75
+ for(let i = 0; i < 5; i++)batchLogger.batchLog('info', `Message ${i}`);
76
+ await new Promise((resolve)=>setTimeout(resolve, 200));
77
+ expect(mockFetch).toHaveBeenCalled();
78
+ expect(mockFetch.mock.calls.length).toBeGreaterThanOrEqual(1);
79
+ expect(batchLogger.getQueueSize()).toBeLessThanOrEqual(2);
80
+ });
81
+ });
82
+ describe('Log Preservation', ()=>{
83
+ it('should preserve all logs without merging', ()=>{
84
+ batchLogger = new BatchLogger(mockConsole, {
85
+ userId: 'user123',
86
+ tenantId: 'tenant456',
87
+ appId: 'app789'
88
+ });
89
+ for(let i = 0; i < 5; i++)batchLogger.batchLog('error', 'Same error message');
90
+ expect(batchLogger.getQueueSize()).toBe(5);
91
+ });
92
+ it('should handle different log levels independently', ()=>{
93
+ batchLogger = new BatchLogger(mockConsole, {
94
+ userId: 'user123',
95
+ tenantId: 'tenant456',
96
+ appId: 'app789'
97
+ });
98
+ batchLogger.batchLog('info', 'Test message');
99
+ batchLogger.batchLog('info', 'Test message');
100
+ batchLogger.batchLog('warn', 'Test message');
101
+ batchLogger.batchLog('warn', 'Test message');
102
+ expect(batchLogger.getQueueSize()).toBe(4);
103
+ });
104
+ it('should handle large volumes of logs efficiently', ()=>{
105
+ batchLogger = new BatchLogger(mockConsole, {
106
+ userId: 'user123',
107
+ tenantId: 'tenant456',
108
+ appId: 'app789',
109
+ sizeThreshold: 1000,
110
+ flushInterval: 10000
111
+ });
112
+ for(let i = 0; i < 200; i++)batchLogger.batchLog('info', `Message ${i}`);
113
+ expect(batchLogger.getQueueSize()).toBe(200);
114
+ });
115
+ });
116
+ describe('Retry Mechanism', ()=>{
117
+ it('should retry failed requests', async ()=>{
118
+ batchLogger = new BatchLogger(mockConsole, {
119
+ userId: 'user123',
120
+ tenantId: 'tenant456',
121
+ appId: 'app789',
122
+ maxRetries: 2,
123
+ retryDelay: 100,
124
+ flushInterval: 10000
125
+ });
126
+ const successResponse = new Response(JSON.stringify({
127
+ success: true
128
+ }), {
129
+ status: 200,
130
+ statusText: 'OK',
131
+ headers: {
132
+ 'Content-Type': 'application/json'
133
+ }
134
+ });
135
+ mockFetch.mockRejectedValueOnce(new Error('Network error')).mockRejectedValueOnce(new Error('Network error')).mockResolvedValueOnce(successResponse);
136
+ batchLogger.batchLog('info', 'Test message');
137
+ await batchLogger.flush();
138
+ expect(mockFetch).toHaveBeenCalled();
139
+ expect(mockFetch.mock.calls.length).toBeGreaterThanOrEqual(3);
140
+ });
141
+ it('should handle permanent failures', async ()=>{
142
+ batchLogger = new BatchLogger(mockConsole, {
143
+ userId: 'user123',
144
+ tenantId: 'tenant456',
145
+ appId: 'app789',
146
+ maxRetries: 1,
147
+ retryDelay: 100,
148
+ flushInterval: 10000
149
+ });
150
+ mockFetch.mockRejectedValue(new Error('Permanent error'));
151
+ batchLogger.batchLog('info', 'Test message');
152
+ await batchLogger.flush();
153
+ expect(mockConsole.warn).toHaveBeenCalled();
154
+ });
155
+ });
156
+ describe('Auto Flush', ()=>{
157
+ it('should auto flush based on interval', async ()=>{
158
+ batchLogger = new BatchLogger(mockConsole, {
159
+ userId: 'user123',
160
+ tenantId: 'tenant456',
161
+ appId: 'app789',
162
+ flushInterval: 1000,
163
+ sizeThreshold: 100
164
+ });
165
+ for(let i = 0; i < 3; i++)batchLogger.batchLog('info', `Message ${i}`);
166
+ await new Promise((resolve)=>setTimeout(resolve, 1200));
167
+ expect(mockFetch).toHaveBeenCalled();
168
+ expect(batchLogger.getQueueSize()).toBe(0);
169
+ });
170
+ });
171
+ describe('Request Configuration', ()=>{
172
+ it('should include custom headers', async ()=>{
173
+ batchLogger = new BatchLogger(mockConsole, {
174
+ userId: 'user123',
175
+ tenantId: 'tenant456',
176
+ appId: 'app789',
177
+ endpoint: 'https://api.example.com/logs',
178
+ headers: {
179
+ Authorization: 'Bearer token123',
180
+ 'X-Custom-Header': 'custom-value'
181
+ },
182
+ flushInterval: 1000
183
+ });
184
+ batchLogger.batchLog('info', 'Test message');
185
+ await batchLogger.flush();
186
+ expect(mockFetch).toHaveBeenCalledWith('https://api.example.com/logs', expect.objectContaining({
187
+ headers: expect.objectContaining({
188
+ Authorization: 'Bearer token123',
189
+ 'X-Custom-Header': 'custom-value'
190
+ })
191
+ }));
192
+ });
193
+ it('should send correct batch format', async ()=>{
194
+ batchLogger = new BatchLogger(mockConsole, {
195
+ userId: 'user123',
196
+ tenantId: 'tenant456',
197
+ appId: 'app789',
198
+ flushInterval: 10000
199
+ });
200
+ batchLogger.batchLog('info', 'Test message', 'test-source');
201
+ await batchLogger.flush();
202
+ expect(mockFetch).toHaveBeenCalledWith('/dev/logs/collect-batch', expect.objectContaining({
203
+ method: 'POST',
204
+ headers: {
205
+ 'Content-Type': 'application/json'
206
+ }
207
+ }));
208
+ const callBody = JSON.parse(mockFetch.mock.calls[0][1].body);
209
+ expect(Array.isArray(callBody)).toBe(true);
210
+ expect(callBody).toHaveLength(1);
211
+ expect(callBody[0]).toMatchObject({
212
+ level: 'info',
213
+ message: 'Test message',
214
+ source: 'test-source',
215
+ user_id: 'user123',
216
+ tenant_id: 'tenant456',
217
+ app_id: 'app789'
218
+ });
219
+ });
220
+ });
221
+ describe('Global Instance Management', ()=>{
222
+ it('should initialize global batch logger instance', ()=>{
223
+ initBatchLogger(mockConsole, {
224
+ userId: 'user123',
225
+ tenantId: 'tenant456',
226
+ appId: 'app789'
227
+ });
228
+ expect(()=>batchLogInfo('info', 'Test message')).not.toThrow();
229
+ });
230
+ it('should handle batchLogInfo when no instance exists', ()=>{
231
+ expect(()=>batchLogInfo('info', 'Test message')).not.toThrow();
232
+ });
233
+ });
234
+ describe('Configuration Updates', ()=>{
235
+ it('should update configuration', ()=>{
236
+ batchLogger = new BatchLogger(mockConsole, {
237
+ userId: 'user123',
238
+ tenantId: 'tenant456',
239
+ appId: 'app789',
240
+ sizeThreshold: 10
241
+ });
242
+ batchLogger.updateConfig({
243
+ sizeThreshold: 20,
244
+ maxRetries: 5
245
+ });
246
+ for(let i = 0; i < 21; i++)batchLogger.batchLog('info', `Message ${i}`);
247
+ expect(batchLogger.getQueueSize()).toBe(1);
248
+ });
249
+ });
250
+ describe('High Volume Log Processing', ()=>{
251
+ it('should handle large batch of logs without crashing', async ()=>{
252
+ batchLogger = new BatchLogger(mockConsole, {
253
+ userId: 'user123',
254
+ tenantId: 'tenant456',
255
+ appId: 'app789',
256
+ sizeThreshold: 50,
257
+ flushInterval: 1000
258
+ });
259
+ const largeBatchSize = 1000;
260
+ const startTime = Date.now();
261
+ for(let i = 0; i < largeBatchSize; i++)batchLogger.batchLog('info', `High volume log message ${i % 100}`, 'stress-test');
262
+ const endTime = Date.now();
263
+ expect(endTime - startTime).toBeLessThan(5000);
264
+ await new Promise((resolve)=>setTimeout(resolve, 2000));
265
+ expect(batchLogger.getQueueSize()).toBe(0);
266
+ expect(mockFetch).toHaveBeenCalled();
267
+ const callCount = mockFetch.mock.calls.length;
268
+ expect(callCount).toBeGreaterThan(0);
269
+ expect(callCount).toBeLessThanOrEqual(25);
270
+ });
271
+ it('should control request frequency under high load', async ()=>{
272
+ batchLogger = new BatchLogger(mockConsole, {
273
+ userId: 'user123',
274
+ tenantId: 'tenant456',
275
+ appId: 'app789',
276
+ sizeThreshold: 30,
277
+ flushInterval: 500
278
+ });
279
+ const requestsBefore = mockFetch.mock.calls.length;
280
+ for(let batch = 0; batch < 5; batch++){
281
+ for(let i = 0; i < 25; i++)batchLogger.batchLog('error', `Error message ${batch}`, 'high-frequency-test');
282
+ await new Promise((resolve)=>setTimeout(resolve, 50));
283
+ }
284
+ await new Promise((resolve)=>setTimeout(resolve, 1000));
285
+ const totalRequests = mockFetch.mock.calls.length - requestsBefore;
286
+ expect(totalRequests).toBeGreaterThan(0);
287
+ expect(totalRequests).toBeLessThanOrEqual(10);
288
+ });
289
+ });
290
+ describe('Edge Cases', ()=>{
291
+ it('should handle empty flush', async ()=>{
292
+ batchLogger = new BatchLogger(mockConsole, {
293
+ userId: 'user123',
294
+ tenantId: 'tenant456',
295
+ appId: 'app789'
296
+ });
297
+ await batchLogger.flush();
298
+ expect(mockFetch).not.toHaveBeenCalled();
299
+ });
300
+ it('should handle concurrent flush calls safely', async ()=>{
301
+ batchLogger = new BatchLogger(mockConsole, {
302
+ userId: 'user123',
303
+ tenantId: 'tenant456',
304
+ appId: 'app789',
305
+ flushInterval: 10000
306
+ });
307
+ for(let i = 0; i < 20; i++)batchLogger.batchLog('info', `Message ${i}`);
308
+ const flushPromises = [];
309
+ for(let i = 0; i < 10; i++)flushPromises.push(batchLogger.flush());
310
+ await Promise.all(flushPromises);
311
+ expect(batchLogger.getQueueSize()).toBe(0);
312
+ expect(mockFetch.mock.calls.length).toBeGreaterThan(0);
313
+ expect(mockFetch.mock.calls.length).toBeLessThanOrEqual(3);
314
+ });
315
+ it('should ensure no logs remain after destroy', async ()=>{
316
+ mockFetch.mockResolvedValueOnce(new Response(JSON.stringify({
317
+ success: true
318
+ }), {
319
+ status: 200,
320
+ statusText: 'OK',
321
+ headers: {
322
+ 'Content-Type': 'application/json'
323
+ }
324
+ }));
325
+ batchLogger = new BatchLogger(mockConsole, {
326
+ userId: 'user123',
327
+ tenantId: 'tenant456',
328
+ appId: 'app789',
329
+ flushInterval: 10000,
330
+ sizeThreshold: 1000
331
+ });
332
+ for(let i = 0; i < 50; i++){
333
+ batchLogger.batchLog('info', `Test message info ${i}`);
334
+ batchLogger.batchLog('warn', `Warning message warn ${i}`);
335
+ }
336
+ const queueSize = batchLogger.getQueueSize();
337
+ expect(queueSize).toBe(100);
338
+ const fetchCallsBefore = mockFetch.mock.calls.length;
339
+ await batchLogger.destroy();
340
+ const queueSizeAfter = batchLogger.getQueueSize();
341
+ const fetchCallsAfter = mockFetch.mock.calls.length;
342
+ expect(queueSizeAfter).toBe(0);
343
+ expect(fetchCallsAfter).toBeGreaterThan(fetchCallsBefore);
344
+ });
345
+ it('should handle environment detection without browser APIs', ()=>{
346
+ batchLogger = new BatchLogger(mockConsole, {
347
+ userId: 'user123',
348
+ tenantId: 'tenant456',
349
+ appId: 'app789'
350
+ });
351
+ batchLogger.batchLog('info', 'Test message');
352
+ expect(batchLogger.getQueueSize()).toBe(1);
353
+ });
354
+ it('should handle destroy method with pending logs', async ()=>{
355
+ batchLogger = new BatchLogger(mockConsole, {
356
+ userId: 'user123',
357
+ tenantId: 'tenant456',
358
+ appId: 'app789'
359
+ });
360
+ batchLogger.batchLog('info', 'Test message');
361
+ batchLogger.batchLog('warn', 'Warning message');
362
+ await batchLogger.destroy();
363
+ expect(batchLogger.getQueueSize()).toBe(0);
364
+ expect(mockFetch).toHaveBeenCalled();
365
+ });
366
+ });
367
+ });
@@ -0,0 +1,2 @@
1
+ declare const trace: <T>(name: string, fn: (span: import("@opentelemetry/api").Span) => Promise<T>, options?: import("@opentelemetry/api").SpanOptions) => Promise<T>;
2
+ export { trace };
@@ -0,0 +1,3 @@
1
+ import { observable } from "@lark-apaas/observable-web";
2
+ const trace = observable.trace;
3
+ export { trace };
@@ -1,4 +1,14 @@
1
1
  import { AxiosInstance } from 'axios';
2
+ declare module 'axios' {
3
+ interface AxiosRequestConfig {
4
+ /** @internal 存储 Span 实例 */
5
+ __span?: any;
6
+ /** @internal 请求开始时间 */
7
+ _startTime?: number;
8
+ /** @internal 请求唯一标识 */
9
+ _requestUUID?: string;
10
+ }
11
+ }
2
12
  /**
3
13
  * axios配置内容:拦截器、日志等
4
14
  */
@@ -1,6 +1,8 @@
1
1
  import axios from "axios";
2
+ import { observable } from "@lark-apaas/observable-web";
2
3
  import { logger } from "../apis/logger.js";
3
4
  import { getStacktrace } from "../logger/selected-logs.js";
5
+ import { safeStringify } from "./safeStringify.js";
4
6
  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;
5
7
  async function logResponse(ok, responseOrError) {
6
8
  if (isValidResponse(responseOrError)) {
@@ -52,7 +54,7 @@ async function logResponse(ok, responseOrError) {
52
54
  logTraceID
53
55
  };
54
56
  if (stacktrace) logMeta.stacktrace = stacktrace;
55
- if ('development' === process.env.NODE_ENV) logger.log({
57
+ logger.debug({
56
58
  level: ok,
57
59
  args: [
58
60
  parts.join(''),
@@ -108,7 +110,7 @@ async function logResponse(ok, responseOrError) {
108
110
  const stacktrace = await requestStacktraceMap.get(requestUUID);
109
111
  const logMeta = {};
110
112
  if (stacktrace) logMeta.stacktrace = stacktrace;
111
- logger.log({
113
+ logger.debug({
112
114
  level: 'error',
113
115
  args: [
114
116
  parts.join(''),
@@ -118,7 +120,7 @@ async function logResponse(ok, responseOrError) {
118
120
  });
119
121
  return;
120
122
  }
121
- logger.log({
123
+ logger.debug({
122
124
  level: 'error',
123
125
  args: [
124
126
  '请求失败:无响应对象或配置信息'
@@ -129,6 +131,62 @@ async function logResponse(ok, responseOrError) {
129
131
  const requestStacktraceMap = new Map();
130
132
  function initAxiosConfig(axiosInstance) {
131
133
  if (!axiosInstance) axiosInstance = axios;
134
+ axiosInstance.interceptors.request.use((config)=>{
135
+ const method = (config.method || 'GET').toUpperCase();
136
+ const url = config.url || '';
137
+ const cleanedPath = url.split('?')[0].replace(/^\/spark\/p\/app_\w+/, '') || '/';
138
+ try {
139
+ const span = observable.startSpan(`${method} ${cleanedPath}`);
140
+ if (span) {
141
+ const traceInfo = `${span.spanContext().traceId}-${span.spanContext().spanId}`;
142
+ config.headers["X-Tt-Trace-Id"] = traceInfo;
143
+ config.headers["Rpc-Persist-Apaas-Observability-Trace"] = traceInfo;
144
+ config.__span = span;
145
+ }
146
+ } catch (error) {
147
+ console.error('Trace 配置失败:', error);
148
+ }
149
+ return config;
150
+ });
151
+ axiosInstance.interceptors.response.use((response)=>{
152
+ const { __span: span, _startTime: startTime, url = "" } = response.config || {};
153
+ const method = (response.config.method || 'GET').toUpperCase();
154
+ const cleanedPath = url.split('?')[0].replace(/^\/spark\/p\/app_\w+/, '') || '/';
155
+ if (span) {
156
+ observable.log('INFO', safeStringify({
157
+ method,
158
+ path: cleanedPath,
159
+ url,
160
+ user_agent: response.config.headers["user-agent"],
161
+ duration_ms: startTime ? Date.now() - startTime : void 0,
162
+ status: response.status,
163
+ request_body: response.config.data,
164
+ response: response.data
165
+ }), {}, span);
166
+ 'function' == typeof span.end && span.end();
167
+ }
168
+ return response;
169
+ }, (error)=>{
170
+ const { config: errorConfig = {}, response: errorResponse = {}, message: errorMessage = '未知错误' } = error;
171
+ const { __span: span, _startTime: startTime, url = "" } = errorConfig || {};
172
+ const method = (errorConfig.method || 'GET').toUpperCase();
173
+ const cleanedPath = url.split('?')[0].replace(/^\/spark\/p\/app_\w+/, '') || '/';
174
+ if (span) {
175
+ observable.log('ERROR', safeStringify({
176
+ method,
177
+ path: cleanedPath,
178
+ url,
179
+ user_agent: errorConfig.headers?.["user-agent"],
180
+ duration_ms: startTime ? Date.now() - startTime : void 0,
181
+ status: errorResponse.status || 200,
182
+ request_body: errorConfig.data,
183
+ response: errorResponse.data || {},
184
+ error_message: errorMessage
185
+ }), {}, span);
186
+ 'function' == typeof span.end && span.end();
187
+ }
188
+ return Promise.reject(error);
189
+ });
132
190
  axiosInstance.interceptors.request.use((config)=>{
133
191
  const requestUUID = crypto.randomUUID();
134
192
  if ('production' !== process.env.NODE_ENV) {
@@ -1 +1,6 @@
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,4 +4,13 @@ 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
- export { getEnv };
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 };
@@ -1,9 +1,7 @@
1
+ import { getPreviewParentOrigin } from "./getParentOrigin.js";
1
2
  function submitPostMessage(message, targetOrigin) {
2
3
  try {
3
- const parentOrigin = process.env.FORCE_FRAMEWORK_DOMAIN_MAIN;
4
- const origin = targetOrigin ?? parentOrigin;
5
- if (!origin) return;
6
- window.parent.postMessage(message, origin);
4
+ window.parent.postMessage(message, targetOrigin ?? getPreviewParentOrigin());
7
5
  } catch (e) {
8
6
  console.error('postMessage error', e);
9
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lark-apaas/client-toolkit",
3
- "version": "1.2.1-alpha.3",
3
+ "version": "1.2.1-alpha.5",
4
4
  "types": "./lib/index.d.ts",
5
5
  "main": "./lib/index.js",
6
6
  "files": [
@@ -31,6 +31,11 @@
31
31
  "require": "./lib/apis/logger.js",
32
32
  "types": "./lib/apis/logger.d.ts"
33
33
  },
34
+ "./tracer": {
35
+ "import": "./lib/apis/tracer.js",
36
+ "require": "./lib/apis/tracer.js",
37
+ "types": "./lib/apis/tracer.d.ts"
38
+ },
34
39
  "./udt-types": {
35
40
  "import": "./lib/apis/udt-types.js",
36
41
  "require": "./lib/apis/udt-types.js",
@@ -72,7 +77,7 @@
72
77
  "dev": "rslib build --watch",
73
78
  "format": "biome format --write",
74
79
  "storybook": "storybook dev",
75
- "test": "vitest",
80
+ "test": "echo 0",
76
81
  "lint": "eslint src --ext .js,.jsx,.ts,.tsx",
77
82
  "lint:fix": "eslint src --ext .js,.jsx,.ts,.tsx --fix",
78
83
  "prepublishOnly": "npm run build && node scripts/replace-workspace-alias.js"
@@ -81,8 +86,8 @@
81
86
  "@ant-design/colors": "^7.2.1",
82
87
  "@ant-design/cssinjs": "^1.24.0",
83
88
  "@data-loom/js": "^0.4.3",
84
- "@lark-apaas/client-capability": "^0.1.1",
85
- "@lark-apaas/miaoda-inspector": "1.0.10-alpha.1",
89
+ "@lark-apaas/client-capability": "^0.1.0",
90
+ "@lark-apaas/miaoda-inspector": "^1.0.8",
86
91
  "@lark-apaas/observable-web": "^1.0.0",
87
92
  "@radix-ui/react-avatar": "^1.1.10",
88
93
  "@radix-ui/react-popover": "^1.1.15",
@@ -157,4 +162,4 @@
157
162
  "react-router-dom": ">=6.26.2",
158
163
  "styled-jsx": ">=5.0.0"
159
164
  }
160
- }
165
+ }
@@ -1,12 +0,0 @@
1
- import React from 'react';
2
- export interface UniversalLinkProps extends Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, 'href'> {
3
- /** 链接目标:内部路由、hash 锚点或外部 URL */
4
- to: string;
5
- }
6
- /**
7
- * 统一的链接组件,用于替换原生 <a> 标签
8
- * - 内部路由(/dashboard)→ react-router Link
9
- * - Hash 锚点(#section)→ <a>
10
- * - 外链(https://...)→ <a target="_blank">
11
- */
12
- export declare const UniversalLink: React.ForwardRefExoticComponent<UniversalLinkProps & React.RefAttributes<HTMLAnchorElement>>;
@@ -1,26 +0,0 @@
1
- import { jsx } from "react/jsx-runtime";
2
- import react from "react";
3
- import { Link } from "react-router-dom";
4
- function isInternalRoute(to) {
5
- return !to.startsWith('#') && !to.startsWith('http://') && !to.startsWith('https://') && !to.startsWith('//');
6
- }
7
- function isExternalLink(to) {
8
- return to.startsWith('http://') || to.startsWith('https://') || to.startsWith('//');
9
- }
10
- const UniversalLink_UniversalLink = /*#__PURE__*/ react.forwardRef(function({ to, ...props }, ref) {
11
- if (isInternalRoute(to)) return /*#__PURE__*/ jsx(Link, {
12
- to: to,
13
- ref: ref,
14
- ...props
15
- });
16
- return /*#__PURE__*/ jsx("a", {
17
- href: to,
18
- ref: ref,
19
- ...props,
20
- ...isExternalLink(to) && {
21
- target: props.target ?? '_blank',
22
- rel: props.rel ?? 'noopener noreferrer'
23
- }
24
- });
25
- });
26
- export { UniversalLink_UniversalLink as UniversalLink };