@yh-ui/request 0.1.21
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.
- package/LICENSE +21 -0
- package/README.md +274 -0
- package/dist/adapters/fetch.cjs +157 -0
- package/dist/adapters/fetch.d.ts +25 -0
- package/dist/adapters/fetch.mjs +148 -0
- package/dist/adapters/index.cjs +27 -0
- package/dist/adapters/index.d.ts +5 -0
- package/dist/adapters/index.mjs +2 -0
- package/dist/adapters/platform.cjs +394 -0
- package/dist/adapters/platform.d.ts +72 -0
- package/dist/adapters/platform.mjs +369 -0
- package/dist/cache/index.cjs +56 -0
- package/dist/cache/index.d.ts +21 -0
- package/dist/cache/index.mjs +14 -0
- package/dist/cache/indexedDB.cjs +188 -0
- package/dist/cache/indexedDB.d.ts +58 -0
- package/dist/cache/indexedDB.mjs +176 -0
- package/dist/cache/localStorage.cjs +158 -0
- package/dist/cache/localStorage.d.ts +58 -0
- package/dist/cache/localStorage.mjs +153 -0
- package/dist/cache/memory.cjs +112 -0
- package/dist/cache/memory.d.ts +71 -0
- package/dist/cache/memory.mjs +103 -0
- package/dist/graphql.cjs +255 -0
- package/dist/graphql.d.ts +192 -0
- package/dist/graphql.mjs +235 -0
- package/dist/http-cache.cjs +248 -0
- package/dist/http-cache.d.ts +156 -0
- package/dist/http-cache.mjs +233 -0
- package/dist/index.cjs +181 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.mjs +16 -0
- package/dist/interceptors/debug.cjs +139 -0
- package/dist/interceptors/debug.d.ts +92 -0
- package/dist/interceptors/debug.mjs +130 -0
- package/dist/interceptors/index.cjs +38 -0
- package/dist/interceptors/index.d.ts +6 -0
- package/dist/interceptors/index.mjs +3 -0
- package/dist/interceptors/progress.cjs +185 -0
- package/dist/interceptors/progress.d.ts +97 -0
- package/dist/interceptors/progress.mjs +177 -0
- package/dist/interceptors/security.cjs +154 -0
- package/dist/interceptors/security.d.ts +83 -0
- package/dist/interceptors/security.mjs +134 -0
- package/dist/plugin.cjs +166 -0
- package/dist/plugin.d.ts +106 -0
- package/dist/plugin.mjs +163 -0
- package/dist/request.cjs +396 -0
- package/dist/request.d.ts +111 -0
- package/dist/request.mjs +339 -0
- package/dist/types.cjs +13 -0
- package/dist/types.d.ts +157 -0
- package/dist/types.mjs +7 -0
- package/dist/useAIStream.cjs +125 -0
- package/dist/useAIStream.d.ts +89 -0
- package/dist/useAIStream.mjs +108 -0
- package/dist/useLoadMore.cjs +136 -0
- package/dist/useLoadMore.d.ts +84 -0
- package/dist/useLoadMore.mjs +134 -0
- package/dist/usePagination.cjs +141 -0
- package/dist/usePagination.d.ts +89 -0
- package/dist/usePagination.mjs +132 -0
- package/dist/useQueue.cjs +243 -0
- package/dist/useQueue.d.ts +118 -0
- package/dist/useQueue.mjs +239 -0
- package/dist/useRequest.cjs +325 -0
- package/dist/useRequest.d.ts +126 -0
- package/dist/useRequest.mjs +329 -0
- package/dist/useRequestQueue.cjs +36 -0
- package/dist/useRequestQueue.d.ts +52 -0
- package/dist/useRequestQueue.mjs +27 -0
- package/dist/useSSE.cjs +241 -0
- package/dist/useSSE.d.ts +74 -0
- package/dist/useSSE.mjs +226 -0
- package/dist/websocket.cjs +325 -0
- package/dist/websocket.d.ts +163 -0
- package/dist/websocket.mjs +316 -0
- package/package.json +61 -0
package/dist/request.mjs
ADDED
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
let requestIdCounter = 0;
|
|
2
|
+
export function generateRequestId() {
|
|
3
|
+
return `req_${Date.now()}_${++requestIdCounter}`;
|
|
4
|
+
}
|
|
5
|
+
export function serializeParams(params) {
|
|
6
|
+
const searchParams = new URLSearchParams();
|
|
7
|
+
const encode = (value) => {
|
|
8
|
+
if (value === null || value === void 0) return "";
|
|
9
|
+
if (typeof value === "object") return JSON.stringify(value);
|
|
10
|
+
return String(value);
|
|
11
|
+
};
|
|
12
|
+
const addParam = (key, value) => {
|
|
13
|
+
if (value !== null && value !== void 0) {
|
|
14
|
+
searchParams.append(key, encode(value));
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
for (const [key, value] of Object.entries(params)) {
|
|
18
|
+
if (Array.isArray(value)) {
|
|
19
|
+
value.forEach((v) => addParam(key, v));
|
|
20
|
+
} else {
|
|
21
|
+
addParam(key, value);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return searchParams.toString();
|
|
25
|
+
}
|
|
26
|
+
export function buildURL(options) {
|
|
27
|
+
const { baseURL = "", url = "", params, serializeParams: sp = true } = options;
|
|
28
|
+
let fullPath = url;
|
|
29
|
+
if (baseURL) {
|
|
30
|
+
fullPath = url.startsWith("http") ? url : baseURL + url;
|
|
31
|
+
}
|
|
32
|
+
if (params && Object.keys(params).length > 0 && sp) {
|
|
33
|
+
const queryString = serializeParams(params);
|
|
34
|
+
const separator = fullPath.includes("?") ? "&" : "?";
|
|
35
|
+
fullPath += separator + queryString;
|
|
36
|
+
}
|
|
37
|
+
return fullPath;
|
|
38
|
+
}
|
|
39
|
+
export function createRequestError(error, config, response) {
|
|
40
|
+
const err = typeof error === "string" ? new Error(error) : error;
|
|
41
|
+
const requestError = {
|
|
42
|
+
name: err.name,
|
|
43
|
+
message: err.message,
|
|
44
|
+
config,
|
|
45
|
+
response,
|
|
46
|
+
requestId: config?.requestId
|
|
47
|
+
};
|
|
48
|
+
if (error instanceof TypeError && error.message.includes("fetch")) {
|
|
49
|
+
requestError.isNetworkError = true;
|
|
50
|
+
requestError.code = "NETWORK_ERROR";
|
|
51
|
+
} else if (config?.signal?.aborted) {
|
|
52
|
+
requestError.isCanceled = true;
|
|
53
|
+
requestError.code = "CANCELED";
|
|
54
|
+
} else if (response) {
|
|
55
|
+
requestError.code = `HTTP_${response.status}`;
|
|
56
|
+
}
|
|
57
|
+
return requestError;
|
|
58
|
+
}
|
|
59
|
+
export class InterceptorManagerImpl {
|
|
60
|
+
handlers = [];
|
|
61
|
+
use(onFulfilled, onRejected) {
|
|
62
|
+
this.handlers.push({ fulfilled: onFulfilled, rejected: onRejected });
|
|
63
|
+
return this.handlers.length - 1;
|
|
64
|
+
}
|
|
65
|
+
eject(id) {
|
|
66
|
+
if (this.handlers[id]) {
|
|
67
|
+
this.handlers[id] = null;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
clear() {
|
|
71
|
+
this.handlers = [];
|
|
72
|
+
}
|
|
73
|
+
async execute(value) {
|
|
74
|
+
let result = value;
|
|
75
|
+
for (const handler of this.handlers) {
|
|
76
|
+
if (handler) {
|
|
77
|
+
try {
|
|
78
|
+
result = await handler.fulfilled(result);
|
|
79
|
+
} catch (error) {
|
|
80
|
+
if (handler.rejected) {
|
|
81
|
+
result = await handler.rejected(error);
|
|
82
|
+
} else {
|
|
83
|
+
throw error;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return result;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
export class Request {
|
|
92
|
+
defaults;
|
|
93
|
+
interceptors;
|
|
94
|
+
abortControllers = /* @__PURE__ */ new Map();
|
|
95
|
+
constructor(options = {}) {
|
|
96
|
+
this.defaults = {
|
|
97
|
+
timeout: 3e4,
|
|
98
|
+
credentials: "same-origin",
|
|
99
|
+
responseType: "json",
|
|
100
|
+
serializeParams: true,
|
|
101
|
+
retry: false,
|
|
102
|
+
retryTimes: 3,
|
|
103
|
+
retryDelay: 1e3,
|
|
104
|
+
retryCondition: (error) => {
|
|
105
|
+
return error.isNetworkError || (error.response?.status ?? 0) >= 500;
|
|
106
|
+
},
|
|
107
|
+
...options.defaultOptions
|
|
108
|
+
};
|
|
109
|
+
this.interceptors = {
|
|
110
|
+
request: new InterceptorManagerImpl(),
|
|
111
|
+
response: new InterceptorManagerImpl()
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* 创建请求实例的静态方法
|
|
116
|
+
*/
|
|
117
|
+
static create(options) {
|
|
118
|
+
return new Request(options);
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* 生成 AbortSignal
|
|
122
|
+
*/
|
|
123
|
+
createAbortSignal(options) {
|
|
124
|
+
const controller = new AbortController();
|
|
125
|
+
if (options.requestKey) {
|
|
126
|
+
const prevController = this.abortControllers.get(options.requestKey);
|
|
127
|
+
if (prevController) {
|
|
128
|
+
prevController.abort();
|
|
129
|
+
}
|
|
130
|
+
this.abortControllers.set(options.requestKey, controller);
|
|
131
|
+
}
|
|
132
|
+
if (options.timeout) {
|
|
133
|
+
const timeoutId = setTimeout(() => {
|
|
134
|
+
controller.abort(new Error("Request timeout"));
|
|
135
|
+
}, options.timeout);
|
|
136
|
+
controller.signal.addEventListener("abort", () => {
|
|
137
|
+
clearTimeout(timeoutId);
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
return controller.signal;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* 执行请求
|
|
144
|
+
*/
|
|
145
|
+
async request(options) {
|
|
146
|
+
const requestId = generateRequestId();
|
|
147
|
+
const mergedOptions = {
|
|
148
|
+
...this.defaults,
|
|
149
|
+
...options,
|
|
150
|
+
requestId
|
|
151
|
+
};
|
|
152
|
+
mergedOptions.fullPath = buildURL(mergedOptions);
|
|
153
|
+
mergedOptions.signal = this.createAbortSignal(mergedOptions);
|
|
154
|
+
let config = await this.interceptors.request.execute(mergedOptions);
|
|
155
|
+
if (config.requestInterceptor) {
|
|
156
|
+
config = await config.requestInterceptor(config);
|
|
157
|
+
}
|
|
158
|
+
const executeRequest = async () => {
|
|
159
|
+
try {
|
|
160
|
+
const fetchOptions = {
|
|
161
|
+
method: config.method || "GET",
|
|
162
|
+
headers: config.headers,
|
|
163
|
+
credentials: config.credentials,
|
|
164
|
+
signal: config.signal
|
|
165
|
+
};
|
|
166
|
+
if (config.data !== void 0 && config.method !== "GET") {
|
|
167
|
+
if (config.responseType === "formdata" || config.data instanceof FormData) {
|
|
168
|
+
fetchOptions.body = config.data;
|
|
169
|
+
} else {
|
|
170
|
+
fetchOptions.body = JSON.stringify(config.data);
|
|
171
|
+
if (!fetchOptions.headers) {
|
|
172
|
+
fetchOptions.headers = {};
|
|
173
|
+
}
|
|
174
|
+
const headers = fetchOptions.headers;
|
|
175
|
+
if (!headers["Content-Type"]) {
|
|
176
|
+
headers["Content-Type"] = "application/json";
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
let response;
|
|
181
|
+
try {
|
|
182
|
+
response = await fetch(config.fullPath, fetchOptions);
|
|
183
|
+
} catch (error) {
|
|
184
|
+
throw createRequestError(error, config);
|
|
185
|
+
}
|
|
186
|
+
if (!response.ok) {
|
|
187
|
+
const error = createRequestError(
|
|
188
|
+
`Request failed with status ${response.status}`,
|
|
189
|
+
config,
|
|
190
|
+
response
|
|
191
|
+
);
|
|
192
|
+
try {
|
|
193
|
+
await this.interceptors.response.execute({
|
|
194
|
+
data: void 0,
|
|
195
|
+
response,
|
|
196
|
+
config,
|
|
197
|
+
requestId
|
|
198
|
+
});
|
|
199
|
+
} catch {
|
|
200
|
+
}
|
|
201
|
+
throw error;
|
|
202
|
+
}
|
|
203
|
+
let data;
|
|
204
|
+
if (config.rawResponse) {
|
|
205
|
+
data = response;
|
|
206
|
+
} else {
|
|
207
|
+
switch (config.responseType) {
|
|
208
|
+
case "text":
|
|
209
|
+
data = await response.text();
|
|
210
|
+
break;
|
|
211
|
+
case "blob":
|
|
212
|
+
data = await response.blob();
|
|
213
|
+
break;
|
|
214
|
+
case "arraybuffer":
|
|
215
|
+
data = await response.arrayBuffer();
|
|
216
|
+
break;
|
|
217
|
+
case "formdata":
|
|
218
|
+
data = await response.formData();
|
|
219
|
+
break;
|
|
220
|
+
case "json":
|
|
221
|
+
default:
|
|
222
|
+
const text = await response.text();
|
|
223
|
+
data = text ? JSON.parse(text) : null;
|
|
224
|
+
break;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
const requestResponse = {
|
|
228
|
+
data,
|
|
229
|
+
response,
|
|
230
|
+
config,
|
|
231
|
+
requestId
|
|
232
|
+
};
|
|
233
|
+
const finalResponse = await this.interceptors.response.execute(
|
|
234
|
+
requestResponse
|
|
235
|
+
);
|
|
236
|
+
return finalResponse;
|
|
237
|
+
} catch (error) {
|
|
238
|
+
const requestError = error;
|
|
239
|
+
const currentRetryCount = config._retryCount ?? 0;
|
|
240
|
+
const maxRetries = config.retryTimes ?? 3;
|
|
241
|
+
const shouldRetry = config.retry && currentRetryCount < maxRetries && (!config.retryCondition || config.retryCondition(requestError));
|
|
242
|
+
if (shouldRetry) {
|
|
243
|
+
const baseDelay = config.retryDelay || 1e3;
|
|
244
|
+
const delayCalculator = config.retryDelayCalculator || defaultExponentialBackoff;
|
|
245
|
+
const delay = delayCalculator(currentRetryCount, baseDelay);
|
|
246
|
+
if (delay < 0) {
|
|
247
|
+
if (config.errorHandler) {
|
|
248
|
+
config.errorHandler(requestError);
|
|
249
|
+
}
|
|
250
|
+
throw requestError;
|
|
251
|
+
}
|
|
252
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
253
|
+
config._retryCount = currentRetryCount + 1;
|
|
254
|
+
return executeRequest();
|
|
255
|
+
}
|
|
256
|
+
if (config.errorHandler) {
|
|
257
|
+
config.errorHandler(requestError);
|
|
258
|
+
}
|
|
259
|
+
throw requestError;
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
return executeRequest();
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* 便捷方法
|
|
266
|
+
*/
|
|
267
|
+
get(url, options) {
|
|
268
|
+
return this.request({ ...options, url, method: "GET" });
|
|
269
|
+
}
|
|
270
|
+
post(url, data, options) {
|
|
271
|
+
return this.request({ ...options, url, method: "POST", data });
|
|
272
|
+
}
|
|
273
|
+
put(url, data, options) {
|
|
274
|
+
return this.request({ ...options, url, method: "PUT", data });
|
|
275
|
+
}
|
|
276
|
+
delete(url, options) {
|
|
277
|
+
return this.request({ ...options, url, method: "DELETE" });
|
|
278
|
+
}
|
|
279
|
+
patch(url, data, options) {
|
|
280
|
+
return this.request({ ...options, url, method: "PATCH", data });
|
|
281
|
+
}
|
|
282
|
+
head(url, options) {
|
|
283
|
+
return this.request({ ...options, url, method: "HEAD" });
|
|
284
|
+
}
|
|
285
|
+
options(url, options) {
|
|
286
|
+
return this.request({ ...options, url, method: "OPTIONS" });
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* 设置默认配置
|
|
290
|
+
*/
|
|
291
|
+
setConfig(config) {
|
|
292
|
+
this.defaults = { ...this.defaults, ...config };
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* 获取默认配置
|
|
296
|
+
*/
|
|
297
|
+
getConfig() {
|
|
298
|
+
return { ...this.defaults };
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* 取消所有请求
|
|
302
|
+
*/
|
|
303
|
+
cancelAll() {
|
|
304
|
+
this.abortControllers.forEach((controller) => {
|
|
305
|
+
controller.abort();
|
|
306
|
+
});
|
|
307
|
+
this.abortControllers.clear();
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* 取消指定 key 的请求
|
|
311
|
+
*/
|
|
312
|
+
cancel(key) {
|
|
313
|
+
const controller = this.abortControllers.get(key);
|
|
314
|
+
if (controller) {
|
|
315
|
+
controller.abort();
|
|
316
|
+
this.abortControllers.delete(key);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
export function defaultExponentialBackoff(retryCount, defaultDelay = 1e3) {
|
|
321
|
+
const baseDelay = defaultDelay * Math.pow(2, retryCount);
|
|
322
|
+
const jitter = Math.random() * 1e3;
|
|
323
|
+
return Math.min(baseDelay + jitter, 3e4);
|
|
324
|
+
}
|
|
325
|
+
export function linearBackoff(retryCount, defaultDelay) {
|
|
326
|
+
return defaultDelay * (retryCount + 1);
|
|
327
|
+
}
|
|
328
|
+
export function fixedBackoff(_retryCount, defaultDelay) {
|
|
329
|
+
return defaultDelay;
|
|
330
|
+
}
|
|
331
|
+
export function exponentialBackoffWithMaxRetries(retryCount, defaultDelay, maxRetries = 10) {
|
|
332
|
+
if (retryCount >= maxRetries) {
|
|
333
|
+
return -1;
|
|
334
|
+
}
|
|
335
|
+
return defaultExponentialBackoff(retryCount, defaultDelay);
|
|
336
|
+
}
|
|
337
|
+
export const request = new Request();
|
|
338
|
+
export const createRequest = (options) => new Request(options);
|
|
339
|
+
export default request;
|
package/dist/types.cjs
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.buildPath = buildPath;
|
|
7
|
+
function buildPath(path, params) {
|
|
8
|
+
let result = path;
|
|
9
|
+
for (const [key, value] of Object.entries(params)) {
|
|
10
|
+
result = result.replace(`:${key}`, encodeURIComponent(String(value)));
|
|
11
|
+
}
|
|
12
|
+
return result;
|
|
13
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @yh-ui/request 严格类型定义
|
|
3
|
+
* 杜绝 any,全链路类型安全
|
|
4
|
+
*/
|
|
5
|
+
/** HTTP 方法 */
|
|
6
|
+
export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS';
|
|
7
|
+
/** 响应数据类型 */
|
|
8
|
+
export type ResponseType = 'json' | 'text' | 'blob' | 'arraybuffer' | 'formdata';
|
|
9
|
+
/** 可序列化的查询参数值 */
|
|
10
|
+
export type ParamValue = string | number | boolean | string[] | number[];
|
|
11
|
+
/** 查询参数对象 */
|
|
12
|
+
export type ParamsRecord = Record<string, ParamValue>;
|
|
13
|
+
/** 请求体类型约束 */
|
|
14
|
+
export type RequestBody = Record<string, unknown> | FormData | Blob | ArrayBuffer | string | null;
|
|
15
|
+
/** 重试延迟计算器类型 */
|
|
16
|
+
export type RetryDelayCalculator = (retryCount: number, defaultDelay: number) => number;
|
|
17
|
+
/**
|
|
18
|
+
* 从 URL 路径中提取路径参数
|
|
19
|
+
* @example
|
|
20
|
+
* type Params = PathParams<'/api/users/:id'>
|
|
21
|
+
* // => { id: string }
|
|
22
|
+
*/
|
|
23
|
+
export type PathParams<T extends string> = T extends `${infer _}:${infer Param}/${infer Rest}` ? Param extends string ? {
|
|
24
|
+
[K in Param]: string;
|
|
25
|
+
} & PathParams<Rest> : never : T extends `${infer _}:${infer Param}` ? {
|
|
26
|
+
[K in Param]: string;
|
|
27
|
+
} : Record<string, never>;
|
|
28
|
+
/**
|
|
29
|
+
* 构建带路径参数的 URL
|
|
30
|
+
* @example
|
|
31
|
+
* const url = buildPath('/api/users/:id', { id: '123' })
|
|
32
|
+
* // => '/api/users/123'
|
|
33
|
+
*/
|
|
34
|
+
export declare function buildPath<T extends string>(path: T, params: PathParams<T>): string;
|
|
35
|
+
export interface RequestOptions<TRequest = unknown> {
|
|
36
|
+
/** 请求基础 URL */
|
|
37
|
+
baseURL?: string;
|
|
38
|
+
/** 请求路径 */
|
|
39
|
+
url?: string;
|
|
40
|
+
/** 请求方法 */
|
|
41
|
+
method?: HttpMethod;
|
|
42
|
+
/** URL 查询参数 */
|
|
43
|
+
params?: ParamsRecord;
|
|
44
|
+
/** 请求体数据 */
|
|
45
|
+
data?: TRequest;
|
|
46
|
+
/** 请求头 */
|
|
47
|
+
headers?: Record<string, string>;
|
|
48
|
+
/** 请求超时时间 (ms) */
|
|
49
|
+
timeout?: number;
|
|
50
|
+
/** credentials 模式 */
|
|
51
|
+
credentials?: RequestCredentials;
|
|
52
|
+
/** 响应类型 */
|
|
53
|
+
responseType?: ResponseType;
|
|
54
|
+
/** 是否 retry */
|
|
55
|
+
retry?: boolean;
|
|
56
|
+
/** 重试次数 */
|
|
57
|
+
retryTimes?: number;
|
|
58
|
+
/** 重试延迟 (ms) */
|
|
59
|
+
retryDelay?: number;
|
|
60
|
+
/** 重试条件函数 */
|
|
61
|
+
retryCondition?: (error: RequestError) => boolean;
|
|
62
|
+
/** 重试延迟计算器 */
|
|
63
|
+
retryDelayCalculator?: RetryDelayCalculator;
|
|
64
|
+
/** 请求拦截器 */
|
|
65
|
+
requestInterceptor?: (config: InternalRequestOptions) => InternalRequestOptions | Promise<InternalRequestOptions>;
|
|
66
|
+
/** 响应拦截器 */
|
|
67
|
+
responseInterceptor?: <T>(response: RequestResponse<T>) => RequestResponse<T> | Promise<RequestResponse<T>>;
|
|
68
|
+
/** 错误处理函数 */
|
|
69
|
+
errorHandler?: (error: RequestError) => void;
|
|
70
|
+
/** 是否返回原始响应 */
|
|
71
|
+
rawResponse?: boolean;
|
|
72
|
+
/** 是否自动序列化 params */
|
|
73
|
+
serializeParams?: boolean;
|
|
74
|
+
/** 是否 abort 上一个相同 key 的请求 */
|
|
75
|
+
abortSameKey?: boolean;
|
|
76
|
+
/** 请求唯一标识 key(用于去重) */
|
|
77
|
+
requestKey?: string;
|
|
78
|
+
/** 调试模式 */
|
|
79
|
+
debug?: boolean;
|
|
80
|
+
/** 回退数据(请求失败时返回) */
|
|
81
|
+
fallbackData?: unknown;
|
|
82
|
+
/** AbortSignal */
|
|
83
|
+
signal?: AbortSignal;
|
|
84
|
+
}
|
|
85
|
+
export interface InternalRequestOptions<TRequest = unknown> extends RequestOptions<TRequest> {
|
|
86
|
+
/** 完整的请求 URL */
|
|
87
|
+
fullPath?: string;
|
|
88
|
+
/** AbortSignal */
|
|
89
|
+
signal?: AbortSignal;
|
|
90
|
+
/** 请求 ID */
|
|
91
|
+
requestId?: string;
|
|
92
|
+
}
|
|
93
|
+
export interface RequestResponse<T = unknown> {
|
|
94
|
+
/** 响应数据 */
|
|
95
|
+
data: T;
|
|
96
|
+
/** 原始响应 */
|
|
97
|
+
response: Response;
|
|
98
|
+
/** 请求配置 */
|
|
99
|
+
config: InternalRequestOptions;
|
|
100
|
+
/** 请求 ID */
|
|
101
|
+
requestId: string;
|
|
102
|
+
/** 请求耗时 (ms) */
|
|
103
|
+
duration?: number;
|
|
104
|
+
}
|
|
105
|
+
export interface RequestError extends Error {
|
|
106
|
+
message: string;
|
|
107
|
+
name: string;
|
|
108
|
+
code?: string;
|
|
109
|
+
originalError?: Error;
|
|
110
|
+
config?: InternalRequestOptions;
|
|
111
|
+
response?: Response;
|
|
112
|
+
isTimeout?: boolean;
|
|
113
|
+
isNetworkError?: boolean;
|
|
114
|
+
isCanceled?: boolean;
|
|
115
|
+
requestId?: string;
|
|
116
|
+
}
|
|
117
|
+
export interface InterceptorManager<T> {
|
|
118
|
+
use(onFulfilled: (value: T) => T | Promise<T>, onRejected?: (error: RequestError) => T | Promise<T>): number;
|
|
119
|
+
eject(id: number): void;
|
|
120
|
+
clear(): void;
|
|
121
|
+
execute(value: T): Promise<T>;
|
|
122
|
+
}
|
|
123
|
+
/** HTTP 适配器接口 - 支持多运行时 */
|
|
124
|
+
export interface HttpAdapter {
|
|
125
|
+
/** 适配器名称 */
|
|
126
|
+
name: string;
|
|
127
|
+
/** 发送请求 */
|
|
128
|
+
request<T>(config: InternalRequestOptions): Promise<RequestResponse<T>>;
|
|
129
|
+
/** 是否支持当前环境 */
|
|
130
|
+
isSupported(): boolean;
|
|
131
|
+
}
|
|
132
|
+
export interface RequestInstance {
|
|
133
|
+
request<TResponse = unknown, TRequest = unknown>(options: RequestOptions<TRequest> & {
|
|
134
|
+
url: string;
|
|
135
|
+
}): Promise<RequestResponse<TResponse>>;
|
|
136
|
+
get<T = unknown>(url: string, options?: Omit<RequestOptions, 'method' | 'data'>): Promise<RequestResponse<T>>;
|
|
137
|
+
post<T = unknown, D = unknown>(url: string, data?: D, options?: Omit<RequestOptions<D>, 'method' | 'data'>): Promise<RequestResponse<T>>;
|
|
138
|
+
put<T = unknown, D = unknown>(url: string, data?: D, options?: Omit<RequestOptions<D>, 'method' | 'data'>): Promise<RequestResponse<T>>;
|
|
139
|
+
delete<T = unknown>(url: string, options?: Omit<RequestOptions, 'method' | 'data'>): Promise<RequestResponse<T>>;
|
|
140
|
+
patch<T = unknown, D = unknown>(url: string, data?: D, options?: Omit<RequestOptions<D>, 'method' | 'data'>): Promise<RequestResponse<T>>;
|
|
141
|
+
head<T = unknown>(url: string, options?: Omit<RequestOptions, 'method' | 'data'>): Promise<RequestResponse<T>>;
|
|
142
|
+
options<T = unknown>(url: string, options?: Omit<RequestOptions, 'method' | 'data'>): Promise<RequestResponse<T>>;
|
|
143
|
+
setConfig(config: Partial<RequestOptions>): void;
|
|
144
|
+
getConfig(): RequestOptions;
|
|
145
|
+
interceptors: {
|
|
146
|
+
request: InterceptorManager<InternalRequestOptions>;
|
|
147
|
+
response: InterceptorManager<RequestResponse<unknown>>;
|
|
148
|
+
};
|
|
149
|
+
cancelAll(): void;
|
|
150
|
+
cancel(key: string): void;
|
|
151
|
+
}
|
|
152
|
+
export interface CreateRequestOptions extends Partial<RequestOptions> {
|
|
153
|
+
defaultOptions?: Partial<RequestOptions>;
|
|
154
|
+
instanceId?: string;
|
|
155
|
+
/** 自定义适配器 */
|
|
156
|
+
adapter?: HttpAdapter;
|
|
157
|
+
}
|
package/dist/types.mjs
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.useAIStream = useAIStream;
|
|
7
|
+
var _vue = require("vue");
|
|
8
|
+
var _useSSE = require("./useSSE.cjs");
|
|
9
|
+
function useAIStream(options = {}) {
|
|
10
|
+
const {
|
|
11
|
+
parseAIMessage = true,
|
|
12
|
+
appendMode = true,
|
|
13
|
+
onText,
|
|
14
|
+
onThinking,
|
|
15
|
+
onToolCall,
|
|
16
|
+
onMessage: userOnMessage,
|
|
17
|
+
...sseOptions
|
|
18
|
+
} = options;
|
|
19
|
+
const text = (0, _vue.ref)("");
|
|
20
|
+
const thinking = (0, _vue.ref)("");
|
|
21
|
+
const toolCalls = (0, _vue.ref)([]);
|
|
22
|
+
const done = (0, _vue.ref)(false);
|
|
23
|
+
const handleMessage = message => {
|
|
24
|
+
if (!parseAIMessage) return;
|
|
25
|
+
const {
|
|
26
|
+
event,
|
|
27
|
+
data
|
|
28
|
+
} = message;
|
|
29
|
+
switch (event) {
|
|
30
|
+
case "chunk":
|
|
31
|
+
case "message":
|
|
32
|
+
if (typeof data === "string") {
|
|
33
|
+
const delta = data;
|
|
34
|
+
text.value += delta;
|
|
35
|
+
onText?.(text.value, delta);
|
|
36
|
+
} else if (data !== null && typeof data === "object") {
|
|
37
|
+
const aiData = data;
|
|
38
|
+
const delta = aiData.content || aiData.text || aiData.delta || "";
|
|
39
|
+
if (delta) {
|
|
40
|
+
if (appendMode) {
|
|
41
|
+
text.value += delta;
|
|
42
|
+
} else {
|
|
43
|
+
text.value = delta;
|
|
44
|
+
}
|
|
45
|
+
onText?.(text.value, delta);
|
|
46
|
+
}
|
|
47
|
+
if (aiData.thinking) {
|
|
48
|
+
thinking.value = aiData.thinking;
|
|
49
|
+
onThinking?.(aiData.thinking);
|
|
50
|
+
}
|
|
51
|
+
if (aiData.tool_calls) {
|
|
52
|
+
toolCalls.value = aiData.tool_calls;
|
|
53
|
+
onToolCall?.(aiData.tool_calls);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
break;
|
|
57
|
+
case "thinking":
|
|
58
|
+
if (typeof data === "string") {
|
|
59
|
+
thinking.value = data;
|
|
60
|
+
onThinking?.(data);
|
|
61
|
+
} else if (data !== null && typeof data === "object") {
|
|
62
|
+
const aiData = data;
|
|
63
|
+
const thinkContent = aiData.thinking || aiData.content || "";
|
|
64
|
+
thinking.value = thinkContent;
|
|
65
|
+
onThinking?.(thinkContent);
|
|
66
|
+
}
|
|
67
|
+
break;
|
|
68
|
+
case "tool":
|
|
69
|
+
if (data !== null && typeof data === "object") {
|
|
70
|
+
const aiData = data;
|
|
71
|
+
if (aiData.tool_calls) {
|
|
72
|
+
toolCalls.value = aiData.tool_calls;
|
|
73
|
+
onToolCall?.(aiData.tool_calls);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
break;
|
|
77
|
+
case "done":
|
|
78
|
+
done.value = true;
|
|
79
|
+
break;
|
|
80
|
+
case "error":
|
|
81
|
+
done.value = true;
|
|
82
|
+
break;
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
const wrappedOnMessage = msg => {
|
|
86
|
+
handleMessage(msg);
|
|
87
|
+
userOnMessage?.(msg);
|
|
88
|
+
};
|
|
89
|
+
const {
|
|
90
|
+
loading,
|
|
91
|
+
content,
|
|
92
|
+
messages,
|
|
93
|
+
error,
|
|
94
|
+
start,
|
|
95
|
+
stop,
|
|
96
|
+
reset
|
|
97
|
+
} = (0, _useSSE.useSSE)({
|
|
98
|
+
...sseOptions,
|
|
99
|
+
onMessage: wrappedOnMessage
|
|
100
|
+
});
|
|
101
|
+
const aiReset = () => {
|
|
102
|
+
text.value = "";
|
|
103
|
+
thinking.value = "";
|
|
104
|
+
toolCalls.value = [];
|
|
105
|
+
done.value = false;
|
|
106
|
+
reset();
|
|
107
|
+
};
|
|
108
|
+
const aiStart = requestOptions => {
|
|
109
|
+
aiReset();
|
|
110
|
+
start(requestOptions);
|
|
111
|
+
};
|
|
112
|
+
return {
|
|
113
|
+
loading,
|
|
114
|
+
content,
|
|
115
|
+
text,
|
|
116
|
+
thinking,
|
|
117
|
+
toolCalls,
|
|
118
|
+
done,
|
|
119
|
+
messages,
|
|
120
|
+
error,
|
|
121
|
+
start: aiStart,
|
|
122
|
+
stop,
|
|
123
|
+
reset: aiReset
|
|
124
|
+
};
|
|
125
|
+
}
|