@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,130 @@
|
|
|
1
|
+
export function createDebugInterceptor(options = {}) {
|
|
2
|
+
const {
|
|
3
|
+
enabled = false,
|
|
4
|
+
level = "log",
|
|
5
|
+
logRequestBody = true,
|
|
6
|
+
logResponseBody = true,
|
|
7
|
+
sanitize,
|
|
8
|
+
logger
|
|
9
|
+
} = options;
|
|
10
|
+
const loggers = {
|
|
11
|
+
log: console.log,
|
|
12
|
+
warn: console.warn,
|
|
13
|
+
error: console.error
|
|
14
|
+
};
|
|
15
|
+
const log = logger || loggers[level];
|
|
16
|
+
const createDebugInfo = (config, response, error) => {
|
|
17
|
+
const info = {
|
|
18
|
+
requestId: config.requestId || "",
|
|
19
|
+
url: config.fullPath || config.url || "",
|
|
20
|
+
method: config.method || "GET",
|
|
21
|
+
timestamp: Date.now()
|
|
22
|
+
};
|
|
23
|
+
if (logRequestBody) {
|
|
24
|
+
info.headers = config.headers;
|
|
25
|
+
info.data = config.data;
|
|
26
|
+
}
|
|
27
|
+
if (response) {
|
|
28
|
+
info.status = response.response.status;
|
|
29
|
+
info.duration = response.duration;
|
|
30
|
+
if (logResponseBody) {
|
|
31
|
+
info.response = response.data;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
if (error) {
|
|
35
|
+
info.error = error.message;
|
|
36
|
+
info.status = error.response?.status;
|
|
37
|
+
}
|
|
38
|
+
return info;
|
|
39
|
+
};
|
|
40
|
+
const printDebug = (info) => {
|
|
41
|
+
if (!enabled) return;
|
|
42
|
+
const sanitizedInfo = sanitize ? sanitize(info) : info;
|
|
43
|
+
const prefix = `[YH-Request] ${sanitizedInfo.method} ${sanitizedInfo.requestId}`;
|
|
44
|
+
const status = sanitizedInfo.status ? ` [${sanitizedInfo.status}]` : "";
|
|
45
|
+
const duration = sanitizedInfo.duration ? ` (${sanitizedInfo.duration}ms)` : "";
|
|
46
|
+
if (sanitizedInfo.error) {
|
|
47
|
+
log({
|
|
48
|
+
...sanitizedInfo,
|
|
49
|
+
message: `${prefix}${status}${duration} - ${sanitizedInfo.error}`
|
|
50
|
+
});
|
|
51
|
+
} else {
|
|
52
|
+
log({
|
|
53
|
+
...sanitizedInfo,
|
|
54
|
+
message: `${prefix}${status}${duration}`
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
return {
|
|
59
|
+
/**
|
|
60
|
+
* 请求拦截
|
|
61
|
+
*/
|
|
62
|
+
onRequest: (config) => {
|
|
63
|
+
if (!enabled) return config;
|
|
64
|
+
const info = createDebugInfo(config);
|
|
65
|
+
printDebug({
|
|
66
|
+
...info,
|
|
67
|
+
message: `[YH-Request] \u2192 ${info.method} ${info.url}`
|
|
68
|
+
});
|
|
69
|
+
return config;
|
|
70
|
+
},
|
|
71
|
+
/**
|
|
72
|
+
* 响应拦截
|
|
73
|
+
*/
|
|
74
|
+
onResponse: (response) => {
|
|
75
|
+
if (!enabled) return response;
|
|
76
|
+
const info = createDebugInfo(response.config, response);
|
|
77
|
+
const duration = Date.now() - info.timestamp;
|
|
78
|
+
info.duration = duration;
|
|
79
|
+
printDebug(info);
|
|
80
|
+
return response;
|
|
81
|
+
},
|
|
82
|
+
/**
|
|
83
|
+
* 错误拦截
|
|
84
|
+
*/
|
|
85
|
+
onError: (error) => {
|
|
86
|
+
if (!enabled) return error;
|
|
87
|
+
const info = createDebugInfo(error.config || {}, void 0, error);
|
|
88
|
+
printDebug(info);
|
|
89
|
+
return error;
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
export class DebugLogger {
|
|
94
|
+
logs = [];
|
|
95
|
+
maxLogs = 100;
|
|
96
|
+
/**
|
|
97
|
+
* 添加日志
|
|
98
|
+
*/
|
|
99
|
+
addLog(info) {
|
|
100
|
+
this.logs.push(info);
|
|
101
|
+
if (this.logs.length > this.maxLogs) {
|
|
102
|
+
this.logs.shift();
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* 获取所有日志
|
|
107
|
+
*/
|
|
108
|
+
getLogs() {
|
|
109
|
+
return [...this.logs];
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* 清除日志
|
|
113
|
+
*/
|
|
114
|
+
clear() {
|
|
115
|
+
this.logs = [];
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* 根据请求 ID 查找日志
|
|
119
|
+
*/
|
|
120
|
+
findByRequestId(requestId) {
|
|
121
|
+
return this.logs.filter((log) => log.requestId === requestId);
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* 导出日志
|
|
125
|
+
*/
|
|
126
|
+
export() {
|
|
127
|
+
return JSON.stringify(this.logs, null, 2);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
export const debugLogger = new DebugLogger();
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
var _progress = require("./progress.cjs");
|
|
7
|
+
Object.keys(_progress).forEach(function (key) {
|
|
8
|
+
if (key === "default" || key === "__esModule") return;
|
|
9
|
+
if (key in exports && exports[key] === _progress[key]) return;
|
|
10
|
+
Object.defineProperty(exports, key, {
|
|
11
|
+
enumerable: true,
|
|
12
|
+
get: function () {
|
|
13
|
+
return _progress[key];
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
var _security = require("./security.cjs");
|
|
18
|
+
Object.keys(_security).forEach(function (key) {
|
|
19
|
+
if (key === "default" || key === "__esModule") return;
|
|
20
|
+
if (key in exports && exports[key] === _security[key]) return;
|
|
21
|
+
Object.defineProperty(exports, key, {
|
|
22
|
+
enumerable: true,
|
|
23
|
+
get: function () {
|
|
24
|
+
return _security[key];
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
var _debug = require("./debug.cjs");
|
|
29
|
+
Object.keys(_debug).forEach(function (key) {
|
|
30
|
+
if (key === "default" || key === "__esModule") return;
|
|
31
|
+
if (key in exports && exports[key] === _debug[key]) return;
|
|
32
|
+
Object.defineProperty(exports, key, {
|
|
33
|
+
enumerable: true,
|
|
34
|
+
get: function () {
|
|
35
|
+
return _debug[key];
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
});
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.XHRProgressHandler = exports.FetchProgressHandler = void 0;
|
|
7
|
+
exports.createProgressInterceptor = createProgressInterceptor;
|
|
8
|
+
function createProgressInterceptor(options) {
|
|
9
|
+
const {
|
|
10
|
+
onUploadProgress,
|
|
11
|
+
onDownloadProgress
|
|
12
|
+
} = options;
|
|
13
|
+
let uploadStartTime = 0;
|
|
14
|
+
let downloadStartTime = 0;
|
|
15
|
+
const calculateRate = (loaded, startTime) => {
|
|
16
|
+
const elapsed = (Date.now() - startTime) / 1e3;
|
|
17
|
+
return elapsed > 0 ? loaded / elapsed : 0;
|
|
18
|
+
};
|
|
19
|
+
const calculateEstimated = (loaded, total, rate) => {
|
|
20
|
+
if (rate <= 0 || total <= 0) return void 0;
|
|
21
|
+
const remaining = total - loaded;
|
|
22
|
+
return remaining > 0 ? remaining / rate * 1e3 : 0;
|
|
23
|
+
};
|
|
24
|
+
const createProgressHandler = isUpload => {
|
|
25
|
+
return event => {
|
|
26
|
+
const startTime = isUpload ? uploadStartTime : downloadStartTime;
|
|
27
|
+
const rate = calculateRate(event.loaded, startTime);
|
|
28
|
+
const estimated = calculateEstimated(event.loaded, event.total, rate);
|
|
29
|
+
const progressEvent = {
|
|
30
|
+
...event,
|
|
31
|
+
rate,
|
|
32
|
+
estimated
|
|
33
|
+
};
|
|
34
|
+
if (isUpload) {
|
|
35
|
+
onUploadProgress?.(progressEvent);
|
|
36
|
+
} else {
|
|
37
|
+
onDownloadProgress?.(progressEvent);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
return {
|
|
42
|
+
/**
|
|
43
|
+
* 请求拦截 - 初始化上传进度
|
|
44
|
+
*/
|
|
45
|
+
onRequest: config => {
|
|
46
|
+
uploadStartTime = Date.now();
|
|
47
|
+
return config;
|
|
48
|
+
},
|
|
49
|
+
/**
|
|
50
|
+
* 响应拦截 - 初始化下载进度
|
|
51
|
+
*/
|
|
52
|
+
onResponse: response => {
|
|
53
|
+
downloadStartTime = Date.now();
|
|
54
|
+
return response;
|
|
55
|
+
},
|
|
56
|
+
/**
|
|
57
|
+
* 获取上传进度处理器
|
|
58
|
+
*/
|
|
59
|
+
getUploadHandler: createProgressHandler(true),
|
|
60
|
+
/**
|
|
61
|
+
* 获取下载进度处理器
|
|
62
|
+
*/
|
|
63
|
+
getDownloadHandler: createProgressHandler(false)
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
class XHRProgressHandler {
|
|
67
|
+
xhr = null;
|
|
68
|
+
onUploadProgress;
|
|
69
|
+
onDownloadProgress;
|
|
70
|
+
uploadStartTime = 0;
|
|
71
|
+
downloadStartTime = 0;
|
|
72
|
+
constructor(options) {
|
|
73
|
+
this.onUploadProgress = options.onUploadProgress;
|
|
74
|
+
this.onDownloadProgress = options.onDownloadProgress;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* 绑定 XHR 事件
|
|
78
|
+
*/
|
|
79
|
+
bind(xhr) {
|
|
80
|
+
this.xhr = xhr;
|
|
81
|
+
if (this.onUploadProgress) {
|
|
82
|
+
xhr.upload.addEventListener("progress", event => {
|
|
83
|
+
if (event.lengthComputable) {
|
|
84
|
+
this.handleUploadProgress(event);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
if (this.onDownloadProgress) {
|
|
89
|
+
xhr.addEventListener("progress", event => {
|
|
90
|
+
if (event.lengthComputable) {
|
|
91
|
+
this.handleDownloadProgress(event);
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
handleUploadProgress(event) {
|
|
97
|
+
if (!this.onUploadProgress) return;
|
|
98
|
+
const now = Date.now();
|
|
99
|
+
const elapsed = (now - this.uploadStartTime) / 1e3;
|
|
100
|
+
const rate = elapsed > 0 ? event.loaded / elapsed : 0;
|
|
101
|
+
const estimated = rate > 0 ? (event.total - event.loaded) / rate * 1e3 : void 0;
|
|
102
|
+
this.onUploadProgress({
|
|
103
|
+
loaded: event.loaded,
|
|
104
|
+
total: event.total,
|
|
105
|
+
percent: event.loaded / event.total * 100,
|
|
106
|
+
rate,
|
|
107
|
+
estimated
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
handleDownloadProgress(event) {
|
|
111
|
+
if (!this.onDownloadProgress) return;
|
|
112
|
+
const now = Date.now();
|
|
113
|
+
const elapsed = (now - this.downloadStartTime) / 1e3;
|
|
114
|
+
const rate = elapsed > 0 ? event.loaded / elapsed : 0;
|
|
115
|
+
const estimated = rate > 0 ? (event.total - event.loaded) / rate * 1e3 : void 0;
|
|
116
|
+
this.onDownloadProgress({
|
|
117
|
+
loaded: event.loaded,
|
|
118
|
+
total: event.total,
|
|
119
|
+
percent: event.loaded / event.total * 100,
|
|
120
|
+
rate,
|
|
121
|
+
estimated
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* 开始上传
|
|
126
|
+
*/
|
|
127
|
+
startUpload() {
|
|
128
|
+
this.uploadStartTime = Date.now();
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* 开始下载
|
|
132
|
+
*/
|
|
133
|
+
startDownload() {
|
|
134
|
+
this.downloadStartTime = Date.now();
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
exports.XHRProgressHandler = XHRProgressHandler;
|
|
138
|
+
class FetchProgressHandler {
|
|
139
|
+
onDownloadProgress;
|
|
140
|
+
downloadStartTime = 0;
|
|
141
|
+
downloadedBytes = 0;
|
|
142
|
+
totalBytes = 0;
|
|
143
|
+
constructor(options) {
|
|
144
|
+
this.onDownloadProgress = options.onDownloadProgress;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* 获取读取处理器
|
|
148
|
+
*/
|
|
149
|
+
getReaderHandler() {
|
|
150
|
+
return (chunk, total) => {
|
|
151
|
+
this.downloadedBytes += chunk.length;
|
|
152
|
+
if (total !== void 0) {
|
|
153
|
+
this.totalBytes = total;
|
|
154
|
+
}
|
|
155
|
+
if (this.onDownloadProgress) {
|
|
156
|
+
const now = Date.now();
|
|
157
|
+
const elapsed = (now - this.downloadStartTime) / 1e3;
|
|
158
|
+
const rate = elapsed > 0 ? this.downloadedBytes / elapsed : 0;
|
|
159
|
+
const estimated = rate > 0 && this.totalBytes > 0 ? (this.totalBytes - this.downloadedBytes) / rate * 1e3 : void 0;
|
|
160
|
+
this.onDownloadProgress({
|
|
161
|
+
loaded: this.downloadedBytes,
|
|
162
|
+
total: this.totalBytes,
|
|
163
|
+
percent: this.totalBytes > 0 ? this.downloadedBytes / this.totalBytes * 100 : 0,
|
|
164
|
+
rate,
|
|
165
|
+
estimated
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* 开始下载
|
|
172
|
+
*/
|
|
173
|
+
startDownload() {
|
|
174
|
+
this.downloadStartTime = Date.now();
|
|
175
|
+
this.downloadedBytes = 0;
|
|
176
|
+
this.totalBytes = 0;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* 设置总字节数
|
|
180
|
+
*/
|
|
181
|
+
setTotal(total) {
|
|
182
|
+
this.totalBytes = total;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
exports.FetchProgressHandler = FetchProgressHandler;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 进度监听拦截器
|
|
3
|
+
* 支持上传和下载进度
|
|
4
|
+
*/
|
|
5
|
+
import type { InternalRequestOptions, RequestResponse } from '../types';
|
|
6
|
+
/** 进度事件 */
|
|
7
|
+
export interface ProgressEvent {
|
|
8
|
+
/** 已传输字节 */
|
|
9
|
+
loaded: number;
|
|
10
|
+
/** 总字节 */
|
|
11
|
+
total: number;
|
|
12
|
+
/** 进度百分比 (0-100) */
|
|
13
|
+
percent: number;
|
|
14
|
+
/** 传输速率 (bytes/s) */
|
|
15
|
+
rate?: number;
|
|
16
|
+
/** 预计剩余时间 (ms) */
|
|
17
|
+
estimated?: number;
|
|
18
|
+
}
|
|
19
|
+
/** 进度回调函数 */
|
|
20
|
+
export type ProgressCallback = (event: ProgressEvent) => void;
|
|
21
|
+
/** 进度拦截器配置 */
|
|
22
|
+
export interface ProgressInterceptorOptions {
|
|
23
|
+
/** 上传进度回调 */
|
|
24
|
+
onUploadProgress?: ProgressCallback;
|
|
25
|
+
/** 下载进度回调 */
|
|
26
|
+
onDownloadProgress?: ProgressCallback;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* 创建进度拦截器
|
|
30
|
+
*/
|
|
31
|
+
export declare function createProgressInterceptor(options: ProgressInterceptorOptions): {
|
|
32
|
+
/**
|
|
33
|
+
* 请求拦截 - 初始化上传进度
|
|
34
|
+
*/
|
|
35
|
+
onRequest: (config: InternalRequestOptions) => InternalRequestOptions;
|
|
36
|
+
/**
|
|
37
|
+
* 响应拦截 - 初始化下载进度
|
|
38
|
+
*/
|
|
39
|
+
onResponse: <T>(response: RequestResponse<T>) => RequestResponse<T>;
|
|
40
|
+
/**
|
|
41
|
+
* 获取上传进度处理器
|
|
42
|
+
*/
|
|
43
|
+
getUploadHandler: (event: ProgressEvent) => void;
|
|
44
|
+
/**
|
|
45
|
+
* 获取下载进度处理器
|
|
46
|
+
*/
|
|
47
|
+
getDownloadHandler: (event: ProgressEvent) => void;
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* XHR 进度处理器
|
|
51
|
+
* 用于传统浏览器的 XMLHttpRequest
|
|
52
|
+
*/
|
|
53
|
+
export declare class XHRProgressHandler {
|
|
54
|
+
private xhr;
|
|
55
|
+
private onUploadProgress?;
|
|
56
|
+
private onDownloadProgress?;
|
|
57
|
+
private uploadStartTime;
|
|
58
|
+
private downloadStartTime;
|
|
59
|
+
constructor(options: ProgressInterceptorOptions);
|
|
60
|
+
/**
|
|
61
|
+
* 绑定 XHR 事件
|
|
62
|
+
*/
|
|
63
|
+
bind(xhr: XMLHttpRequest): void;
|
|
64
|
+
private handleUploadProgress;
|
|
65
|
+
private handleDownloadProgress;
|
|
66
|
+
/**
|
|
67
|
+
* 开始上传
|
|
68
|
+
*/
|
|
69
|
+
startUpload(): void;
|
|
70
|
+
/**
|
|
71
|
+
* 开始下载
|
|
72
|
+
*/
|
|
73
|
+
startDownload(): void;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Fetch 流式进度处理器
|
|
77
|
+
* 用于 Fetch API 的响应体流式读取
|
|
78
|
+
*/
|
|
79
|
+
export declare class FetchProgressHandler {
|
|
80
|
+
private onDownloadProgress?;
|
|
81
|
+
private downloadStartTime;
|
|
82
|
+
private downloadedBytes;
|
|
83
|
+
private totalBytes;
|
|
84
|
+
constructor(options: ProgressInterceptorOptions);
|
|
85
|
+
/**
|
|
86
|
+
* 获取读取处理器
|
|
87
|
+
*/
|
|
88
|
+
getReaderHandler(): (chunk: Uint8Array, total?: number) => void;
|
|
89
|
+
/**
|
|
90
|
+
* 开始下载
|
|
91
|
+
*/
|
|
92
|
+
startDownload(): void;
|
|
93
|
+
/**
|
|
94
|
+
* 设置总字节数
|
|
95
|
+
*/
|
|
96
|
+
setTotal(total: number): void;
|
|
97
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
export function createProgressInterceptor(options) {
|
|
2
|
+
const { onUploadProgress, onDownloadProgress } = options;
|
|
3
|
+
let uploadStartTime = 0;
|
|
4
|
+
let downloadStartTime = 0;
|
|
5
|
+
const calculateRate = (loaded, startTime) => {
|
|
6
|
+
const elapsed = (Date.now() - startTime) / 1e3;
|
|
7
|
+
return elapsed > 0 ? loaded / elapsed : 0;
|
|
8
|
+
};
|
|
9
|
+
const calculateEstimated = (loaded, total, rate) => {
|
|
10
|
+
if (rate <= 0 || total <= 0) return void 0;
|
|
11
|
+
const remaining = total - loaded;
|
|
12
|
+
return remaining > 0 ? remaining / rate * 1e3 : 0;
|
|
13
|
+
};
|
|
14
|
+
const createProgressHandler = (isUpload) => {
|
|
15
|
+
return (event) => {
|
|
16
|
+
const startTime = isUpload ? uploadStartTime : downloadStartTime;
|
|
17
|
+
const rate = calculateRate(event.loaded, startTime);
|
|
18
|
+
const estimated = calculateEstimated(event.loaded, event.total, rate);
|
|
19
|
+
const progressEvent = {
|
|
20
|
+
...event,
|
|
21
|
+
rate,
|
|
22
|
+
estimated
|
|
23
|
+
};
|
|
24
|
+
if (isUpload) {
|
|
25
|
+
onUploadProgress?.(progressEvent);
|
|
26
|
+
} else {
|
|
27
|
+
onDownloadProgress?.(progressEvent);
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
return {
|
|
32
|
+
/**
|
|
33
|
+
* 请求拦截 - 初始化上传进度
|
|
34
|
+
*/
|
|
35
|
+
onRequest: (config) => {
|
|
36
|
+
uploadStartTime = Date.now();
|
|
37
|
+
return config;
|
|
38
|
+
},
|
|
39
|
+
/**
|
|
40
|
+
* 响应拦截 - 初始化下载进度
|
|
41
|
+
*/
|
|
42
|
+
onResponse: (response) => {
|
|
43
|
+
downloadStartTime = Date.now();
|
|
44
|
+
return response;
|
|
45
|
+
},
|
|
46
|
+
/**
|
|
47
|
+
* 获取上传进度处理器
|
|
48
|
+
*/
|
|
49
|
+
getUploadHandler: createProgressHandler(true),
|
|
50
|
+
/**
|
|
51
|
+
* 获取下载进度处理器
|
|
52
|
+
*/
|
|
53
|
+
getDownloadHandler: createProgressHandler(false)
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
export class XHRProgressHandler {
|
|
57
|
+
xhr = null;
|
|
58
|
+
onUploadProgress;
|
|
59
|
+
onDownloadProgress;
|
|
60
|
+
uploadStartTime = 0;
|
|
61
|
+
downloadStartTime = 0;
|
|
62
|
+
constructor(options) {
|
|
63
|
+
this.onUploadProgress = options.onUploadProgress;
|
|
64
|
+
this.onDownloadProgress = options.onDownloadProgress;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* 绑定 XHR 事件
|
|
68
|
+
*/
|
|
69
|
+
bind(xhr) {
|
|
70
|
+
this.xhr = xhr;
|
|
71
|
+
if (this.onUploadProgress) {
|
|
72
|
+
xhr.upload.addEventListener("progress", (event) => {
|
|
73
|
+
if (event.lengthComputable) {
|
|
74
|
+
this.handleUploadProgress(
|
|
75
|
+
event
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
if (this.onDownloadProgress) {
|
|
81
|
+
xhr.addEventListener("progress", (event) => {
|
|
82
|
+
if (event.lengthComputable) {
|
|
83
|
+
this.handleDownloadProgress(
|
|
84
|
+
event
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
handleUploadProgress(event) {
|
|
91
|
+
if (!this.onUploadProgress) return;
|
|
92
|
+
const now = Date.now();
|
|
93
|
+
const elapsed = (now - this.uploadStartTime) / 1e3;
|
|
94
|
+
const rate = elapsed > 0 ? event.loaded / elapsed : 0;
|
|
95
|
+
const estimated = rate > 0 ? (event.total - event.loaded) / rate * 1e3 : void 0;
|
|
96
|
+
this.onUploadProgress({
|
|
97
|
+
loaded: event.loaded,
|
|
98
|
+
total: event.total,
|
|
99
|
+
percent: event.loaded / event.total * 100,
|
|
100
|
+
rate,
|
|
101
|
+
estimated
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
handleDownloadProgress(event) {
|
|
105
|
+
if (!this.onDownloadProgress) return;
|
|
106
|
+
const now = Date.now();
|
|
107
|
+
const elapsed = (now - this.downloadStartTime) / 1e3;
|
|
108
|
+
const rate = elapsed > 0 ? event.loaded / elapsed : 0;
|
|
109
|
+
const estimated = rate > 0 ? (event.total - event.loaded) / rate * 1e3 : void 0;
|
|
110
|
+
this.onDownloadProgress({
|
|
111
|
+
loaded: event.loaded,
|
|
112
|
+
total: event.total,
|
|
113
|
+
percent: event.loaded / event.total * 100,
|
|
114
|
+
rate,
|
|
115
|
+
estimated
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* 开始上传
|
|
120
|
+
*/
|
|
121
|
+
startUpload() {
|
|
122
|
+
this.uploadStartTime = Date.now();
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* 开始下载
|
|
126
|
+
*/
|
|
127
|
+
startDownload() {
|
|
128
|
+
this.downloadStartTime = Date.now();
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
export class FetchProgressHandler {
|
|
132
|
+
onDownloadProgress;
|
|
133
|
+
downloadStartTime = 0;
|
|
134
|
+
downloadedBytes = 0;
|
|
135
|
+
totalBytes = 0;
|
|
136
|
+
constructor(options) {
|
|
137
|
+
this.onDownloadProgress = options.onDownloadProgress;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* 获取读取处理器
|
|
141
|
+
*/
|
|
142
|
+
getReaderHandler() {
|
|
143
|
+
return (chunk, total) => {
|
|
144
|
+
this.downloadedBytes += chunk.length;
|
|
145
|
+
if (total !== void 0) {
|
|
146
|
+
this.totalBytes = total;
|
|
147
|
+
}
|
|
148
|
+
if (this.onDownloadProgress) {
|
|
149
|
+
const now = Date.now();
|
|
150
|
+
const elapsed = (now - this.downloadStartTime) / 1e3;
|
|
151
|
+
const rate = elapsed > 0 ? this.downloadedBytes / elapsed : 0;
|
|
152
|
+
const estimated = rate > 0 && this.totalBytes > 0 ? (this.totalBytes - this.downloadedBytes) / rate * 1e3 : void 0;
|
|
153
|
+
this.onDownloadProgress({
|
|
154
|
+
loaded: this.downloadedBytes,
|
|
155
|
+
total: this.totalBytes,
|
|
156
|
+
percent: this.totalBytes > 0 ? this.downloadedBytes / this.totalBytes * 100 : 0,
|
|
157
|
+
rate,
|
|
158
|
+
estimated
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* 开始下载
|
|
165
|
+
*/
|
|
166
|
+
startDownload() {
|
|
167
|
+
this.downloadStartTime = Date.now();
|
|
168
|
+
this.downloadedBytes = 0;
|
|
169
|
+
this.totalBytes = 0;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* 设置总字节数
|
|
173
|
+
*/
|
|
174
|
+
setTotal(total) {
|
|
175
|
+
this.totalBytes = total;
|
|
176
|
+
}
|
|
177
|
+
}
|