@kevisual/query 0.0.46 → 0.0.48

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.
@@ -13,14 +13,6 @@ type AdapterOpts = {
13
13
  body?: Record<string, any> | FormData;
14
14
  timeout?: number;
15
15
  method?: Method;
16
- /**
17
- * @deprecated use responseType
18
- */
19
- isBlob?: boolean;
20
- /**
21
- * @deprecated use responseType
22
- */
23
- isText?: boolean;
24
16
  /**
25
17
  * 响应类型,
26
18
  * */
@@ -118,6 +110,8 @@ type QueryOptions = {
118
110
  headers?: Record<string, string>;
119
111
  timeout?: number;
120
112
  isClient?: boolean;
113
+ tokenName?: string;
114
+ storage?: Storage;
121
115
  beforeRequest?: Fn;
122
116
  };
123
117
  type Data = {
@@ -148,10 +142,7 @@ declare const wrapperError: ({ code, message }: {
148
142
  message?: string;
149
143
  }) => {
150
144
  code: number;
151
- success: boolean;
152
145
  message: string;
153
- showError: (fn?: () => void) => void;
154
- noMsg: boolean;
155
146
  };
156
147
  /**
157
148
  * const query = new Query();
@@ -180,10 +171,9 @@ declare class Query {
180
171
  */
181
172
  stop?: boolean;
182
173
  qws: QueryWs;
183
- /**
184
- * 默认是 /client/router或者 默认是 /api/router
185
- */
186
- isClient: boolean;
174
+ tokenName: string;
175
+ storage: Storage;
176
+ token: string;
187
177
  constructor(opts?: QueryOptions);
188
178
  setQueryWs(qws: QueryWs): void;
189
179
  /**
@@ -251,18 +241,10 @@ type QueryOpts = {
251
241
  * 前端调用后端QueryRouter, 封装 beforeRequest 和 wss
252
242
  */
253
243
  declare class QueryClient extends Query {
254
- tokenName: string;
255
- storage: Storage;
256
- token: string;
257
244
  constructor(opts?: QueryOptions & {
258
- tokenName?: string;
259
- storage?: Storage;
260
245
  io?: boolean;
261
246
  });
262
247
  createWs(opts?: QueryWsOpts): void;
263
- getToken(): string;
264
- saveToken(token: string): void;
265
- removeToken(): void;
266
248
  }
267
249
 
268
250
  export { BaseQuery, Query, QueryClient, QueryWs, adapter, wrapperError };
@@ -2,19 +2,13 @@
2
2
  var isTextForContentType = (contentType) => {
3
3
  if (!contentType)
4
4
  return false;
5
- const textTypes = ["text/", "xml", "html", "javascript", "css", "csv", "plain", "x-www-form-urlencoded", "md"];
5
+ const textTypes = ["text/", "xml", "html", "javascript", "css", "csv", "plain", "x-www-form-urlencoded", "md", "json"];
6
6
  return textTypes.some((type) => contentType.includes(type));
7
7
  };
8
8
  var adapter = async (opts = {}, overloadOpts) => {
9
9
  const controller = new AbortController;
10
10
  const signal = controller.signal;
11
11
  const isPostFile = opts.isPostFile || false;
12
- let responseType = opts.responseType || "json";
13
- if (opts.isBlob) {
14
- responseType = "blob";
15
- } else if (opts.isText) {
16
- responseType = "text";
17
- }
18
12
  const timeout = opts.timeout || 60000 * 3;
19
13
  const timer = setTimeout(() => {
20
14
  controller.abort();
@@ -73,21 +67,31 @@ var adapter = async (opts = {}, overloadOpts) => {
73
67
  headers
74
68
  }).then(async (response) => {
75
69
  const contentType = response.headers.get("Content-Type");
76
- if (responseType === "blob") {
77
- return await response.blob();
78
- }
79
- const isText = responseType === "text";
80
70
  const isJson = contentType && contentType.includes("application/json");
81
- if (isJson && !isText) {
82
- return await response.json();
71
+ const isSuccess = response.ok;
72
+ if (isJson) {
73
+ const json = await response.json();
74
+ if (json?.code) {
75
+ return json;
76
+ }
77
+ return {
78
+ code: isSuccess ? 200 : response.status,
79
+ status: response.status,
80
+ data: json
81
+ };
83
82
  } else if (isTextForContentType(contentType)) {
84
83
  return {
85
- code: response.status,
84
+ code: isSuccess ? 200 : response.status,
86
85
  status: response.status,
87
86
  data: await response.text()
88
87
  };
89
88
  } else {
90
- return response;
89
+ return {
90
+ code: isSuccess ? 200 : response.status,
91
+ status: response.status,
92
+ data: "非文本非JSON响应, 请手动处理response。",
93
+ response
94
+ };
91
95
  }
92
96
  }).catch((err) => {
93
97
  if (err.name === "AbortError") {
@@ -306,10 +310,7 @@ class QueryWs {
306
310
  var wrapperError = ({ code, message }) => {
307
311
  const result = {
308
312
  code: code || 500,
309
- success: false,
310
- message: message || "api request error",
311
- showError: (fn) => {},
312
- noMsg: true
313
+ message: message || "请求错误"
313
314
  };
314
315
  return result;
315
316
  };
@@ -323,9 +324,13 @@ class Query {
323
324
  timeout;
324
325
  stop;
325
326
  qws;
326
- isClient = false;
327
+ tokenName;
328
+ storage;
329
+ token;
327
330
  constructor(opts) {
328
331
  this.adapter = opts?.adapter || adapter;
332
+ this.tokenName = opts?.tokenName || "token";
333
+ this.storage = opts?.storage || globalThis?.localStorage;
329
334
  const defaultURL = opts?.isClient ? "/client/router" : "/api/router";
330
335
  this.url = opts?.url || defaultURL;
331
336
  this.headers = opts?.headers || {
@@ -336,7 +341,7 @@ class Query {
336
341
  this.beforeRequest = opts.beforeRequest;
337
342
  } else {
338
343
  this.beforeRequest = async (opts2) => {
339
- const token = globalThis?.localStorage?.getItem("token");
344
+ const token = this.token || this.storage?.getItem?.(this.tokenName);
340
345
  if (token) {
341
346
  opts2.headers = {
342
347
  ...opts2.headers,
@@ -358,7 +363,6 @@ class Query {
358
363
  }
359
364
  async post(body, options) {
360
365
  const url = options?.url || this.url;
361
- console.log("query post", url, body, options);
362
366
  const { headers, adapter: adapter2, beforeRequest, afterResponse, timeout, ...rest } = options || {};
363
367
  const _headers = { ...this.headers, ...headers };
364
368
  const _adapter = adapter2 || this.adapter;
@@ -378,7 +382,7 @@ class Query {
378
382
  if (res === false) {
379
383
  return wrapperError({
380
384
  code: 500,
381
- message: "request is cancel",
385
+ message: "请求取消",
382
386
  req
383
387
  });
384
388
  }
@@ -387,13 +391,13 @@ class Query {
387
391
  console.error("request beforeFn error", e, req);
388
392
  return wrapperError({
389
393
  code: 500,
390
- message: "api request beforeFn error",
394
+ message: "请求在请求前处理时发生错误",
391
395
  req
392
396
  });
393
397
  }
394
398
  if (this.stop && !options?.noStop) {
395
399
  const that = this;
396
- await new Promise((resolve) => {
400
+ const res = await new Promise((resolve) => {
397
401
  let timer = 0;
398
402
  const detect = setInterval(() => {
399
403
  if (!that.stop) {
@@ -401,11 +405,20 @@ class Query {
401
405
  resolve(true);
402
406
  }
403
407
  timer++;
404
- if (timer > 30) {
405
- console.error("request stop: timeout", req.url, timer);
408
+ if (timer > 5) {
409
+ console.error("等待请求失败:", req.url, timer);
410
+ clearInterval(detect);
411
+ resolve(false);
406
412
  }
407
413
  }, 1000);
408
414
  });
415
+ if (!res) {
416
+ return wrapperError({
417
+ code: 500,
418
+ message: "请求取消,可能是因为用户未登录或者token过期",
419
+ req
420
+ });
421
+ }
409
422
  }
410
423
  return _adapter(req).then(async (res) => {
411
424
  try {
@@ -418,10 +431,10 @@ class Query {
418
431
  }
419
432
  return res;
420
433
  } catch (e) {
421
- console.error("request afterFn error", e, req);
434
+ console.error("请求在响应后处理时发生错误", e, req);
422
435
  return wrapperError({
423
436
  code: 500,
424
- message: "api request afterFn error",
437
+ message: "请求在响应后处理时发生错误",
425
438
  req
426
439
  });
427
440
  }
@@ -485,23 +498,8 @@ class BaseQuery {
485
498
 
486
499
  // src/query-browser.ts
487
500
  class QueryClient extends Query {
488
- tokenName;
489
- storage;
490
- token;
491
501
  constructor(opts) {
492
502
  super(opts);
493
- this.tokenName = opts?.tokenName || "token";
494
- this.storage = opts?.storage || globalThis.localStorage;
495
- this.beforeRequest = async (opts2) => {
496
- const token = this.token || this.getToken();
497
- if (token) {
498
- opts2.headers = {
499
- ...opts2.headers,
500
- Authorization: `Bearer ${token}`
501
- };
502
- }
503
- return opts2;
504
- };
505
503
  if (opts?.io) {
506
504
  this.createWs();
507
505
  }
@@ -509,15 +507,6 @@ class QueryClient extends Query {
509
507
  createWs(opts) {
510
508
  this.qws = new QueryWs({ url: this.url, ...opts });
511
509
  }
512
- getToken() {
513
- return this.storage.getItem(this.tokenName);
514
- }
515
- saveToken(token) {
516
- this.storage.setItem(this.tokenName, token);
517
- }
518
- removeToken() {
519
- this.storage.removeItem(this.tokenName);
520
- }
521
510
  }
522
511
  export {
523
512
  wrapperError,
package/dist/query.d.ts CHANGED
@@ -13,14 +13,6 @@ type AdapterOpts = {
13
13
  body?: Record<string, any> | FormData;
14
14
  timeout?: number;
15
15
  method?: Method;
16
- /**
17
- * @deprecated use responseType
18
- */
19
- isBlob?: boolean;
20
- /**
21
- * @deprecated use responseType
22
- */
23
- isText?: boolean;
24
16
  /**
25
17
  * 响应类型,
26
18
  * */
@@ -118,6 +110,8 @@ type QueryOptions = {
118
110
  headers?: Record<string, string>;
119
111
  timeout?: number;
120
112
  isClient?: boolean;
113
+ tokenName?: string;
114
+ storage?: Storage;
121
115
  beforeRequest?: Fn;
122
116
  };
123
117
  type Data = {
@@ -143,26 +137,12 @@ type DataOpts = Partial<QueryOpts> & {
143
137
  */
144
138
  noStop?: boolean;
145
139
  };
146
- /**
147
- * 设置基础响应, 设置 success 和 showError,
148
- * success 是 code 是否等于 200
149
- * showError 是 如果 success 为 false 且 noMsg 为 false, 则调用 showError
150
- * @param res 响应
151
- */
152
- declare const setBaseResponse: (res: Partial<Result & {
153
- success?: boolean;
154
- showError?: (fn?: () => void) => void;
155
- noMsg?: boolean;
156
- }>) => Result;
157
140
  declare const wrapperError: ({ code, message }: {
158
141
  code?: number;
159
142
  message?: string;
160
143
  }) => {
161
144
  code: number;
162
- success: boolean;
163
145
  message: string;
164
- showError: (fn?: () => void) => void;
165
- noMsg: boolean;
166
146
  };
167
147
  /**
168
148
  * const query = new Query();
@@ -191,10 +171,9 @@ declare class Query {
191
171
  */
192
172
  stop?: boolean;
193
173
  qws: QueryWs;
194
- /**
195
- * 默认是 /client/router或者 默认是 /api/router
196
- */
197
- isClient: boolean;
174
+ tokenName: string;
175
+ storage: Storage;
176
+ token: string;
198
177
  constructor(opts?: QueryOptions);
199
178
  setQueryWs(qws: QueryWs): void;
200
179
  /**
@@ -251,5 +230,5 @@ declare class BaseQuery<T extends Query = Query, R extends {
251
230
  get<R = any, P = any>(data: P, options?: DataOpts): Promise<Result<R>>;
252
231
  }
253
232
 
254
- export { BaseQuery, Query, adapter, setBaseResponse, wrapperError };
233
+ export { BaseQuery, Query, adapter, wrapperError };
255
234
  export type { Data, DataOpts, Fn, QueryOptions, QueryOpts, Result };
package/dist/query.js CHANGED
@@ -2,19 +2,13 @@
2
2
  var isTextForContentType = (contentType) => {
3
3
  if (!contentType)
4
4
  return false;
5
- const textTypes = ["text/", "xml", "html", "javascript", "css", "csv", "plain", "x-www-form-urlencoded", "md"];
5
+ const textTypes = ["text/", "xml", "html", "javascript", "css", "csv", "plain", "x-www-form-urlencoded", "md", "json"];
6
6
  return textTypes.some((type) => contentType.includes(type));
7
7
  };
8
8
  var adapter = async (opts = {}, overloadOpts) => {
9
9
  const controller = new AbortController;
10
10
  const signal = controller.signal;
11
11
  const isPostFile = opts.isPostFile || false;
12
- let responseType = opts.responseType || "json";
13
- if (opts.isBlob) {
14
- responseType = "blob";
15
- } else if (opts.isText) {
16
- responseType = "text";
17
- }
18
12
  const timeout = opts.timeout || 60000 * 3;
19
13
  const timer = setTimeout(() => {
20
14
  controller.abort();
@@ -73,21 +67,31 @@ var adapter = async (opts = {}, overloadOpts) => {
73
67
  headers
74
68
  }).then(async (response) => {
75
69
  const contentType = response.headers.get("Content-Type");
76
- if (responseType === "blob") {
77
- return await response.blob();
78
- }
79
- const isText = responseType === "text";
80
70
  const isJson = contentType && contentType.includes("application/json");
81
- if (isJson && !isText) {
82
- return await response.json();
71
+ const isSuccess = response.ok;
72
+ if (isJson) {
73
+ const json = await response.json();
74
+ if (json?.code) {
75
+ return json;
76
+ }
77
+ return {
78
+ code: isSuccess ? 200 : response.status,
79
+ status: response.status,
80
+ data: json
81
+ };
83
82
  } else if (isTextForContentType(contentType)) {
84
83
  return {
85
- code: response.status,
84
+ code: isSuccess ? 200 : response.status,
86
85
  status: response.status,
87
86
  data: await response.text()
88
87
  };
89
88
  } else {
90
- return response;
89
+ return {
90
+ code: isSuccess ? 200 : response.status,
91
+ status: response.status,
92
+ data: "非文本非JSON响应, 请手动处理response。",
93
+ response
94
+ };
91
95
  }
92
96
  }).catch((err) => {
93
97
  if (err.name === "AbortError") {
@@ -106,22 +110,10 @@ var adapter = async (opts = {}, overloadOpts) => {
106
110
  };
107
111
 
108
112
  // src/query.ts
109
- var setBaseResponse = (res) => {
110
- res.success = res.code === 200;
111
- res.showError = (fn) => {
112
- if (!res.success && !res.noMsg) {
113
- fn?.();
114
- }
115
- };
116
- return res;
117
- };
118
113
  var wrapperError = ({ code, message }) => {
119
114
  const result = {
120
115
  code: code || 500,
121
- success: false,
122
- message: message || "api request error",
123
- showError: (fn) => {},
124
- noMsg: true
116
+ message: message || "请求错误"
125
117
  };
126
118
  return result;
127
119
  };
@@ -135,9 +127,13 @@ class Query {
135
127
  timeout;
136
128
  stop;
137
129
  qws;
138
- isClient = false;
130
+ tokenName;
131
+ storage;
132
+ token;
139
133
  constructor(opts) {
140
134
  this.adapter = opts?.adapter || adapter;
135
+ this.tokenName = opts?.tokenName || "token";
136
+ this.storage = opts?.storage || globalThis?.localStorage;
141
137
  const defaultURL = opts?.isClient ? "/client/router" : "/api/router";
142
138
  this.url = opts?.url || defaultURL;
143
139
  this.headers = opts?.headers || {
@@ -148,7 +144,7 @@ class Query {
148
144
  this.beforeRequest = opts.beforeRequest;
149
145
  } else {
150
146
  this.beforeRequest = async (opts2) => {
151
- const token = globalThis?.localStorage?.getItem("token");
147
+ const token = this.token || this.storage?.getItem?.(this.tokenName);
152
148
  if (token) {
153
149
  opts2.headers = {
154
150
  ...opts2.headers,
@@ -170,7 +166,6 @@ class Query {
170
166
  }
171
167
  async post(body, options) {
172
168
  const url = options?.url || this.url;
173
- console.log("query post", url, body, options);
174
169
  const { headers, adapter: adapter2, beforeRequest, afterResponse, timeout, ...rest } = options || {};
175
170
  const _headers = { ...this.headers, ...headers };
176
171
  const _adapter = adapter2 || this.adapter;
@@ -190,7 +185,7 @@ class Query {
190
185
  if (res === false) {
191
186
  return wrapperError({
192
187
  code: 500,
193
- message: "request is cancel",
188
+ message: "请求取消",
194
189
  req
195
190
  });
196
191
  }
@@ -199,13 +194,13 @@ class Query {
199
194
  console.error("request beforeFn error", e, req);
200
195
  return wrapperError({
201
196
  code: 500,
202
- message: "api request beforeFn error",
197
+ message: "请求在请求前处理时发生错误",
203
198
  req
204
199
  });
205
200
  }
206
201
  if (this.stop && !options?.noStop) {
207
202
  const that = this;
208
- await new Promise((resolve) => {
203
+ const res = await new Promise((resolve) => {
209
204
  let timer = 0;
210
205
  const detect = setInterval(() => {
211
206
  if (!that.stop) {
@@ -213,11 +208,20 @@ class Query {
213
208
  resolve(true);
214
209
  }
215
210
  timer++;
216
- if (timer > 30) {
217
- console.error("request stop: timeout", req.url, timer);
211
+ if (timer > 5) {
212
+ console.error("等待请求失败:", req.url, timer);
213
+ clearInterval(detect);
214
+ resolve(false);
218
215
  }
219
216
  }, 1000);
220
217
  });
218
+ if (!res) {
219
+ return wrapperError({
220
+ code: 500,
221
+ message: "请求取消,可能是因为用户未登录或者token过期",
222
+ req
223
+ });
224
+ }
221
225
  }
222
226
  return _adapter(req).then(async (res) => {
223
227
  try {
@@ -230,10 +234,10 @@ class Query {
230
234
  }
231
235
  return res;
232
236
  } catch (e) {
233
- console.error("request afterFn error", e, req);
237
+ console.error("请求在响应后处理时发生错误", e, req);
234
238
  return wrapperError({
235
239
  code: 500,
236
- message: "api request afterFn error",
240
+ message: "请求在响应后处理时发生错误",
237
241
  req
238
242
  });
239
243
  }
@@ -296,7 +300,6 @@ class BaseQuery {
296
300
  }
297
301
  export {
298
302
  wrapperError,
299
- setBaseResponse,
300
303
  adapter,
301
304
  Query,
302
305
  BaseQuery
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kevisual/query",
3
- "version": "0.0.46",
3
+ "version": "0.0.48",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "build": "npm run clean && bun run bun.config.ts",
@@ -19,7 +19,7 @@
19
19
  "description": "",
20
20
  "devDependencies": {
21
21
  "@kevisual/code-builder": "^0.0.6",
22
- "@kevisual/router": "^0.0.74",
22
+ "@kevisual/router": "^0.0.80",
23
23
  "@types/node": "^25.2.3",
24
24
  "typescript": "^5.9.3",
25
25
  "es-toolkit": "^1.44.0",
package/src/adapter.ts CHANGED
@@ -13,14 +13,6 @@ export type AdapterOpts = {
13
13
  body?: Record<string, any> | FormData; // body 可以是对象、字符串或 FormData
14
14
  timeout?: number;
15
15
  method?: Method;
16
- /**
17
- * @deprecated use responseType
18
- */
19
- isBlob?: boolean; // 是否返回 Blob 对象, 第一优先
20
- /**
21
- * @deprecated use responseType
22
- */
23
- isText?: boolean; // 是否返回文本内容, 第二优先
24
16
  /**
25
17
  * 响应类型,
26
18
  * */
@@ -29,7 +21,7 @@ export type AdapterOpts = {
29
21
  };
30
22
  export const isTextForContentType = (contentType: string | null) => {
31
23
  if (!contentType) return false;
32
- const textTypes = ['text/', 'xml', 'html', 'javascript', 'css', 'csv', 'plain', 'x-www-form-urlencoded', 'md'];
24
+ const textTypes = ['text/', 'xml', 'html', 'javascript', 'css', 'csv', 'plain', 'x-www-form-urlencoded', 'md', 'json'];
33
25
  return textTypes.some((type) => contentType.includes(type));
34
26
  };
35
27
  /**
@@ -42,13 +34,6 @@ export const adapter = async (opts: AdapterOpts = {}, overloadOpts?: RequestInit
42
34
  const controller = new AbortController();
43
35
  const signal = controller.signal;
44
36
  const isPostFile = opts.isPostFile || false; // 是否为文件上传
45
- let responseType = opts.responseType || 'json'; // 响应类型
46
- if (opts.isBlob) {
47
- responseType = 'blob';
48
- } else if (opts.isText) {
49
- responseType = 'text';
50
- }
51
-
52
37
  const timeout = opts.timeout || 60000 * 3; // 默认超时时间为 60s * 3
53
38
  const timer = setTimeout(() => {
54
39
  controller.abort();
@@ -110,22 +95,32 @@ export const adapter = async (opts: AdapterOpts = {}, overloadOpts?: RequestInit
110
95
  .then(async (response) => {
111
96
  // 获取 Content-Type 头部信息
112
97
  const contentType = response.headers.get('Content-Type');
113
- if (responseType === 'blob') {
114
- return await response.blob(); // 直接返回 Blob 对象
115
- }
116
- const isText = responseType === 'text';
117
98
  const isJson = contentType && contentType.includes('application/json');
99
+ const isSuccess = response.ok;
118
100
  // 判断返回的数据类型
119
- if (isJson && !isText) {
120
- return await response.json(); // 解析为 JSON
101
+ if (isJson) {
102
+ const json = await response.json();
103
+ if (json?.code) {
104
+ return json;
105
+ }
106
+ return {
107
+ code: isSuccess ? 200 : response.status,
108
+ status: response.status,
109
+ data: json,
110
+ }
121
111
  } else if (isTextForContentType(contentType)) {
122
112
  return {
123
- code: response.status,
113
+ code: isSuccess ? 200 : response.status,
124
114
  status: response.status,
125
115
  data: await response.text(), // 直接返回文本内容
126
116
  };
127
117
  } else {
128
- return response;
118
+ return {
119
+ code: isSuccess ? 200 : response.status,
120
+ status: response.status,
121
+ data: '非文本非JSON响应, 请手动处理response。',
122
+ response
123
+ };
129
124
  }
130
125
  })
131
126
  .catch((err) => {
@@ -143,8 +138,4 @@ export const adapter = async (opts: AdapterOpts = {}, overloadOpts?: RequestInit
143
138
  .finally(() => {
144
139
  clearTimeout(timer);
145
140
  });
146
- };
147
- /**
148
- * adapter
149
- */
150
- export const queryFetch = adapter;
141
+ };
@@ -1,4 +1,5 @@
1
1
 
2
+ import { toJSONSchema, fromJSONSchema } from '@kevisual/router/browser'
2
3
  type RouteInfo = {
3
4
  path: string;
4
5
  key: string;
@@ -44,6 +45,14 @@ export const createQueryByRoutes = (list: RouteInfo[]) => {
44
45
  if (!obj[route.path]) {
45
46
  obj[route.path] = {};
46
47
  }
48
+ if (route.metadata?.args) {
49
+ const args = route.metadata.args;
50
+ if (args?.$schema) {
51
+ // 将 args 转换为 JSON Schema
52
+ const jsonSchema = fromJSONSchema(args);
53
+ route.metadata.args = toJSONSchema(jsonSchema);
54
+ }
55
+ }
47
56
  obj[route.path][route.key] = route;
48
57
  }
49
58
  const code = `