etherreq 1.2.5 → 1.2.7
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/etherreq.js +65 -32
- package/dist/index.js +16 -19
- package/dist/request.js +31 -19
- package/dist/types/etherreq.d.ts +10 -11
- package/dist/types/index.d.ts +9 -9
- package/dist/types/request.d.ts +14 -6
- package/dist/types/types/cache.d.ts +1 -0
- package/package.json +1 -1
package/dist/etherreq.js
CHANGED
|
@@ -1,68 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 创建一个可配置的 HTTP 客户端
|
|
3
|
+
* @param defaultConfig - 默认配置(支持 baseURL, headers 等)
|
|
4
|
+
*/
|
|
1
5
|
export const create = (defaultConfig = {}) => {
|
|
2
|
-
// 拦截器存储
|
|
3
6
|
const requestInterceptors = [];
|
|
4
7
|
const responseInterceptors = [];
|
|
8
|
+
/**
|
|
9
|
+
* 核心请求函数
|
|
10
|
+
*/
|
|
5
11
|
const client = async (config) => {
|
|
6
|
-
//
|
|
7
|
-
const
|
|
12
|
+
// 合并配置:传入 config > defaultConfig,确保url始终存在
|
|
13
|
+
const { url, method: inputMethod, ...configWithoutUrl } = config; // 分离url和其他配置
|
|
14
|
+
const mergedConfig = {
|
|
15
|
+
url, // 确保url存在
|
|
16
|
+
method: (inputMethod ?? defaultConfig.method ?? 'GET'), // 使用输入的method或默认GET,并添加类型断言
|
|
17
|
+
headers: {
|
|
18
|
+
...defaultConfig?.headers,
|
|
19
|
+
...config.headers,
|
|
20
|
+
},
|
|
21
|
+
baseURL: config.baseURL ?? defaultConfig.baseURL,
|
|
22
|
+
body: config.body ?? defaultConfig.body,
|
|
23
|
+
params: config.params ?? defaultConfig.params,
|
|
24
|
+
cache: config.cache ?? defaultConfig.cache,
|
|
25
|
+
...configWithoutUrl, // 使用分离后的配置,避免defaultConfig覆盖已明确设置的值
|
|
26
|
+
};
|
|
27
|
+
// 🔧 自动拼接 baseURL(如果提供了)
|
|
28
|
+
let finalUrl = mergedConfig.url;
|
|
29
|
+
if (mergedConfig.baseURL && mergedConfig.url && !mergedConfig.url.startsWith('http')) {
|
|
30
|
+
try {
|
|
31
|
+
finalUrl = new URL(mergedConfig.url, mergedConfig.baseURL).toString();
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
throw new Error(`[etherreq] Invalid URL: ${mergedConfig.url} with base ${mergedConfig.baseURL}`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
8
37
|
// 应用请求拦截器
|
|
9
|
-
let processedConfig = { ...mergedConfig };
|
|
38
|
+
let processedConfig = { ...mergedConfig, url: finalUrl };
|
|
10
39
|
for (const interceptor of requestInterceptors) {
|
|
11
|
-
processedConfig = await interceptor(processedConfig);
|
|
40
|
+
processedConfig = await Promise.resolve(interceptor(processedConfig));
|
|
12
41
|
}
|
|
42
|
+
const { url: processedUrl, method: processedMethod, headers, body, ...rest } = processedConfig;
|
|
43
|
+
// ✅ 关键:只在非 GET/HEAD/OPTIONS 时发送 body
|
|
44
|
+
const hasBody = body != null &&
|
|
45
|
+
processedMethod !== 'GET' &&
|
|
46
|
+
processedMethod !== 'HEAD' &&
|
|
47
|
+
processedMethod !== 'OPTIONS';
|
|
13
48
|
try {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
headers: {
|
|
24
|
-
...(processedConfig.headers || {}),
|
|
25
|
-
},
|
|
26
|
-
// 只有非GET/HEAD/OPTIONS请求才发送body
|
|
27
|
-
body: hasBody ? JSON.stringify(processedConfig.body) : undefined,
|
|
28
|
-
});
|
|
29
|
-
// 检查响应状态
|
|
49
|
+
const fetchOptions = {
|
|
50
|
+
method: processedMethod,
|
|
51
|
+
headers: headers || {},
|
|
52
|
+
};
|
|
53
|
+
if (hasBody) {
|
|
54
|
+
// 自动序列化 JSON(可扩展支持 FormData 等)
|
|
55
|
+
fetchOptions.body = typeof body === 'string' ? body : JSON.stringify(body);
|
|
56
|
+
}
|
|
57
|
+
const response = await fetch(processedUrl, fetchOptions);
|
|
30
58
|
if (!response.ok) {
|
|
31
59
|
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
32
60
|
}
|
|
33
|
-
//
|
|
34
|
-
|
|
61
|
+
// 尝试解析 JSON,失败则返回文本
|
|
62
|
+
let data;
|
|
63
|
+
const contentType = response.headers.get('content-type');
|
|
64
|
+
if (contentType && contentType.includes('application/json')) {
|
|
65
|
+
data = await response.json();
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
data = await response.text();
|
|
69
|
+
}
|
|
35
70
|
let responseObject = {
|
|
36
71
|
data,
|
|
37
72
|
status: response.status,
|
|
38
73
|
statusText: response.statusText,
|
|
39
|
-
headers: response.headers,
|
|
74
|
+
headers: Object.fromEntries(response.headers.entries()),
|
|
40
75
|
config: processedConfig,
|
|
41
76
|
};
|
|
42
77
|
// 应用响应拦截器
|
|
43
78
|
for (const interceptor of responseInterceptors) {
|
|
44
|
-
responseObject = await interceptor(responseObject);
|
|
79
|
+
responseObject = await Promise.resolve(interceptor(responseObject));
|
|
45
80
|
}
|
|
46
81
|
return responseObject;
|
|
47
82
|
}
|
|
48
83
|
catch (error) {
|
|
49
|
-
// 如果是网络错误或其他类型的错误
|
|
50
84
|
throw new Error(`Request failed: ${error.message}`);
|
|
51
85
|
}
|
|
52
86
|
};
|
|
53
|
-
//
|
|
87
|
+
// 拦截器 API
|
|
54
88
|
client.interceptors = {
|
|
55
89
|
request: {
|
|
56
|
-
use: (
|
|
57
|
-
requestInterceptors.push(
|
|
90
|
+
use: (onFulfilled, onRejected) => {
|
|
91
|
+
requestInterceptors.push(onFulfilled);
|
|
58
92
|
},
|
|
59
93
|
},
|
|
60
94
|
response: {
|
|
61
|
-
use: (
|
|
62
|
-
responseInterceptors.push(
|
|
95
|
+
use: (onFulfilled, onRejected) => {
|
|
96
|
+
responseInterceptors.push(onFulfilled);
|
|
63
97
|
},
|
|
64
98
|
},
|
|
65
99
|
};
|
|
66
100
|
return client;
|
|
67
101
|
};
|
|
68
|
-
export {};
|
package/dist/index.js
CHANGED
|
@@ -2,33 +2,30 @@
|
|
|
2
2
|
import { request, baseURL as _baseURL, setBaseURL } from './request.js';
|
|
3
3
|
// 示例封装方法
|
|
4
4
|
const createMethod = (method) => (url, data, callback) => {
|
|
5
|
-
let options;
|
|
5
|
+
let options = { method }; // 修复:排除URL字段,明确指定method类型
|
|
6
6
|
if (data &&
|
|
7
7
|
typeof data === 'object' &&
|
|
8
8
|
!Array.isArray(data) &&
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
//
|
|
15
|
-
|
|
16
|
-
options = { params: data, method: method };
|
|
17
|
-
}
|
|
18
|
-
else {
|
|
19
|
-
options = { body: data, method: method };
|
|
20
|
-
}
|
|
9
|
+
method in { POST: 1, PUT: 1, PATCH: 1 }) {
|
|
10
|
+
// 有数据对象且为 POST/PUT/PATCH 请求
|
|
11
|
+
options = { ...options, params: data };
|
|
12
|
+
}
|
|
13
|
+
else if (data && typeof data !== 'function') {
|
|
14
|
+
// 有数据但不是对象(如原始类型)
|
|
15
|
+
options = { ...options, body: data };
|
|
21
16
|
}
|
|
22
17
|
else {
|
|
23
|
-
|
|
18
|
+
// 没有数据或数据是回调函数
|
|
19
|
+
if (typeof data === 'function')
|
|
20
|
+
callback = data;
|
|
24
21
|
}
|
|
25
22
|
const promise = request(url, options);
|
|
26
|
-
if (
|
|
27
|
-
promise
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
return promise;
|
|
23
|
+
if (callback) {
|
|
24
|
+
promise
|
|
25
|
+
.then(data => callback(null, data))
|
|
26
|
+
.catch(err => callback(err, null));
|
|
31
27
|
}
|
|
28
|
+
return promise;
|
|
32
29
|
};
|
|
33
30
|
export const etherreq = Object.assign((url, options, callback) => createMethod('GET')(url, options, callback), {
|
|
34
31
|
get: createMethod('GET'),
|
package/dist/request.js
CHANGED
|
@@ -3,11 +3,11 @@ import { create } from './etherreq.js';
|
|
|
3
3
|
import { requestCache, getCacheKey } from './cache.js';
|
|
4
4
|
// 创建一个带有默认配置的请求实例
|
|
5
5
|
const instance = create({
|
|
6
|
-
baseURL: 'https://api.example.com', // 默认基础 URL
|
|
6
|
+
baseURL: 'https://api.example.com', // 默认基础 URL(可被覆盖)
|
|
7
7
|
});
|
|
8
8
|
// 请求拦截器
|
|
9
9
|
instance.interceptors.request.use((config) => {
|
|
10
|
-
const token = localStorage.getItem('token');
|
|
10
|
+
const token = typeof localStorage !== 'undefined' ? localStorage.getItem('token') : null;
|
|
11
11
|
const headers = {
|
|
12
12
|
...(config.headers || {}),
|
|
13
13
|
Authorization: token ? `Bearer ${token}` : undefined,
|
|
@@ -56,51 +56,63 @@ let _baseURL = 'https://api.example.com'; // 内部变量用于保存 base URL
|
|
|
56
56
|
* 判断是否应该禁用缓存
|
|
57
57
|
*/
|
|
58
58
|
function shouldDisableCache(config) {
|
|
59
|
-
// 检查请求级别的缓存禁用
|
|
60
59
|
if (config.cache === false || config.disableCache) {
|
|
61
60
|
return true;
|
|
62
61
|
}
|
|
63
|
-
// 检查缓存配置
|
|
64
62
|
if (config.cache && config.cache.enabled === false) {
|
|
65
63
|
return true;
|
|
66
64
|
}
|
|
67
65
|
return false;
|
|
68
66
|
}
|
|
69
67
|
/**
|
|
70
|
-
*
|
|
68
|
+
* 获取缓存过期时间(毫秒)
|
|
71
69
|
*/
|
|
72
70
|
function getCacheTTL(config) {
|
|
73
71
|
if (config.cache && typeof config.cache.ttl === 'number') {
|
|
74
72
|
return config.cache.ttl;
|
|
75
73
|
}
|
|
76
|
-
return 5 * 60 * 1000; // 默认5分钟
|
|
74
|
+
return 5 * 60 * 1000; // 默认 5 分钟
|
|
77
75
|
}
|
|
78
76
|
/**
|
|
79
|
-
* 封装 request 函数,支持 baseURL
|
|
80
|
-
* @param url -
|
|
77
|
+
* 封装 request 函数,支持 baseURL 拼接、URL 校验等
|
|
78
|
+
* @param url - 请求路径(必须是非空字符串)
|
|
81
79
|
* @param options - 请求配置
|
|
82
80
|
*/
|
|
83
81
|
export const request = (url, options = {}) => {
|
|
84
|
-
//
|
|
85
|
-
|
|
86
|
-
|
|
82
|
+
// 🔴 关键:严格校验 url
|
|
83
|
+
if (typeof url !== 'string' || url.trim() === '') {
|
|
84
|
+
throw new Error(`[etherreq] Invalid URL: "${String(url)}". Expected a non-empty string.`);
|
|
85
|
+
}
|
|
86
|
+
// 确定 baseURL:优先使用 options.baseURL,其次全局 _baseURL,最后 fallback 到当前页面 origin(仅浏览器)
|
|
87
|
+
let baseURL = options.baseURL || _baseURL;
|
|
88
|
+
if (!baseURL && typeof window !== 'undefined') {
|
|
89
|
+
baseURL = window.location.origin;
|
|
90
|
+
}
|
|
91
|
+
// 构造最终 URL
|
|
87
92
|
let finalURL;
|
|
88
93
|
if (baseURL && !url.startsWith('http')) {
|
|
89
|
-
|
|
90
|
-
|
|
94
|
+
try {
|
|
95
|
+
finalURL = new URL(url, baseURL).toString();
|
|
96
|
+
}
|
|
97
|
+
catch (err) {
|
|
98
|
+
throw new Error(`[etherreq] Invalid URL combination: base="${baseURL}", path="${url}"`);
|
|
99
|
+
}
|
|
91
100
|
}
|
|
92
101
|
else {
|
|
93
|
-
// 如果 url 是绝对 URL 或者 baseURL 为空,则直接使用 url
|
|
94
102
|
finalURL = url;
|
|
95
103
|
}
|
|
96
104
|
return instance({
|
|
105
|
+
url: finalURL, // 明确指定url属性
|
|
97
106
|
...options,
|
|
98
|
-
url: finalURL,
|
|
99
107
|
});
|
|
100
108
|
};
|
|
101
|
-
//
|
|
102
|
-
export
|
|
103
|
-
//
|
|
109
|
+
// 导出 baseURL 供读取(只读)
|
|
110
|
+
export const baseURL = _baseURL;
|
|
111
|
+
// 允许外部设置全局 baseURL
|
|
104
112
|
export const setBaseURL = (newBaseURL) => {
|
|
105
|
-
|
|
113
|
+
if (typeof newBaseURL !== 'string' || newBaseURL.trim() === '') {
|
|
114
|
+
console.warn('[etherreq] setBaseURL received invalid value:', newBaseURL);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
_baseURL = newBaseURL.trim();
|
|
106
118
|
};
|
package/dist/types/etherreq.d.ts
CHANGED
|
@@ -1,18 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
import type { EtherRequestOptions } from './types/cache.js';
|
|
2
|
+
import type { RequestConfig as EtherRequestConfig } from './types/config.js';
|
|
3
|
+
/**
|
|
4
|
+
* 创建一个可配置的 HTTP 客户端
|
|
5
|
+
* @param defaultConfig - 默认配置(支持 baseURL, headers 等)
|
|
6
|
+
*/
|
|
7
|
+
export declare const create: (defaultConfig?: Partial<EtherRequestConfig>) => {
|
|
8
|
+
(config: EtherRequestOptions): Promise<any>;
|
|
9
9
|
interceptors: {
|
|
10
10
|
request: {
|
|
11
|
-
use: (
|
|
11
|
+
use: (onFulfilled: (config: any) => any, onRejected?: any) => void;
|
|
12
12
|
};
|
|
13
13
|
response: {
|
|
14
|
-
use: (
|
|
14
|
+
use: (onFulfilled: (response: any) => any, onRejected?: any) => void;
|
|
15
15
|
};
|
|
16
16
|
};
|
|
17
17
|
};
|
|
18
|
-
export {};
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { baseURL as _baseURL, setBaseURL } from './request.js';
|
|
2
2
|
import type { EtherRequestOptions } from './types/cache.js';
|
|
3
|
-
export declare const etherreq: ((url: string, options?: EtherRequestOptions, callback?: (error: Error | null, data: any) => void) => Promise<any>
|
|
4
|
-
get: (url: string, data?: any, callback?: (error: Error | null, data: any) => void) => Promise<any
|
|
5
|
-
post: (url: string, data?: any, callback?: (error: Error | null, data: any) => void) => Promise<any
|
|
6
|
-
put: (url: string, data?: any, callback?: (error: Error | null, data: any) => void) => Promise<any
|
|
7
|
-
delete: (url: string, data?: any, callback?: (error: Error | null, data: any) => void) => Promise<any
|
|
8
|
-
del: (url: string, data?: any, callback?: (error: Error | null, data: any) => void) => Promise<any
|
|
9
|
-
head: (url: string, data?: any, callback?: (error: Error | null, data: any) => void) => Promise<any
|
|
10
|
-
options: (url: string, data?: any, callback?: (error: Error | null, data: any) => void) => Promise<any
|
|
11
|
-
patch: (url: string, data?: any, callback?: (error: Error | null, data: any) => void) => Promise<any
|
|
3
|
+
export declare const etherreq: ((url: string, options?: EtherRequestOptions, callback?: (error: Error | null, data: any) => void) => Promise<any>) & {
|
|
4
|
+
get: (url: string, data?: any, callback?: (error: Error | null, data: any) => void) => Promise<any>;
|
|
5
|
+
post: (url: string, data?: any, callback?: (error: Error | null, data: any) => void) => Promise<any>;
|
|
6
|
+
put: (url: string, data?: any, callback?: (error: Error | null, data: any) => void) => Promise<any>;
|
|
7
|
+
delete: (url: string, data?: any, callback?: (error: Error | null, data: any) => void) => Promise<any>;
|
|
8
|
+
del: (url: string, data?: any, callback?: (error: Error | null, data: any) => void) => Promise<any>;
|
|
9
|
+
head: (url: string, data?: any, callback?: (error: Error | null, data: any) => void) => Promise<any>;
|
|
10
|
+
options: (url: string, data?: any, callback?: (error: Error | null, data: any) => void) => Promise<any>;
|
|
11
|
+
patch: (url: string, data?: any, callback?: (error: Error | null, data: any) => void) => Promise<any>;
|
|
12
12
|
login: (url: string, data?: any, callback?: (error: Error | null, data: any) => void) => Promise<any> | undefined;
|
|
13
13
|
};
|
|
14
14
|
export { setBaseURL, _baseURL as baseURL };
|
package/dist/types/request.d.ts
CHANGED
|
@@ -1,10 +1,18 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
1
|
+
import type { CacheOptions } from './types/cache.js';
|
|
2
|
+
interface RequestConfigWithoutUrl {
|
|
3
|
+
method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS';
|
|
4
|
+
headers?: Record<string, string>;
|
|
5
|
+
body?: any;
|
|
6
|
+
params?: Record<string, string | number | boolean>;
|
|
7
|
+
baseURL?: string;
|
|
8
|
+
cache?: CacheOptions | false;
|
|
9
|
+
}
|
|
3
10
|
/**
|
|
4
|
-
* 封装 request 函数,支持 baseURL
|
|
5
|
-
* @param url -
|
|
11
|
+
* 封装 request 函数,支持 baseURL 拼接、URL 校验等
|
|
12
|
+
* @param url - 请求路径(必须是非空字符串)
|
|
6
13
|
* @param options - 请求配置
|
|
7
14
|
*/
|
|
8
|
-
export declare const request: (url: string, options?:
|
|
9
|
-
export
|
|
15
|
+
export declare const request: (url: string, options?: RequestConfigWithoutUrl) => Promise<any>;
|
|
16
|
+
export declare const baseURL: string;
|
|
10
17
|
export declare const setBaseURL: (newBaseURL: string) => void;
|
|
18
|
+
export {};
|