@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 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
- /** 请求体;可传函数以便延迟求值(在 `prepareRequest` 中通过 `getValue` 解析) */
12
- data?: (() => unknown) | unknown;
13
- /** 请求头;可传函数以便延迟求值,便于动态注入(如 token) */
14
- headers?: (() => Record<string, string>) | Record<string, string>;
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
- */ // API 标准响应格式
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 typeof value === 'function' ? value() : value;
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
- /** 仅支持 body HTTP 方法 */ const BODY_METHODS = new Set([
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` 注入所有请求;`data` 仅注入 POST/PUT/PATCH/DELETE,避免 GET/HEAD body 报错。
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 = resolveValue(extraHeadersFn);
108
+ const extra = resolveRequestValue(extraHeadersFn);
103
109
  if (extra && Object.keys(extra).length > 0) {
104
- var _resolveValue;
105
- const existing = (_resolveValue = resolveValue(config.headers)) !== null && _resolveValue !== void 0 ? _resolveValue : {};
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 && BODY_METHODS.has(method)) {
114
- const extra = resolveValue(extraDataFn);
119
+ if (extraDataFn) {
120
+ const extra = resolveRequestValue(extraDataFn);
115
121
  if (extra && Object.keys(extra).length > 0) {
116
- const existing = resolveValue(config.data);
117
- if (existing == null) {
118
- // body 为空(如 clearSession POST undefined):直接用 extra 作为 body
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
- data: extra
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?: (() => Record<string, unknown>) | Record<string, unknown>;
13
- headers?: (() => Record<string, string>) | Record<string, string>;
14
+ data?: MaybeRequestValue<RequestData>;
15
+ headers?: MaybeRequestValue<RequestHeaders>;
14
16
  urlPrefix: string;
15
17
  };
16
18
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blueking/chat-helper",
3
- "version": "0.0.4",
3
+ "version": "0.0.5",
4
4
  "description": "",
5
5
  "main": "./dist/index.ts.js",
6
6
  "types": "./dist/index.d.ts",