@blueking/chat-helper 0.0.4 → 0.0.5
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/README.md +10 -0
- package/dist/http/fetch/fetch.d.ts +6 -4
- package/dist/http/fetch/fetch.ts.js +3 -3
- package/dist/http/fetch/index.d.ts +3 -0
- package/dist/http/fetch/index.ts.js +40 -25
- package/dist/http/fetch/resolve-request-value.d.ts +16 -0
- package/dist/http/fetch/resolve-request-value.ts.js +38 -0
- package/dist/type.d.ts +5 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -357,10 +357,20 @@ useChatHelper({
|
|
|
357
357
|
Authorization: `Bearer ${getToken()}`,
|
|
358
358
|
'X-Request-ID': generateRequestId(),
|
|
359
359
|
}),
|
|
360
|
+
|
|
361
|
+
// 也支持 Vue ref / computed(每次请求读取最新值)
|
|
362
|
+
// headers: computed(() => ({ Authorization: `Bearer ${token.value}` })),
|
|
363
|
+
// data: ref({ app_id: currentAppId.value }),
|
|
360
364
|
},
|
|
361
365
|
});
|
|
362
366
|
```
|
|
363
367
|
|
|
368
|
+
**响应式写法说明**:
|
|
369
|
+
|
|
370
|
+
- `headers` / `data` 支持:普通对象、零参函数、`ref`、`computed`,以及「函数返回 ref/computed」。
|
|
371
|
+
- 每次发起 HTTP 请求前都会重新解析,修改 `.value` 或替换外层 ref 后,后续请求自动使用新值。
|
|
372
|
+
- `data` 对 POST/PUT/PATCH/DELETE 合并进请求体;对 GET/HEAD/OPTIONS 合并进 query(`params`),不会写入 body。
|
|
373
|
+
|
|
364
374
|
### interceptors(可选)
|
|
365
375
|
|
|
366
376
|
配置请求和响应拦截器,用于统一处理请求和响应。
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { MaybeRequestValue, RequestHeaders } from './resolve-request-value';
|
|
2
|
+
export type { MaybeRequestValue, RequestData, RequestHeaders } from './resolve-request-value';
|
|
1
3
|
export interface ApiResponse<T = unknown> {
|
|
2
4
|
code: number | string;
|
|
3
5
|
data: T;
|
|
@@ -8,10 +10,10 @@ export interface IRequestConfig {
|
|
|
8
10
|
baseURL?: string;
|
|
9
11
|
controller?: AbortController;
|
|
10
12
|
credentials?: 'include' | 'omit' | 'same-origin';
|
|
11
|
-
/**
|
|
12
|
-
data?:
|
|
13
|
-
/**
|
|
14
|
-
headers?:
|
|
13
|
+
/** 请求体;支持对象/函数/ref 延迟求值(在 `prepareRequest` 中解析) */
|
|
14
|
+
data?: MaybeRequestValue<unknown>;
|
|
15
|
+
/** 请求头;支持对象/函数/ref 延迟求值,便于动态注入(如 token) */
|
|
16
|
+
headers?: MaybeRequestValue<RequestHeaders>;
|
|
15
17
|
method?: string;
|
|
16
18
|
mode?: 'cors' | 'no-cors' | 'same-origin';
|
|
17
19
|
params?: Record<string, unknown>;
|
|
@@ -22,8 +22,7 @@
|
|
|
22
22
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
|
23
23
|
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
24
24
|
* IN THE SOFTWARE.
|
|
25
|
-
*/
|
|
26
|
-
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
|
|
25
|
+
*/ function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
|
|
27
26
|
try {
|
|
28
27
|
var info = gen[key](arg);
|
|
29
28
|
var value = info.value;
|
|
@@ -104,6 +103,7 @@ function _object_spread_props(target, source) {
|
|
|
104
103
|
}
|
|
105
104
|
return target;
|
|
106
105
|
}
|
|
106
|
+
import { resolveRequestValue } from './resolve-request-value.ts.js';
|
|
107
107
|
class InterceptorManager {
|
|
108
108
|
clear() {
|
|
109
109
|
this.handlers = [];
|
|
@@ -463,7 +463,7 @@ function createError(message, config, code, response) {
|
|
|
463
463
|
return error;
|
|
464
464
|
}
|
|
465
465
|
function getValue(value) {
|
|
466
|
-
return
|
|
466
|
+
return resolveRequestValue(value);
|
|
467
467
|
}
|
|
468
468
|
/** 排除 AbortController、Headers 等类实例,只对普通对象深度合并 */ function isPlainObject(value) {
|
|
469
469
|
if (!value || typeof value !== 'object') return false;
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { FetchClient } from './fetch';
|
|
2
2
|
import type { IUseChatHelperOptions } from '../../type';
|
|
3
|
+
export { FetchClient } from './fetch';
|
|
3
4
|
export type * from './fetch';
|
|
5
|
+
export type { MaybeRequestValue, RequestData, RequestHeaders } from './resolve-request-value';
|
|
6
|
+
export { resolveRequestValue } from './resolve-request-value';
|
|
4
7
|
export declare const useFetch: (options: IUseChatHelperOptions) => {
|
|
5
8
|
fetchClient: FetchClient;
|
|
6
9
|
reset: (newOptions: IUseChatHelperOptions) => void;
|
|
@@ -75,23 +75,29 @@ function _object_spread_props(target, source) {
|
|
|
75
75
|
return target;
|
|
76
76
|
}
|
|
77
77
|
import { FetchClient } from './fetch.ts.js';
|
|
78
|
-
|
|
78
|
+
import { resolveRequestValue } from './resolve-request-value.ts.js';
|
|
79
|
+
export { FetchClient } from './fetch.ts.js';
|
|
80
|
+
export { resolveRequestValue } from './resolve-request-value.ts.js';
|
|
81
|
+
/** 使用请求体的 HTTP 方法 */ const BODY_METHODS = new Set([
|
|
79
82
|
'POST',
|
|
80
83
|
'PUT',
|
|
81
84
|
'PATCH',
|
|
82
85
|
'DELETE'
|
|
83
86
|
]);
|
|
87
|
+
/** 无请求体、通过 query 传参的 HTTP 方法 */ const QUERY_METHODS = new Set([
|
|
88
|
+
'GET',
|
|
89
|
+
'HEAD',
|
|
90
|
+
'OPTIONS'
|
|
91
|
+
]);
|
|
84
92
|
/** 排除 FormData / Blob / ArrayBuffer 等,只对普通对象展开合并 */ function isPlainObject(value) {
|
|
85
93
|
if (!value || typeof value !== 'object') return false;
|
|
86
94
|
const proto = Object.getPrototypeOf(value);
|
|
87
95
|
return proto === Object.prototype || proto === null;
|
|
88
96
|
}
|
|
89
|
-
/** 解析「函数 | 值」两种形式,支持延迟求值(如响应式 token) */ function resolveValue(value) {
|
|
90
|
-
return typeof value === 'function' ? value() : value;
|
|
91
|
-
}
|
|
92
97
|
/**
|
|
93
98
|
* 注册全局默认请求配置拦截器,将 `requestData.headers/data` 自动合并到每次请求。
|
|
94
|
-
* - `headers`
|
|
99
|
+
* - `headers` 注入所有请求
|
|
100
|
+
* - `data` 对 POST/PUT/PATCH/DELETE 合并进 body;对 GET/HEAD/OPTIONS 合并进 query(params)
|
|
95
101
|
* - 优先级最低(单次 IRequestConfig > 用户拦截器 > 本拦截器)。
|
|
96
102
|
*/ function registerRequestDataInterceptor(client, opts) {
|
|
97
103
|
const { headers: extraHeadersFn, data: extraDataFn } = opts.requestData;
|
|
@@ -99,10 +105,10 @@ import { FetchClient } from './fetch.ts.js';
|
|
|
99
105
|
client.interceptors.request.use((config)=>{
|
|
100
106
|
let result = config;
|
|
101
107
|
if (extraHeadersFn) {
|
|
102
|
-
const extra =
|
|
108
|
+
const extra = resolveRequestValue(extraHeadersFn);
|
|
103
109
|
if (extra && Object.keys(extra).length > 0) {
|
|
104
|
-
var
|
|
105
|
-
const existing = (
|
|
110
|
+
var _resolveRequestValue;
|
|
111
|
+
const existing = (_resolveRequestValue = resolveRequestValue(config.headers)) !== null && _resolveRequestValue !== void 0 ? _resolveRequestValue : {};
|
|
106
112
|
result = _object_spread_props(_object_spread({}, result), {
|
|
107
113
|
headers: _object_spread({}, existing, extra)
|
|
108
114
|
});
|
|
@@ -110,25 +116,34 @@ import { FetchClient } from './fetch.ts.js';
|
|
|
110
116
|
}
|
|
111
117
|
var _config_method;
|
|
112
118
|
const method = ((_config_method = config.method) !== null && _config_method !== void 0 ? _config_method : 'GET').toUpperCase();
|
|
113
|
-
if (extraDataFn
|
|
114
|
-
const extra =
|
|
119
|
+
if (extraDataFn) {
|
|
120
|
+
const extra = resolveRequestValue(extraDataFn);
|
|
115
121
|
if (extra && Object.keys(extra).length > 0) {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
122
|
+
if (BODY_METHODS.has(method)) {
|
|
123
|
+
const existing = resolveRequestValue(config.data);
|
|
124
|
+
if (existing == null) {
|
|
125
|
+
// body 为空(如 clearSession POST undefined):直接用 extra 作为 body
|
|
126
|
+
result = _object_spread_props(_object_spread({}, result), {
|
|
127
|
+
data: extra
|
|
128
|
+
});
|
|
129
|
+
} else if (isPlainObject(existing)) {
|
|
130
|
+
// body 是普通对象:浅合并,extra 字段优先级更低(existing 已有同名字段时保留)
|
|
131
|
+
result = _object_spread_props(_object_spread({}, result), {
|
|
132
|
+
data: _object_spread({}, extra, existing)
|
|
133
|
+
});
|
|
134
|
+
} else {
|
|
135
|
+
// body 是 FormData / Blob / string 等:跳过注入,避免破坏原始 body
|
|
136
|
+
console.warn('[chat-helper] requestData.data 无法注入:当前请求体不是普通对象(FormData/Blob/string 等),已跳过合并。', {
|
|
137
|
+
method,
|
|
138
|
+
existingType: typeof existing
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
} else if (QUERY_METHODS.has(method)) {
|
|
142
|
+
var _config_params;
|
|
143
|
+
const existingParams = (_config_params = config.params) !== null && _config_params !== void 0 ? _config_params : {};
|
|
144
|
+
// query 浅合并,extra 优先级更低(单次请求的 params 同名字段优先)
|
|
119
145
|
result = _object_spread_props(_object_spread({}, result), {
|
|
120
|
-
|
|
121
|
-
});
|
|
122
|
-
} else if (isPlainObject(existing)) {
|
|
123
|
-
// body 是普通对象:浅合并,extra 字段优先级更低(existing 已有同名字段时保留)
|
|
124
|
-
result = _object_spread_props(_object_spread({}, result), {
|
|
125
|
-
data: _object_spread({}, extra, existing)
|
|
126
|
-
});
|
|
127
|
-
} else {
|
|
128
|
-
// body 是 FormData / Blob / string 等:跳过注入,避免破坏原始 body
|
|
129
|
-
console.warn('[chat-helper] requestData.data 无法注入:当前请求体不是普通对象(FormData/Blob/string 等),已跳过合并。', {
|
|
130
|
-
method,
|
|
131
|
-
existingType: typeof existing
|
|
146
|
+
params: _object_spread({}, extra, existingParams)
|
|
132
147
|
});
|
|
133
148
|
}
|
|
134
149
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** 请求头键值对 */
|
|
2
|
+
export type RequestHeaders = Record<string, string>;
|
|
3
|
+
/** 请求体附加数据键值对 */
|
|
4
|
+
export type RequestData = Record<string, unknown>;
|
|
5
|
+
/**
|
|
6
|
+
* 支持延迟求值的请求配置值:
|
|
7
|
+
* - 普通对象
|
|
8
|
+
* - 零参函数(可返回对象、ref、computed)
|
|
9
|
+
* - Ref / ComputedRef / MaybeRef
|
|
10
|
+
*/
|
|
11
|
+
export type MaybeRequestValue<T> = T | (() => MaybeRequestValue<T>);
|
|
12
|
+
/**
|
|
13
|
+
* 解析请求配置值,在每次请求前调用以读取最新值。
|
|
14
|
+
* 递归展开函数、ref、computed,直到得到最终对象或原始值。
|
|
15
|
+
*/
|
|
16
|
+
export declare function resolveRequestValue<T>(value: MaybeRequestValue<T> | undefined): T | undefined;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Tencent is pleased to support the open source community by making
|
|
3
|
+
* 蓝鲸智云PaaS平台 (BlueKing PaaS) available.
|
|
4
|
+
*
|
|
5
|
+
* Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.
|
|
6
|
+
*
|
|
7
|
+
* 蓝鲸智云PaaS平台 (BlueKing PaaS) is licensed under the MIT License.
|
|
8
|
+
*/ import { isRef, unref } from 'vue';
|
|
9
|
+
function isVueRef(value) {
|
|
10
|
+
return isRef(value);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* 解析请求配置值,在每次请求前调用以读取最新值。
|
|
14
|
+
* 递归展开函数、ref、computed,直到得到最终对象或原始值。
|
|
15
|
+
*/ export function resolveRequestValue(value) {
|
|
16
|
+
if (value === undefined || value === null) {
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
let current = value;
|
|
20
|
+
const maxDepth = 32;
|
|
21
|
+
let depth = 0;
|
|
22
|
+
while(depth < maxDepth){
|
|
23
|
+
depth += 1;
|
|
24
|
+
if (typeof current === 'function') {
|
|
25
|
+
current = current();
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
if (isVueRef(current)) {
|
|
29
|
+
current = unref(current);
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
if (depth >= maxDepth) {
|
|
35
|
+
console.warn('[chat-helper] resolveRequestValue: exceeded max unwrap depth');
|
|
36
|
+
}
|
|
37
|
+
return current;
|
|
38
|
+
}
|
package/dist/type.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { IRequestConfig, ISSEProtocol } from './http/fetch';
|
|
2
2
|
import type { IResponse } from './http/fetch';
|
|
3
|
+
import type { MaybeRequestValue, RequestData, RequestHeaders } from './http/fetch/resolve-request-value';
|
|
4
|
+
export type { MaybeRequestValue, RequestData, RequestHeaders } from './http/fetch/resolve-request-value';
|
|
3
5
|
export interface IUseChatHelperOptions {
|
|
4
6
|
protocol?: ISSEProtocol;
|
|
5
7
|
/** 自定义拦截器,优先级高于内置 requestData 拦截器 */
|
|
@@ -7,10 +9,10 @@ export interface IUseChatHelperOptions {
|
|
|
7
9
|
request?: (config: IRequestConfig) => IRequestConfig;
|
|
8
10
|
response?: (response: IResponse) => IResponse;
|
|
9
11
|
};
|
|
10
|
-
/**
|
|
12
|
+
/** 全局默认请求配置,通过内置拦截器自动合并到每次请求(支持对象/函数/ref 延迟求值) */
|
|
11
13
|
requestData: {
|
|
12
|
-
data?:
|
|
13
|
-
headers?:
|
|
14
|
+
data?: MaybeRequestValue<RequestData>;
|
|
15
|
+
headers?: MaybeRequestValue<RequestHeaders>;
|
|
14
16
|
urlPrefix: string;
|
|
15
17
|
};
|
|
16
18
|
}
|