@lark-apaas/client-toolkit 1.0.8-alpha.1 → 1.0.9
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.
|
@@ -35,10 +35,6 @@ export interface ApiResponse<T = any> {
|
|
|
35
35
|
status: number;
|
|
36
36
|
/** 状态文本 */
|
|
37
37
|
statusText: string;
|
|
38
|
-
/** 请求是否成功 (2xx 状态码) */
|
|
39
|
-
success: boolean;
|
|
40
|
-
/** 响应头(序列化为普通对象,可通过 postMessage 传递) */
|
|
41
|
-
headers?: Record<string, string>;
|
|
42
38
|
}
|
|
43
39
|
/**
|
|
44
40
|
* API 错误类
|
|
@@ -49,28 +45,39 @@ export declare class ApiError extends Error {
|
|
|
49
45
|
response?: Response;
|
|
50
46
|
config?: RequestConfig;
|
|
51
47
|
code?: string;
|
|
52
|
-
|
|
53
|
-
responseData?: any;
|
|
54
|
-
constructor(message: string, config?: RequestConfig, response?: Response, code?: string, responseData?: any);
|
|
55
|
-
/**
|
|
56
|
-
* 序列化为可传递的 JSON 对象(用于 postMessage 等场景)
|
|
57
|
-
* 移除不可序列化的对象(如 Response、Request 等)
|
|
58
|
-
*/
|
|
59
|
-
toJSON(): object;
|
|
48
|
+
constructor(message: string, config?: RequestConfig, response?: Response, code?: string);
|
|
60
49
|
}
|
|
50
|
+
/**
|
|
51
|
+
* 请求拦截器类型
|
|
52
|
+
*/
|
|
53
|
+
export type RequestInterceptor = (config: RequestConfig) => RequestConfig | Promise<RequestConfig>;
|
|
54
|
+
/**
|
|
55
|
+
* 响应拦截器类型
|
|
56
|
+
*/
|
|
57
|
+
export type ResponseInterceptor = <T = any>(response: ApiResponse<T>) => ApiResponse<T> | Promise<ApiResponse<T>>;
|
|
58
|
+
/**
|
|
59
|
+
* 错误拦截器类型
|
|
60
|
+
*/
|
|
61
|
+
export type ErrorInterceptor = (error: ApiError) => any;
|
|
61
62
|
/**
|
|
62
63
|
* 通用接口测试 API 代理类
|
|
63
64
|
*
|
|
64
65
|
* 特性:
|
|
65
66
|
* - 支持所有 RESTful 请求方法 (GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS)
|
|
66
67
|
* - 完善的错误处理和边界检查
|
|
68
|
+
* - 请求/响应拦截器
|
|
67
69
|
* - 超时控制和请求取消
|
|
68
70
|
* - 支持多种响应类型
|
|
69
|
-
* - 所有响应数据可序列化(可通过 postMessage 传递)
|
|
70
71
|
*/
|
|
71
72
|
declare class ApiProxy {
|
|
72
73
|
/** 默认配置 */
|
|
73
74
|
private defaultConfig;
|
|
75
|
+
/** 请求拦截器队列 */
|
|
76
|
+
private requestInterceptors;
|
|
77
|
+
/** 响应拦截器队列 */
|
|
78
|
+
private responseInterceptors;
|
|
79
|
+
/** 错误拦截器队列 */
|
|
80
|
+
private errorInterceptors;
|
|
74
81
|
/** 活跃的请求控制器 Map */
|
|
75
82
|
private activeRequests;
|
|
76
83
|
/**
|
|
@@ -78,6 +85,18 @@ declare class ApiProxy {
|
|
|
78
85
|
* @param baseConfig 基础配置
|
|
79
86
|
*/
|
|
80
87
|
constructor(baseConfig?: Partial<RequestConfig>);
|
|
88
|
+
/**
|
|
89
|
+
* 添加请求拦截器
|
|
90
|
+
*/
|
|
91
|
+
useRequestInterceptor(interceptor: RequestInterceptor): void;
|
|
92
|
+
/**
|
|
93
|
+
* 添加响应拦截器
|
|
94
|
+
*/
|
|
95
|
+
useResponseInterceptor(interceptor: ResponseInterceptor): void;
|
|
96
|
+
/**
|
|
97
|
+
* 添加错误拦截器
|
|
98
|
+
*/
|
|
99
|
+
useErrorInterceptor(interceptor: ErrorInterceptor): void;
|
|
81
100
|
/**
|
|
82
101
|
* 验证 URL 格式
|
|
83
102
|
*/
|
|
@@ -95,9 +114,17 @@ declare class ApiProxy {
|
|
|
95
114
|
*/
|
|
96
115
|
private parseResponse;
|
|
97
116
|
/**
|
|
98
|
-
*
|
|
117
|
+
* 执行请求拦截器
|
|
118
|
+
*/
|
|
119
|
+
private runRequestInterceptors;
|
|
120
|
+
/**
|
|
121
|
+
* 执行响应拦截器
|
|
122
|
+
*/
|
|
123
|
+
private runResponseInterceptors;
|
|
124
|
+
/**
|
|
125
|
+
* 执行错误拦截器
|
|
99
126
|
*/
|
|
100
|
-
private
|
|
127
|
+
private runErrorInterceptors;
|
|
101
128
|
/**
|
|
102
129
|
* 生成请求唯一键
|
|
103
130
|
*/
|
|
@@ -4,8 +4,7 @@ class ApiError extends Error {
|
|
|
4
4
|
response;
|
|
5
5
|
config;
|
|
6
6
|
code;
|
|
7
|
-
|
|
8
|
-
constructor(message, config, response, code, responseData){
|
|
7
|
+
constructor(message, config, response, code){
|
|
9
8
|
super(message);
|
|
10
9
|
this.name = 'ApiError';
|
|
11
10
|
this.config = config;
|
|
@@ -13,27 +12,8 @@ class ApiError extends Error {
|
|
|
13
12
|
this.status = response?.status;
|
|
14
13
|
this.statusText = response?.statusText;
|
|
15
14
|
this.code = code;
|
|
16
|
-
this.responseData = responseData;
|
|
17
15
|
Object.setPrototypeOf(this, ApiError.prototype);
|
|
18
16
|
}
|
|
19
|
-
toJSON() {
|
|
20
|
-
return {
|
|
21
|
-
name: this.name,
|
|
22
|
-
message: this.message,
|
|
23
|
-
code: this.code,
|
|
24
|
-
status: this.status,
|
|
25
|
-
statusText: this.statusText,
|
|
26
|
-
responseData: this.responseData,
|
|
27
|
-
config: this.config ? {
|
|
28
|
-
url: this.config.url,
|
|
29
|
-
method: this.config.method,
|
|
30
|
-
headers: this.config.headers,
|
|
31
|
-
params: this.config.params,
|
|
32
|
-
timeout: this.config.timeout,
|
|
33
|
-
responseType: this.config.responseType
|
|
34
|
-
} : void 0
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
17
|
}
|
|
38
18
|
class ApiProxy {
|
|
39
19
|
defaultConfig = {
|
|
@@ -43,9 +23,12 @@ class ApiProxy {
|
|
|
43
23
|
headers: {
|
|
44
24
|
'Content-Type': 'application/json',
|
|
45
25
|
'X-Suda-Csrf-Token': window.csrfToken || '',
|
|
46
|
-
'Cache-Control': 'no-store
|
|
26
|
+
'Cache-Control': 'no-store'
|
|
47
27
|
}
|
|
48
28
|
};
|
|
29
|
+
requestInterceptors = [];
|
|
30
|
+
responseInterceptors = [];
|
|
31
|
+
errorInterceptors = [];
|
|
49
32
|
activeRequests = new Map();
|
|
50
33
|
constructor(baseConfig){
|
|
51
34
|
if (baseConfig) this.defaultConfig = {
|
|
@@ -53,6 +36,18 @@ class ApiProxy {
|
|
|
53
36
|
...baseConfig
|
|
54
37
|
};
|
|
55
38
|
}
|
|
39
|
+
useRequestInterceptor(interceptor) {
|
|
40
|
+
if ('function' != typeof interceptor) throw new TypeError('Request interceptor must be a function');
|
|
41
|
+
this.requestInterceptors.push(interceptor);
|
|
42
|
+
}
|
|
43
|
+
useResponseInterceptor(interceptor) {
|
|
44
|
+
if ('function' != typeof interceptor) throw new TypeError('Response interceptor must be a function');
|
|
45
|
+
this.responseInterceptors.push(interceptor);
|
|
46
|
+
}
|
|
47
|
+
useErrorInterceptor(interceptor) {
|
|
48
|
+
if ('function' != typeof interceptor) throw new TypeError('Error interceptor must be a function');
|
|
49
|
+
this.errorInterceptors.push(interceptor);
|
|
50
|
+
}
|
|
56
51
|
validateUrl(url) {
|
|
57
52
|
if (!url || 'string' != typeof url) return false;
|
|
58
53
|
if (url.startsWith('/') || url.startsWith('./') || url.startsWith('../')) return true;
|
|
@@ -120,12 +115,35 @@ class ApiProxy {
|
|
|
120
115
|
throw new ApiError(`Failed to parse response as ${responseType}`, void 0, response, 'PARSE_ERROR');
|
|
121
116
|
}
|
|
122
117
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
118
|
+
async runRequestInterceptors(config) {
|
|
119
|
+
let modifiedConfig = {
|
|
120
|
+
...config
|
|
121
|
+
};
|
|
122
|
+
for (const interceptor of this.requestInterceptors)try {
|
|
123
|
+
modifiedConfig = await interceptor(modifiedConfig);
|
|
124
|
+
} catch (error) {
|
|
125
|
+
console.error('Request interceptor error:', error);
|
|
126
|
+
throw new ApiError('Request interceptor failed', modifiedConfig, void 0, 'INTERCEPTOR_ERROR');
|
|
127
|
+
}
|
|
128
|
+
return modifiedConfig;
|
|
129
|
+
}
|
|
130
|
+
async runResponseInterceptors(response) {
|
|
131
|
+
let modifiedResponse = response;
|
|
132
|
+
for (const interceptor of this.responseInterceptors)try {
|
|
133
|
+
modifiedResponse = await interceptor(modifiedResponse);
|
|
134
|
+
} catch (error) {
|
|
135
|
+
console.error('Response interceptor error:', error);
|
|
136
|
+
}
|
|
137
|
+
return modifiedResponse;
|
|
138
|
+
}
|
|
139
|
+
async runErrorInterceptors(error) {
|
|
140
|
+
for (const interceptor of this.errorInterceptors)try {
|
|
141
|
+
const result = await interceptor(error);
|
|
142
|
+
if (void 0 !== result) return result;
|
|
143
|
+
} catch (err) {
|
|
144
|
+
console.error('Error interceptor failed:', err);
|
|
145
|
+
}
|
|
146
|
+
throw error;
|
|
129
147
|
}
|
|
130
148
|
generateRequestKey(config) {
|
|
131
149
|
return `${config.method || 'GET'}_${config.url}_${Date.now()}_${Math.random()}`;
|
|
@@ -142,8 +160,15 @@ class ApiProxy {
|
|
|
142
160
|
...config.headers
|
|
143
161
|
}
|
|
144
162
|
};
|
|
145
|
-
const
|
|
146
|
-
|
|
163
|
+
const finalConfig = await this.runRequestInterceptors(mergedConfig);
|
|
164
|
+
const requestKey = this.generateRequestKey(finalConfig);
|
|
165
|
+
try {
|
|
166
|
+
const response = await this.executeRequest(finalConfig, requestKey);
|
|
167
|
+
return response;
|
|
168
|
+
} catch (error) {
|
|
169
|
+
const apiError = error instanceof ApiError ? error : new ApiError(error instanceof Error ? error.message : 'Unknown error', finalConfig, void 0, 'REQUEST_FAILED');
|
|
170
|
+
return await this.runErrorInterceptors(apiError);
|
|
171
|
+
}
|
|
147
172
|
}
|
|
148
173
|
async executeRequest(config, requestKey) {
|
|
149
174
|
let abortController;
|
|
@@ -172,28 +197,27 @@ class ApiProxy {
|
|
|
172
197
|
const response = await fetch(fullUrl, fetchOptions);
|
|
173
198
|
if (timeoutId) clearTimeout(timeoutId);
|
|
174
199
|
this.activeRequests.delete(requestKey);
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
data = 204 === response.status || 304 === response.status ? null : await this.parseResponse(response, config.responseType || 'json');
|
|
178
|
-
} catch (parseError) {
|
|
179
|
-
console.error('Failed to parse response:', parseError);
|
|
180
|
-
data = null;
|
|
181
|
-
}
|
|
200
|
+
if (!response.ok) throw new ApiError(`HTTP Error: ${response.status} ${response.statusText}`, config, response, 'HTTP_ERROR');
|
|
201
|
+
const data = await this.parseResponse(response, config.responseType || 'json');
|
|
182
202
|
const apiResponse = {
|
|
183
203
|
data,
|
|
184
204
|
status: response.status,
|
|
185
|
-
statusText: response.statusText
|
|
186
|
-
success: response.ok,
|
|
187
|
-
headers: this.serializeHeaders(response.headers)
|
|
205
|
+
statusText: response.statusText
|
|
188
206
|
};
|
|
189
|
-
return apiResponse;
|
|
207
|
+
return await this.runResponseInterceptors(apiResponse);
|
|
190
208
|
} catch (error) {
|
|
209
|
+
let errorResponse = null, errorResponseString = '';
|
|
210
|
+
if (error.response && error.config.responseType) errorResponse = await this.parseResponse(error.response, error.config.responseType || 'json');
|
|
211
|
+
try {
|
|
212
|
+
errorResponseString = JSON.stringify(errorResponse);
|
|
213
|
+
} catch (error) {
|
|
214
|
+
throw new ApiError(error instanceof Error ? error.message : 'Response parse error', config, void 0, 'UNKNOWN_ERROR');
|
|
215
|
+
}
|
|
191
216
|
if (timeoutId) clearTimeout(timeoutId);
|
|
192
217
|
this.activeRequests.delete(requestKey);
|
|
193
|
-
if (error instanceof ApiError) throw error;
|
|
194
218
|
if (error instanceof Error && 'AbortError' === error.name) throw new ApiError(config.timeout ? 'Request timeout' : 'Request cancelled', config, void 0, 'ABORT_ERROR');
|
|
195
219
|
if (error instanceof TypeError) throw new ApiError('Network error or CORS issue', config, void 0, 'NETWORK_ERROR');
|
|
196
|
-
throw new ApiError(
|
|
220
|
+
throw new ApiError(errorResponseString, config, errorResponse, 'UNKNOWN_ERROR');
|
|
197
221
|
}
|
|
198
222
|
}
|
|
199
223
|
async get(url, config) {
|
package/lib/utils/getAppId.js
CHANGED
|
@@ -2,10 +2,11 @@ function getAppId(path) {
|
|
|
2
2
|
if (window.MIAODA_APP_ID) return window.MIAODA_APP_ID;
|
|
3
3
|
let prefix;
|
|
4
4
|
prefix = path.includes('/ai/feida/runtime/') ? '/ai/feida/runtime/' : path.includes('/spark/r/') ? '/spark/r/' : '/ai/miaoda/';
|
|
5
|
-
|
|
5
|
+
const windowAppId = window.appId || null;
|
|
6
|
+
if (!path.startsWith(prefix)) return windowAppId;
|
|
6
7
|
const remainder = path.substring(prefix.length);
|
|
7
8
|
const nextSlashIndex = remainder.indexOf('/');
|
|
8
|
-
if (-1 === nextSlashIndex) return remainder;
|
|
9
|
-
return remainder.substring(0, nextSlashIndex);
|
|
9
|
+
if (-1 === nextSlashIndex) return remainder || windowAppId;
|
|
10
|
+
return remainder.substring(0, nextSlashIndex) || windowAppId;
|
|
10
11
|
}
|
|
11
12
|
export { getAppId };
|