@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
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ref,
|
|
3
|
+
shallowRef,
|
|
4
|
+
computed,
|
|
5
|
+
watch,
|
|
6
|
+
onMounted,
|
|
7
|
+
onUnmounted
|
|
8
|
+
} from "vue";
|
|
9
|
+
import { debounce, throttle } from "@yh-ui/utils";
|
|
10
|
+
const globalCache = /* @__PURE__ */ new Map();
|
|
11
|
+
function setCache(key, value, cacheTime) {
|
|
12
|
+
const expireTime = Date.now() + (cacheTime || 5 * 60 * 1e3);
|
|
13
|
+
globalCache.set(key, { data: value, expireTime });
|
|
14
|
+
}
|
|
15
|
+
export function useRequest(service, options = {}) {
|
|
16
|
+
const {
|
|
17
|
+
manual = false,
|
|
18
|
+
defaultParams = [],
|
|
19
|
+
debounceWait,
|
|
20
|
+
throttleWait,
|
|
21
|
+
formatResult,
|
|
22
|
+
onSuccess,
|
|
23
|
+
onError,
|
|
24
|
+
onFinally
|
|
25
|
+
} = options;
|
|
26
|
+
const loading = ref(false);
|
|
27
|
+
const loadingMore = ref(false);
|
|
28
|
+
const data = shallowRef(void 0);
|
|
29
|
+
const error = shallowRef(void 0);
|
|
30
|
+
const params = ref(defaultParams);
|
|
31
|
+
const noMore = ref(false);
|
|
32
|
+
const runCount = ref(0);
|
|
33
|
+
let abortController = null;
|
|
34
|
+
const format = (response) => {
|
|
35
|
+
if (formatResult) {
|
|
36
|
+
return formatResult(response);
|
|
37
|
+
}
|
|
38
|
+
if (response && typeof response === "object" && !("config" in response && "requestId" in response)) {
|
|
39
|
+
return response;
|
|
40
|
+
}
|
|
41
|
+
const res = response;
|
|
42
|
+
return res?.data ?? res;
|
|
43
|
+
};
|
|
44
|
+
const run = async (...runParams) => {
|
|
45
|
+
const currentRunCount = ++runCount.value;
|
|
46
|
+
if (abortController) {
|
|
47
|
+
abortController.abort();
|
|
48
|
+
}
|
|
49
|
+
abortController = new AbortController();
|
|
50
|
+
params.value = runParams;
|
|
51
|
+
loading.value = true;
|
|
52
|
+
error.value = void 0;
|
|
53
|
+
try {
|
|
54
|
+
const response = await service(...runParams);
|
|
55
|
+
if (currentRunCount !== runCount.value) {
|
|
56
|
+
const cancelError = new Error("Request canceled");
|
|
57
|
+
cancelError.isCanceled = true;
|
|
58
|
+
throw cancelError;
|
|
59
|
+
}
|
|
60
|
+
const formattedData = format(response);
|
|
61
|
+
data.value = formattedData;
|
|
62
|
+
if (onSuccess) {
|
|
63
|
+
onSuccess(formattedData, runParams);
|
|
64
|
+
}
|
|
65
|
+
return response;
|
|
66
|
+
} catch (err) {
|
|
67
|
+
const cancelableErr = err;
|
|
68
|
+
if (cancelableErr?.isCanceled || cancelableErr?.name === "AbortError") {
|
|
69
|
+
throw err;
|
|
70
|
+
}
|
|
71
|
+
error.value = err;
|
|
72
|
+
if (onError) {
|
|
73
|
+
onError(err, runParams);
|
|
74
|
+
}
|
|
75
|
+
throw err;
|
|
76
|
+
} finally {
|
|
77
|
+
if (currentRunCount === runCount.value) {
|
|
78
|
+
loading.value = false;
|
|
79
|
+
if (onFinally) {
|
|
80
|
+
onFinally(runParams);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
const mutate = (newData) => {
|
|
86
|
+
if (typeof newData === "function") {
|
|
87
|
+
const oldData = data.value;
|
|
88
|
+
data.value = newData(oldData);
|
|
89
|
+
} else if (newData !== void 0) {
|
|
90
|
+
data.value = newData;
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
const cancel = () => {
|
|
94
|
+
if (abortController) {
|
|
95
|
+
abortController.abort();
|
|
96
|
+
abortController = null;
|
|
97
|
+
}
|
|
98
|
+
loading.value = false;
|
|
99
|
+
};
|
|
100
|
+
const refresh = async () => {
|
|
101
|
+
await run(...params.value).catch(() => {
|
|
102
|
+
});
|
|
103
|
+
};
|
|
104
|
+
const loadMore = async () => {
|
|
105
|
+
if (loadingMore.value || noMore.value) return;
|
|
106
|
+
loadingMore.value = true;
|
|
107
|
+
try {
|
|
108
|
+
await run(...params.value).catch(() => {
|
|
109
|
+
});
|
|
110
|
+
} finally {
|
|
111
|
+
loadingMore.value = false;
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
let wrappedRun = run;
|
|
115
|
+
let cancelFn;
|
|
116
|
+
if (debounceWait) {
|
|
117
|
+
const debounced = debounce(
|
|
118
|
+
((...args) => {
|
|
119
|
+
run(...args).catch(() => {
|
|
120
|
+
});
|
|
121
|
+
}),
|
|
122
|
+
debounceWait
|
|
123
|
+
);
|
|
124
|
+
wrappedRun = ((...args) => {
|
|
125
|
+
debounced(...args);
|
|
126
|
+
return Promise.resolve();
|
|
127
|
+
});
|
|
128
|
+
wrappedRun.cancel = () => debounced.cancel();
|
|
129
|
+
cancelFn = () => debounced.cancel();
|
|
130
|
+
} else if (throttleWait) {
|
|
131
|
+
const throttled = throttle(
|
|
132
|
+
((...args) => {
|
|
133
|
+
run(...args).catch(() => {
|
|
134
|
+
});
|
|
135
|
+
}),
|
|
136
|
+
throttleWait
|
|
137
|
+
);
|
|
138
|
+
wrappedRun = ((...args) => {
|
|
139
|
+
throttled(...args);
|
|
140
|
+
return Promise.resolve();
|
|
141
|
+
});
|
|
142
|
+
wrappedRun.cancel = () => throttled.cancel();
|
|
143
|
+
cancelFn = () => throttled.cancel();
|
|
144
|
+
}
|
|
145
|
+
onUnmounted(() => {
|
|
146
|
+
cancelFn?.();
|
|
147
|
+
});
|
|
148
|
+
if (!manual && defaultParams.length > 0) {
|
|
149
|
+
onMounted(() => {
|
|
150
|
+
if (!debounceWait && !throttleWait) {
|
|
151
|
+
run(...defaultParams).catch(() => {
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
const disabled = computed(() => loading.value);
|
|
157
|
+
return {
|
|
158
|
+
loading,
|
|
159
|
+
loadingMore,
|
|
160
|
+
data,
|
|
161
|
+
error,
|
|
162
|
+
params,
|
|
163
|
+
noMore,
|
|
164
|
+
run: wrappedRun,
|
|
165
|
+
mutate,
|
|
166
|
+
cancel,
|
|
167
|
+
refresh,
|
|
168
|
+
loadMore,
|
|
169
|
+
disabled
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
export function useRequestSWR(cacheKey, service, options = {}) {
|
|
173
|
+
const {
|
|
174
|
+
cacheTime = 10 * 60 * 1e3,
|
|
175
|
+
setCache: customSetCache,
|
|
176
|
+
refreshOnWindowFocus = false,
|
|
177
|
+
refreshDepsWait = 1e3,
|
|
178
|
+
refreshDeps = [],
|
|
179
|
+
manual = false,
|
|
180
|
+
...requestOptions
|
|
181
|
+
} = options;
|
|
182
|
+
const setCacheFn = customSetCache || ((key, value) => setCache(key, value, cacheTime));
|
|
183
|
+
const getKey = () => {
|
|
184
|
+
const key = typeof cacheKey === "function" ? cacheKey() : cacheKey;
|
|
185
|
+
return key;
|
|
186
|
+
};
|
|
187
|
+
const { loading, data, error, params, run, mutate, cancel, refresh, disabled } = useRequest((key) => service(key), {
|
|
188
|
+
manual,
|
|
189
|
+
defaultParams: manual ? [] : [getKey()],
|
|
190
|
+
...requestOptions
|
|
191
|
+
});
|
|
192
|
+
const updateCache = (newData) => {
|
|
193
|
+
const key = getKey();
|
|
194
|
+
if (key) {
|
|
195
|
+
setCacheFn(key, newData);
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
watch(
|
|
199
|
+
() => data.value,
|
|
200
|
+
(newData) => {
|
|
201
|
+
if (newData !== void 0) {
|
|
202
|
+
updateCache(newData);
|
|
203
|
+
}
|
|
204
|
+
},
|
|
205
|
+
{ immediate: true }
|
|
206
|
+
);
|
|
207
|
+
if (refreshOnWindowFocus && !manual) {
|
|
208
|
+
onMounted(() => {
|
|
209
|
+
const handleFocus = () => {
|
|
210
|
+
const key = getKey();
|
|
211
|
+
if (key && !loading.value) {
|
|
212
|
+
refresh();
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
window.addEventListener("visibilitychange", handleFocus);
|
|
216
|
+
window.addEventListener("focus", handleFocus);
|
|
217
|
+
onUnmounted(() => {
|
|
218
|
+
window.removeEventListener("visibilitychange", handleFocus);
|
|
219
|
+
window.removeEventListener("focus", handleFocus);
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
if (refreshDeps && refreshDeps.length > 0 && !manual) {
|
|
224
|
+
watch(
|
|
225
|
+
() => refreshDeps.map((dep) => dep.value),
|
|
226
|
+
() => {
|
|
227
|
+
const key = getKey();
|
|
228
|
+
if (key) {
|
|
229
|
+
setTimeout(() => run(key).catch(() => {
|
|
230
|
+
}), refreshDepsWait);
|
|
231
|
+
}
|
|
232
|
+
},
|
|
233
|
+
{ deep: true }
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
return {
|
|
237
|
+
loading,
|
|
238
|
+
data,
|
|
239
|
+
error,
|
|
240
|
+
params,
|
|
241
|
+
loadingMore: ref(false),
|
|
242
|
+
noMore: ref(false),
|
|
243
|
+
run,
|
|
244
|
+
mutate,
|
|
245
|
+
cancel,
|
|
246
|
+
refresh,
|
|
247
|
+
loadMore: async () => {
|
|
248
|
+
return Promise.resolve();
|
|
249
|
+
},
|
|
250
|
+
disabled
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
export function useRequestPolling(service, options = {}) {
|
|
254
|
+
const {
|
|
255
|
+
polling = false,
|
|
256
|
+
pollingInterval = 3e3,
|
|
257
|
+
pollingWhenHidden = false,
|
|
258
|
+
...requestOptions
|
|
259
|
+
} = options;
|
|
260
|
+
const {
|
|
261
|
+
loading,
|
|
262
|
+
data,
|
|
263
|
+
error,
|
|
264
|
+
params,
|
|
265
|
+
loadingMore,
|
|
266
|
+
noMore,
|
|
267
|
+
run,
|
|
268
|
+
mutate,
|
|
269
|
+
cancel,
|
|
270
|
+
refresh,
|
|
271
|
+
loadMore,
|
|
272
|
+
disabled
|
|
273
|
+
} = useRequest(service, {
|
|
274
|
+
manual: true,
|
|
275
|
+
...requestOptions
|
|
276
|
+
});
|
|
277
|
+
const isPolling = ref(false);
|
|
278
|
+
const pollingTimer = ref(null);
|
|
279
|
+
const startPolling = () => {
|
|
280
|
+
if (pollingTimer.value) return;
|
|
281
|
+
isPolling.value = true;
|
|
282
|
+
run(...params.value).catch(() => {
|
|
283
|
+
});
|
|
284
|
+
pollingTimer.value = setInterval(() => {
|
|
285
|
+
if (!pollingWhenHidden && document.hidden) {
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
run(...params.value).catch(() => {
|
|
289
|
+
});
|
|
290
|
+
}, pollingInterval);
|
|
291
|
+
};
|
|
292
|
+
const pause = () => {
|
|
293
|
+
if (pollingTimer.value) {
|
|
294
|
+
clearInterval(pollingTimer.value);
|
|
295
|
+
pollingTimer.value = null;
|
|
296
|
+
}
|
|
297
|
+
isPolling.value = false;
|
|
298
|
+
};
|
|
299
|
+
const resume = () => {
|
|
300
|
+
if (polling && !pollingTimer.value) {
|
|
301
|
+
startPolling();
|
|
302
|
+
}
|
|
303
|
+
};
|
|
304
|
+
onUnmounted(() => {
|
|
305
|
+
pause();
|
|
306
|
+
cancel();
|
|
307
|
+
});
|
|
308
|
+
if (polling) {
|
|
309
|
+
onMounted(() => {
|
|
310
|
+
startPolling();
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
return {
|
|
314
|
+
loading,
|
|
315
|
+
data,
|
|
316
|
+
error,
|
|
317
|
+
params,
|
|
318
|
+
loadingMore,
|
|
319
|
+
noMore,
|
|
320
|
+
run,
|
|
321
|
+
mutate,
|
|
322
|
+
cancel,
|
|
323
|
+
refresh,
|
|
324
|
+
loadMore,
|
|
325
|
+
disabled,
|
|
326
|
+
pause,
|
|
327
|
+
resume
|
|
328
|
+
};
|
|
329
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.useRequestQueue = useRequestQueue;
|
|
7
|
+
var _useQueue = require("./useQueue.cjs");
|
|
8
|
+
function useRequestQueue(options = {}) {
|
|
9
|
+
const {
|
|
10
|
+
request: _customRequest,
|
|
11
|
+
...queueOptions
|
|
12
|
+
} = options;
|
|
13
|
+
const queue = (0, _useQueue.useQueue)(queueOptions);
|
|
14
|
+
const addRequest = (requestFn, addOptions = {}) => {
|
|
15
|
+
return queue.add(requestFn, {
|
|
16
|
+
...addOptions,
|
|
17
|
+
metadata: {
|
|
18
|
+
...addOptions.metadata,
|
|
19
|
+
type: "request"
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
};
|
|
23
|
+
const cancelByKey = key => {
|
|
24
|
+
const pending = queue.pendingTasks.value ?? [];
|
|
25
|
+
const running = queue.runningTasks.value ?? [];
|
|
26
|
+
const task = [...pending, ...running].find(t => t.key === key);
|
|
27
|
+
if (task) {
|
|
28
|
+
queue.cancel(task.id);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
return {
|
|
32
|
+
...queue,
|
|
33
|
+
addRequest,
|
|
34
|
+
cancelByKey
|
|
35
|
+
};
|
|
36
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { useQueue, type UseQueueOptions, type QueueTask } from './useQueue';
|
|
2
|
+
/**
|
|
3
|
+
* useRequestQueue - 请求队列 Hook
|
|
4
|
+
*
|
|
5
|
+
* 专用于 HTTP 请求的队列管理,支持并发限制、优先级
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* const { addRequest, cancelByKey } = useRequestQueue({
|
|
9
|
+
* concurrency: 2,
|
|
10
|
+
* })
|
|
11
|
+
*
|
|
12
|
+
* addRequest(() => request.get('/api/item/1'), { key: 'item-1' })
|
|
13
|
+
*/
|
|
14
|
+
/** 请求队列配置 */
|
|
15
|
+
export interface UseRequestQueueOptions extends UseQueueOptions {
|
|
16
|
+
/** 请求实例 */
|
|
17
|
+
request?: unknown;
|
|
18
|
+
}
|
|
19
|
+
/** 请求队列返回 */
|
|
20
|
+
export interface UseRequestQueueReturn {
|
|
21
|
+
tasks: ReturnType<typeof useQueue>['tasks'];
|
|
22
|
+
pendingTasks: ReturnType<typeof useQueue>['pendingTasks'];
|
|
23
|
+
runningTasks: ReturnType<typeof useQueue>['runningTasks'];
|
|
24
|
+
completedTasks: ReturnType<typeof useQueue>['completedTasks'];
|
|
25
|
+
failedTasks: ReturnType<typeof useQueue>['failedTasks'];
|
|
26
|
+
isRunning: ReturnType<typeof useQueue>['isRunning'];
|
|
27
|
+
isEmpty: ReturnType<typeof useQueue>['isEmpty'];
|
|
28
|
+
isAllComplete: ReturnType<typeof useQueue>['isAllComplete'];
|
|
29
|
+
completedCount: ReturnType<typeof useQueue>['completedCount'];
|
|
30
|
+
totalCount: ReturnType<typeof useQueue>['totalCount'];
|
|
31
|
+
addRequest: <T = unknown>(requestFn: () => Promise<T>, options?: {
|
|
32
|
+
key?: string;
|
|
33
|
+
priority?: number;
|
|
34
|
+
delay?: number;
|
|
35
|
+
metadata?: Record<string, unknown>;
|
|
36
|
+
}) => string;
|
|
37
|
+
cancelByKey: (key: string) => void;
|
|
38
|
+
remove: (taskId: string) => void;
|
|
39
|
+
clear: () => void;
|
|
40
|
+
start: () => void;
|
|
41
|
+
pause: () => void;
|
|
42
|
+
resume: () => void;
|
|
43
|
+
cancel: (taskId: string) => void;
|
|
44
|
+
cancelAll: () => void;
|
|
45
|
+
retry: (taskId: string) => void;
|
|
46
|
+
retryAll: () => void;
|
|
47
|
+
getTask: (taskId: string) => QueueTask | undefined;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* useRequestQueue - HTTP 请求队列 Hook
|
|
51
|
+
*/
|
|
52
|
+
export declare function useRequestQueue(options?: UseRequestQueueOptions): UseRequestQueueReturn;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { useQueue } from "./useQueue.mjs";
|
|
2
|
+
export function useRequestQueue(options = {}) {
|
|
3
|
+
const { request: _customRequest, ...queueOptions } = options;
|
|
4
|
+
const queue = useQueue(queueOptions);
|
|
5
|
+
const addRequest = (requestFn, addOptions = {}) => {
|
|
6
|
+
return queue.add(requestFn, {
|
|
7
|
+
...addOptions,
|
|
8
|
+
metadata: {
|
|
9
|
+
...addOptions.metadata,
|
|
10
|
+
type: "request"
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
};
|
|
14
|
+
const cancelByKey = (key) => {
|
|
15
|
+
const pending = queue.pendingTasks.value ?? [];
|
|
16
|
+
const running = queue.runningTasks.value ?? [];
|
|
17
|
+
const task = [...pending, ...running].find((t) => t.key === key);
|
|
18
|
+
if (task) {
|
|
19
|
+
queue.cancel(task.id);
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
return {
|
|
23
|
+
...queue,
|
|
24
|
+
addRequest,
|
|
25
|
+
cancelByKey
|
|
26
|
+
};
|
|
27
|
+
}
|
package/dist/useSSE.cjs
ADDED
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.useSSE = useSSE;
|
|
7
|
+
var _vue = require("vue");
|
|
8
|
+
function useSSE(options = {}) {
|
|
9
|
+
const {
|
|
10
|
+
parseJSON = true,
|
|
11
|
+
separator = "\n\n",
|
|
12
|
+
eventPrefix = "event:",
|
|
13
|
+
onStart,
|
|
14
|
+
onMessage,
|
|
15
|
+
onDone,
|
|
16
|
+
onError,
|
|
17
|
+
onCustomEvent,
|
|
18
|
+
...fetchOptions
|
|
19
|
+
} = options;
|
|
20
|
+
const loading = (0, _vue.ref)(false);
|
|
21
|
+
const content = (0, _vue.ref)("");
|
|
22
|
+
const messages = (0, _vue.ref)([]);
|
|
23
|
+
const error = (0, _vue.shallowRef)(void 0);
|
|
24
|
+
let abortController = null;
|
|
25
|
+
let reader = null;
|
|
26
|
+
let decoder;
|
|
27
|
+
const getDecoder = () => {
|
|
28
|
+
if (!decoder) {
|
|
29
|
+
decoder = new TextDecoder();
|
|
30
|
+
}
|
|
31
|
+
return decoder;
|
|
32
|
+
};
|
|
33
|
+
const parseLine = line => {
|
|
34
|
+
const result = {
|
|
35
|
+
data: ""
|
|
36
|
+
};
|
|
37
|
+
if (!line || line.startsWith("#")) return result;
|
|
38
|
+
if (line.startsWith(eventPrefix)) {
|
|
39
|
+
result.event = line.slice(eventPrefix.length).trim();
|
|
40
|
+
return result;
|
|
41
|
+
}
|
|
42
|
+
if (line.startsWith("data:")) {
|
|
43
|
+
const data = line.slice(5);
|
|
44
|
+
result.data = data.startsWith(" ") ? data.slice(1) : data;
|
|
45
|
+
}
|
|
46
|
+
return result;
|
|
47
|
+
};
|
|
48
|
+
const parseSSE = rawData => {
|
|
49
|
+
const lines = rawData.split("\n");
|
|
50
|
+
let originalEventType = "message";
|
|
51
|
+
let eventData = "";
|
|
52
|
+
let done = false;
|
|
53
|
+
for (const line of lines) {
|
|
54
|
+
const {
|
|
55
|
+
event,
|
|
56
|
+
data
|
|
57
|
+
} = parseLine(line);
|
|
58
|
+
if (event) originalEventType = event;
|
|
59
|
+
if (data) eventData += data;
|
|
60
|
+
}
|
|
61
|
+
let eventType = "message";
|
|
62
|
+
switch (originalEventType.toLowerCase()) {
|
|
63
|
+
case "start":
|
|
64
|
+
eventType = "start";
|
|
65
|
+
break;
|
|
66
|
+
case "message":
|
|
67
|
+
case "chunk":
|
|
68
|
+
case "content":
|
|
69
|
+
eventType = "chunk";
|
|
70
|
+
break;
|
|
71
|
+
case "done":
|
|
72
|
+
case "stop":
|
|
73
|
+
eventType = "done";
|
|
74
|
+
done = true;
|
|
75
|
+
break;
|
|
76
|
+
case "error":
|
|
77
|
+
eventType = "error";
|
|
78
|
+
break;
|
|
79
|
+
case "tool":
|
|
80
|
+
case "function":
|
|
81
|
+
eventType = "tool";
|
|
82
|
+
break;
|
|
83
|
+
case "thinking":
|
|
84
|
+
case "thinking_start":
|
|
85
|
+
case "thinking_end":
|
|
86
|
+
eventType = "thinking";
|
|
87
|
+
break;
|
|
88
|
+
default:
|
|
89
|
+
eventType = "custom";
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
if (!eventData && eventType !== "done" && eventType !== "start") return null;
|
|
93
|
+
let finalData = eventData;
|
|
94
|
+
if (parseJSON && eventData) {
|
|
95
|
+
try {
|
|
96
|
+
finalData = JSON.parse(eventData);
|
|
97
|
+
} catch {}
|
|
98
|
+
}
|
|
99
|
+
if (eventType === "custom" && onCustomEvent) {
|
|
100
|
+
onCustomEvent(originalEventType, finalData);
|
|
101
|
+
}
|
|
102
|
+
return {
|
|
103
|
+
event: eventType,
|
|
104
|
+
data: finalData,
|
|
105
|
+
raw: rawData,
|
|
106
|
+
done
|
|
107
|
+
};
|
|
108
|
+
};
|
|
109
|
+
const start = async requestOptions => {
|
|
110
|
+
stop();
|
|
111
|
+
reset();
|
|
112
|
+
const config = {
|
|
113
|
+
...fetchOptions,
|
|
114
|
+
...requestOptions
|
|
115
|
+
};
|
|
116
|
+
loading.value = true;
|
|
117
|
+
error.value = void 0;
|
|
118
|
+
onStart?.();
|
|
119
|
+
try {
|
|
120
|
+
let url = config.url || "";
|
|
121
|
+
if (config.baseURL) {
|
|
122
|
+
url = url.startsWith("http") ? url : config.baseURL + url;
|
|
123
|
+
}
|
|
124
|
+
if (config.params) {
|
|
125
|
+
const params = new URLSearchParams();
|
|
126
|
+
Object.entries(config.params).forEach(([key, value]) => {
|
|
127
|
+
if (Array.isArray(value)) {
|
|
128
|
+
value.forEach(v => params.append(key, String(v)));
|
|
129
|
+
} else {
|
|
130
|
+
params.append(key, String(value));
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
const sep = url.includes("?") ? "&" : "?";
|
|
134
|
+
url += sep + params.toString();
|
|
135
|
+
}
|
|
136
|
+
const headers = config.headers || {};
|
|
137
|
+
const fetchInit = {
|
|
138
|
+
method: config.method || "POST",
|
|
139
|
+
headers,
|
|
140
|
+
credentials: config.credentials || "same-origin"
|
|
141
|
+
};
|
|
142
|
+
if (config.data && config.method !== "GET") {
|
|
143
|
+
fetchInit.body = JSON.stringify(config.data);
|
|
144
|
+
if (!headers["Content-Type"]) {
|
|
145
|
+
headers["Content-Type"] = "application/json";
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
abortController = new AbortController();
|
|
149
|
+
fetchInit.signal = abortController.signal;
|
|
150
|
+
const response = await fetch(url, fetchInit);
|
|
151
|
+
if (!response.ok) {
|
|
152
|
+
throw new Error(`SSE request failed: ${response.status} ${response.statusText}`);
|
|
153
|
+
}
|
|
154
|
+
const body = response.body;
|
|
155
|
+
if (!body) {
|
|
156
|
+
throw new Error("Response body is null");
|
|
157
|
+
}
|
|
158
|
+
reader = body.getReader();
|
|
159
|
+
const textDecoder = getDecoder();
|
|
160
|
+
let buffer = "";
|
|
161
|
+
while (true) {
|
|
162
|
+
const {
|
|
163
|
+
done: readDone,
|
|
164
|
+
value
|
|
165
|
+
} = await reader.read();
|
|
166
|
+
if (readDone) {
|
|
167
|
+
if (buffer) {
|
|
168
|
+
const msg = parseSSE(buffer);
|
|
169
|
+
if (msg) {
|
|
170
|
+
handleMessage(msg);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
break;
|
|
174
|
+
}
|
|
175
|
+
const chunk = textDecoder.decode(value, {
|
|
176
|
+
stream: true
|
|
177
|
+
});
|
|
178
|
+
buffer += chunk;
|
|
179
|
+
const parts = buffer.split(separator);
|
|
180
|
+
buffer = parts.pop() || "";
|
|
181
|
+
for (const part of parts) {
|
|
182
|
+
const msg = parseSSE(part);
|
|
183
|
+
if (msg) {
|
|
184
|
+
handleMessage(msg);
|
|
185
|
+
if (msg.done) {
|
|
186
|
+
onDone?.(content.value);
|
|
187
|
+
loading.value = false;
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
onDone?.(content.value);
|
|
194
|
+
} catch (err) {
|
|
195
|
+
const errWithName = err;
|
|
196
|
+
if (errWithName?.name === "AbortError") {
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
const errObj = err instanceof Error ? err : new Error(String(err));
|
|
200
|
+
error.value = errObj;
|
|
201
|
+
onError?.(errObj);
|
|
202
|
+
} finally {
|
|
203
|
+
loading.value = false;
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
const handleMessage = message => {
|
|
207
|
+
if (typeof message.data === "string") {
|
|
208
|
+
content.value += message.data;
|
|
209
|
+
}
|
|
210
|
+
messages.value.push(message);
|
|
211
|
+
onMessage?.(message);
|
|
212
|
+
};
|
|
213
|
+
const stop = () => {
|
|
214
|
+
if (reader) {
|
|
215
|
+
reader.cancel();
|
|
216
|
+
reader = null;
|
|
217
|
+
}
|
|
218
|
+
if (abortController) {
|
|
219
|
+
abortController.abort();
|
|
220
|
+
abortController = null;
|
|
221
|
+
}
|
|
222
|
+
loading.value = false;
|
|
223
|
+
};
|
|
224
|
+
const reset = () => {
|
|
225
|
+
content.value = "";
|
|
226
|
+
messages.value = [];
|
|
227
|
+
error.value = void 0;
|
|
228
|
+
};
|
|
229
|
+
(0, _vue.onUnmounted)(() => {
|
|
230
|
+
stop();
|
|
231
|
+
});
|
|
232
|
+
return {
|
|
233
|
+
loading,
|
|
234
|
+
content,
|
|
235
|
+
messages,
|
|
236
|
+
error,
|
|
237
|
+
start,
|
|
238
|
+
stop,
|
|
239
|
+
reset
|
|
240
|
+
};
|
|
241
|
+
}
|