etherreq 1.2.1 → 1.2.4
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/dist/cache.js +44 -41
- package/dist/etherreq.js +51 -67
- package/dist/index.js +8 -2
- package/dist/request.js +2 -2
- package/dist/types/cache.d.ts +1 -1
- package/dist/types/etherreq.d.ts +8 -10
- package/dist/types/index.d.ts +2 -2
- package/dist/types/request.d.ts +1 -1
- package/dist/types/types/config.d.ts +10 -1
- package/package.json +1 -1
package/dist/cache.js
CHANGED
|
@@ -7,8 +7,8 @@ function hashString(str) {
|
|
|
7
7
|
let hash = 0;
|
|
8
8
|
for (let i = 0; i < str.length; i++) {
|
|
9
9
|
const char = str.charCodeAt(i);
|
|
10
|
-
hash = (
|
|
11
|
-
hash
|
|
10
|
+
hash = (hash << 5) - hash + char;
|
|
11
|
+
hash |= 0; // 转换为32位整数
|
|
12
12
|
}
|
|
13
13
|
return Math.abs(hash).toString(36);
|
|
14
14
|
}
|
|
@@ -18,9 +18,9 @@ function hashString(str) {
|
|
|
18
18
|
*/
|
|
19
19
|
function isLocalStorageAvailable() {
|
|
20
20
|
try {
|
|
21
|
-
const
|
|
22
|
-
localStorage.setItem(
|
|
23
|
-
localStorage.removeItem(
|
|
21
|
+
const test = '__storage_test__';
|
|
22
|
+
localStorage.setItem(test, test);
|
|
23
|
+
localStorage.removeItem(test);
|
|
24
24
|
return true;
|
|
25
25
|
}
|
|
26
26
|
catch (e) {
|
|
@@ -34,15 +34,19 @@ function isLocalStorageAvailable() {
|
|
|
34
34
|
* @returns 缓存键字符串
|
|
35
35
|
*/
|
|
36
36
|
export function getCacheKey(url, options) {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
for (const [key, value] of Object.entries(rawParams)) {
|
|
41
|
-
stringifiedParams[key] = String(value);
|
|
37
|
+
// 仅对GET请求添加参数到缓存键中
|
|
38
|
+
if (options.method.toUpperCase() !== 'GET') {
|
|
39
|
+
return hashString(url);
|
|
42
40
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
41
|
+
// 将参数按字母顺序排序以确保一致的缓存键
|
|
42
|
+
if (options.params) {
|
|
43
|
+
const sortedParams = Object.keys(options.params)
|
|
44
|
+
.sort()
|
|
45
|
+
.map(key => `${key}=${options.params[key]}`)
|
|
46
|
+
.join('&');
|
|
47
|
+
return hashString(`${url}?${sortedParams}`);
|
|
48
|
+
}
|
|
49
|
+
return hashString(url);
|
|
46
50
|
}
|
|
47
51
|
/**
|
|
48
52
|
* 内存缓存存储类
|
|
@@ -69,7 +73,7 @@ class MemoryCache {
|
|
|
69
73
|
cleanupExpired() {
|
|
70
74
|
const now = Date.now();
|
|
71
75
|
for (const [key, entry] of this.store.entries()) {
|
|
72
|
-
if (entry.expireAt
|
|
76
|
+
if (entry.expireAt < now) {
|
|
73
77
|
this.store.delete(key);
|
|
74
78
|
}
|
|
75
79
|
}
|
|
@@ -86,14 +90,14 @@ class MemoryCache {
|
|
|
86
90
|
*/
|
|
87
91
|
get(key) {
|
|
88
92
|
const entry = this.store.get(key);
|
|
89
|
-
if (entry
|
|
90
|
-
return
|
|
93
|
+
if (!entry) {
|
|
94
|
+
return null;
|
|
91
95
|
}
|
|
92
|
-
|
|
93
|
-
if (entry) {
|
|
96
|
+
if (entry.expireAt < Date.now()) {
|
|
94
97
|
this.store.delete(key);
|
|
98
|
+
return null;
|
|
95
99
|
}
|
|
96
|
-
return
|
|
100
|
+
return entry.value;
|
|
97
101
|
}
|
|
98
102
|
/**
|
|
99
103
|
* 删除指定缓存
|
|
@@ -127,7 +131,7 @@ class MemoryCache {
|
|
|
127
131
|
* 本地存储缓存类
|
|
128
132
|
*/
|
|
129
133
|
class LocalStorageCache {
|
|
130
|
-
constructor(prefix = '
|
|
134
|
+
constructor(prefix = 'etherreq_cache_') {
|
|
131
135
|
this.prefix = prefix;
|
|
132
136
|
this.available = isLocalStorageAvailable();
|
|
133
137
|
}
|
|
@@ -138,12 +142,14 @@ class LocalStorageCache {
|
|
|
138
142
|
if (!this.available)
|
|
139
143
|
return;
|
|
140
144
|
try {
|
|
141
|
-
const
|
|
142
|
-
|
|
143
|
-
|
|
145
|
+
const item = {
|
|
146
|
+
value,
|
|
147
|
+
expireAt: Date.now() + ttl,
|
|
148
|
+
};
|
|
149
|
+
localStorage.setItem(this.prefix + key, JSON.stringify(item));
|
|
144
150
|
}
|
|
145
|
-
catch (
|
|
146
|
-
console.warn('
|
|
151
|
+
catch (e) {
|
|
152
|
+
console.warn('Cache set failed:', e);
|
|
147
153
|
}
|
|
148
154
|
}
|
|
149
155
|
/**
|
|
@@ -153,21 +159,19 @@ class LocalStorageCache {
|
|
|
153
159
|
if (!this.available)
|
|
154
160
|
return null;
|
|
155
161
|
try {
|
|
156
|
-
const
|
|
157
|
-
if (!
|
|
162
|
+
const itemStr = localStorage.getItem(this.prefix + key);
|
|
163
|
+
if (!itemStr) {
|
|
158
164
|
return null;
|
|
159
|
-
const entry = JSON.parse(item);
|
|
160
|
-
if (entry.expireAt > Date.now()) {
|
|
161
|
-
return entry.value;
|
|
162
165
|
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
localStorage.removeItem(
|
|
166
|
+
const item = JSON.parse(itemStr);
|
|
167
|
+
if (item.expireAt < Date.now()) {
|
|
168
|
+
localStorage.removeItem(this.prefix + key);
|
|
166
169
|
return null;
|
|
167
170
|
}
|
|
171
|
+
return item.value;
|
|
168
172
|
}
|
|
169
|
-
catch (
|
|
170
|
-
console.
|
|
173
|
+
catch (e) {
|
|
174
|
+
console.error('Cache get failed:', e);
|
|
171
175
|
return null;
|
|
172
176
|
}
|
|
173
177
|
}
|
|
@@ -191,17 +195,16 @@ class LocalStorageCache {
|
|
|
191
195
|
if (!this.available)
|
|
192
196
|
return;
|
|
193
197
|
try {
|
|
194
|
-
const keysToRemove = [];
|
|
195
198
|
for (let i = 0; i < localStorage.length; i++) {
|
|
196
199
|
const key = localStorage.key(i);
|
|
197
200
|
if (key && key.startsWith(this.prefix)) {
|
|
198
|
-
|
|
201
|
+
localStorage.removeItem(key);
|
|
202
|
+
i--; // 修正索引
|
|
199
203
|
}
|
|
200
204
|
}
|
|
201
|
-
keysToRemove.forEach(key => localStorage.removeItem(key));
|
|
202
205
|
}
|
|
203
|
-
catch (
|
|
204
|
-
console.
|
|
206
|
+
catch (e) {
|
|
207
|
+
console.error('Cache clear failed:', e);
|
|
205
208
|
}
|
|
206
209
|
}
|
|
207
210
|
}
|
|
@@ -276,6 +279,6 @@ class CacheFactory {
|
|
|
276
279
|
}
|
|
277
280
|
}
|
|
278
281
|
// 默认导出混合缓存实例
|
|
279
|
-
export const requestCache = CacheFactory.create(
|
|
282
|
+
export const requestCache = CacheFactory.create();
|
|
280
283
|
// 导出缓存工厂和工具函数
|
|
281
284
|
export { CacheFactory, MemoryCache, LocalStorageCache, HybridCache, hashString, isLocalStorageAvailable };
|
package/dist/etherreq.js
CHANGED
|
@@ -1,84 +1,68 @@
|
|
|
1
|
-
// src/etherreq.ts
|
|
2
1
|
export const create = (defaultConfig = {}) => {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const nextConfig = await interceptor.fulfilled(config);
|
|
14
|
-
// ✅ 拦截器可以返回一个 response 对象,表示中断请求
|
|
15
|
-
if (nextConfig && nextConfig.isFromCache) {
|
|
16
|
-
return nextConfig.data; // ✅ 直接返回缓存结果,不再往下执行
|
|
17
|
-
}
|
|
18
|
-
config = nextConfig;
|
|
2
|
+
// 拦截器存储
|
|
3
|
+
const requestInterceptors = [];
|
|
4
|
+
const responseInterceptors = [];
|
|
5
|
+
const client = async (config) => {
|
|
6
|
+
// 合并默认配置
|
|
7
|
+
const mergedConfig = { ...defaultConfig, ...config };
|
|
8
|
+
// 应用请求拦截器
|
|
9
|
+
let processedConfig = { ...mergedConfig };
|
|
10
|
+
for (const interceptor of requestInterceptors) {
|
|
11
|
+
processedConfig = await interceptor(processedConfig);
|
|
19
12
|
}
|
|
20
|
-
// 检查 url 是否存在
|
|
21
|
-
if (!config.url) {
|
|
22
|
-
throw new Error('请求配置中缺少 url 属性');
|
|
23
|
-
}
|
|
24
|
-
let response;
|
|
25
|
-
const { url, method = 'GET', headers = {}, body } = config;
|
|
26
|
-
let text = '';
|
|
27
13
|
try {
|
|
28
|
-
|
|
14
|
+
// 确定是否需要发送请求体
|
|
15
|
+
const method = processedConfig.method || 'GET';
|
|
16
|
+
const hasBody = processedConfig.body &&
|
|
17
|
+
method !== 'GET' &&
|
|
18
|
+
method !== 'HEAD' &&
|
|
19
|
+
method !== 'OPTIONS';
|
|
20
|
+
// 发送请求
|
|
21
|
+
const response = await fetch(processedConfig.url, {
|
|
29
22
|
method,
|
|
30
|
-
headers
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
throw new Error('服务器返回了 HTML 页面,预期为 JSON 格式');
|
|
23
|
+
headers: {
|
|
24
|
+
...(processedConfig.headers || {}),
|
|
25
|
+
},
|
|
26
|
+
// 只有非GET/HEAD/OPTIONS请求才发送body
|
|
27
|
+
body: hasBody ? JSON.stringify(processedConfig.body) : undefined,
|
|
28
|
+
});
|
|
29
|
+
// 检查响应状态
|
|
30
|
+
if (!response.ok) {
|
|
31
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
40
32
|
}
|
|
41
|
-
|
|
42
|
-
|
|
33
|
+
// 解析响应数据
|
|
34
|
+
const data = await response.json();
|
|
35
|
+
let responseObject = {
|
|
43
36
|
data,
|
|
44
|
-
status:
|
|
45
|
-
statusText:
|
|
46
|
-
headers:
|
|
47
|
-
config,
|
|
37
|
+
status: response.status,
|
|
38
|
+
statusText: response.statusText,
|
|
39
|
+
headers: response.headers,
|
|
40
|
+
config: processedConfig,
|
|
48
41
|
};
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
if (text) {
|
|
53
|
-
error.message += `\n响应内容: ${text.substring(0, 200)}...`;
|
|
54
|
-
}
|
|
55
|
-
for (const interceptor of interceptors.response) {
|
|
56
|
-
if (interceptor.rejected) {
|
|
57
|
-
return interceptor.rejected(error);
|
|
58
|
-
}
|
|
42
|
+
// 应用响应拦截器
|
|
43
|
+
for (const interceptor of responseInterceptors) {
|
|
44
|
+
responseObject = await interceptor(responseObject);
|
|
59
45
|
}
|
|
60
|
-
|
|
46
|
+
return responseObject;
|
|
61
47
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
48
|
+
catch (error) {
|
|
49
|
+
// 如果是网络错误或其他类型的错误
|
|
50
|
+
throw new Error(`Request failed: ${error.message}`);
|
|
65
51
|
}
|
|
66
|
-
return response;
|
|
67
|
-
};
|
|
68
|
-
const instance = (config) => {
|
|
69
|
-
return dispatchRequest({
|
|
70
|
-
...defaultConfig,
|
|
71
|
-
...config,
|
|
72
|
-
});
|
|
73
52
|
};
|
|
74
|
-
|
|
75
|
-
|
|
53
|
+
// 拦截器接口
|
|
54
|
+
client.interceptors = {
|
|
76
55
|
request: {
|
|
77
|
-
use: (fulfilled, rejected) =>
|
|
56
|
+
use: (fulfilled, rejected) => {
|
|
57
|
+
requestInterceptors.push(fulfilled);
|
|
58
|
+
},
|
|
78
59
|
},
|
|
79
60
|
response: {
|
|
80
|
-
use: (fulfilled, rejected) =>
|
|
61
|
+
use: (fulfilled, rejected) => {
|
|
62
|
+
responseInterceptors.push(fulfilled);
|
|
63
|
+
},
|
|
81
64
|
},
|
|
82
65
|
};
|
|
83
|
-
return
|
|
66
|
+
return client;
|
|
84
67
|
};
|
|
68
|
+
export {};
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
-
import { request, baseURL as _baseURL, setBaseURL } from './request';
|
|
2
|
+
import { request, baseURL as _baseURL, setBaseURL } from './request.js';
|
|
3
3
|
// 示例封装方法
|
|
4
4
|
const createMethod = (method) => (url, data, callback) => {
|
|
5
5
|
let options;
|
|
@@ -11,7 +11,13 @@ const createMethod = (method) => (url, data, callback) => {
|
|
|
11
11
|
!data.headers &&
|
|
12
12
|
!data.params &&
|
|
13
13
|
!data.baseURL) {
|
|
14
|
-
|
|
14
|
+
// 对于GET请求,不应发送body,而是将数据作为params处理
|
|
15
|
+
if (method.toUpperCase() === 'GET') {
|
|
16
|
+
options = { params: data, method: method };
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
options = { body: data, method: method };
|
|
20
|
+
}
|
|
15
21
|
}
|
|
16
22
|
else {
|
|
17
23
|
options = { ...data, method: method };
|
package/dist/request.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/request.ts
|
|
2
|
-
import { create } from './etherreq';
|
|
3
|
-
import { requestCache, getCacheKey } from './cache';
|
|
2
|
+
import { create } from './etherreq.js';
|
|
3
|
+
import { requestCache, getCacheKey } from './cache.js';
|
|
4
4
|
// 创建一个带有默认配置的请求实例
|
|
5
5
|
const instance = create({
|
|
6
6
|
baseURL: 'https://api.example.com', // 默认基础 URL
|
package/dist/types/cache.d.ts
CHANGED
package/dist/types/etherreq.d.ts
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
(config: RequestConfig): Promise<any>;
|
|
10
|
-
use(fulfilled: any, rejected: any): void;
|
|
1
|
+
export declare const create: (defaultConfig?: any) => {
|
|
2
|
+
(config: any): Promise<{
|
|
3
|
+
data: any;
|
|
4
|
+
status: number;
|
|
5
|
+
statusText: string;
|
|
6
|
+
headers: Headers;
|
|
7
|
+
config: any;
|
|
8
|
+
}>;
|
|
11
9
|
interceptors: {
|
|
12
10
|
request: {
|
|
13
11
|
use: (fulfilled: any, rejected: any) => void;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { baseURL as _baseURL, setBaseURL } from './request';
|
|
2
|
-
import { EtherRequestOptions } from './types/cache';
|
|
1
|
+
import { baseURL as _baseURL, setBaseURL } from './request.js';
|
|
2
|
+
import type { EtherRequestOptions } from './types/cache.js';
|
|
3
3
|
export declare const etherreq: ((url: string, options?: EtherRequestOptions, callback?: (error: Error | null, data: any) => void) => Promise<any> | undefined) & {
|
|
4
4
|
get: (url: string, data?: any, callback?: (error: Error | null, data: any) => void) => Promise<any> | undefined;
|
|
5
5
|
post: (url: string, data?: any, callback?: (error: Error | null, data: any) => void) => Promise<any> | undefined;
|
package/dist/types/request.d.ts
CHANGED
|
@@ -1,4 +1,13 @@
|
|
|
1
|
-
import { CacheOptions } from './cache';
|
|
1
|
+
import type { CacheOptions } from './cache';
|
|
2
|
+
export interface RequestConfig {
|
|
3
|
+
url?: string;
|
|
4
|
+
method?: string;
|
|
5
|
+
headers?: Record<string, string>;
|
|
6
|
+
body?: any;
|
|
7
|
+
params?: Record<string, string | number | boolean>;
|
|
8
|
+
baseURL?: string;
|
|
9
|
+
cache?: CacheOptions | false;
|
|
10
|
+
}
|
|
2
11
|
/**
|
|
3
12
|
* 全局配置接口
|
|
4
13
|
*/
|