@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.
- package/dist/query-adapter.d.ts +1 -13
- package/dist/query-adapter.js +19 -17
- package/dist/query-api.d.ts +15 -17
- package/dist/query-api.js +13949 -176
- package/dist/query-browser.d.ts +5 -23
- package/dist/query-browser.js +42 -53
- package/dist/query.d.ts +6 -27
- package/dist/query.js +42 -39
- package/package.json +2 -2
- package/src/adapter.ts +20 -29
- package/src/create-query/index.ts +9 -0
- package/src/query-api.ts +17 -5
- package/src/query-browser.ts +1 -25
- package/src/query.ts +27 -38
package/src/query-api.ts
CHANGED
|
@@ -64,18 +64,27 @@ type InferType<T> =
|
|
|
64
64
|
T extends { properties: infer P } ? InferFromJSONSchema<T> : // 处理没有 type 但有 properties 的对象
|
|
65
65
|
T;
|
|
66
66
|
|
|
67
|
+
// 检查是否标记为可选
|
|
68
|
+
type IsOptional<T> = T extends { optional: true } ? true : false;
|
|
69
|
+
|
|
67
70
|
// 提取 args 对象,将每个 Zod schema 或 JSON Schema 转换为实际类型
|
|
71
|
+
// 根据 optional 字段分离必需字段和可选字段
|
|
68
72
|
type ExtractArgsFromMetadata<T> = T extends { metadata?: { args?: infer A } }
|
|
69
73
|
? A extends Record<string, any>
|
|
70
|
-
|
|
71
|
-
|
|
74
|
+
? (
|
|
75
|
+
// 必需字段(没有 optional: true)
|
|
76
|
+
{ [K in keyof A as IsOptional<A[K]> extends true ? never : K]: InferType<A[K]> } &
|
|
77
|
+
// 可选字段(有 optional: true)
|
|
78
|
+
{ [K in keyof A as IsOptional<A[K]> extends true ? K : never]?: InferType<A[K]> }
|
|
79
|
+
)
|
|
80
|
+
: never
|
|
72
81
|
: never;
|
|
73
82
|
|
|
74
83
|
// 类型映射:将 API 配置转换为方法签名
|
|
75
84
|
type ApiMethods<P extends { [path: string]: { [key: string]: Pos } }> = {
|
|
76
85
|
[Path in keyof P]: {
|
|
77
86
|
[Key in keyof P[Path]]: (
|
|
78
|
-
data?:
|
|
87
|
+
data?: ExtractArgsFromMetadata<P[Path][Key]>,
|
|
79
88
|
opts?: DataOpts
|
|
80
89
|
) => ReturnType<Query['post']>
|
|
81
90
|
}
|
|
@@ -97,7 +106,7 @@ export class QueryApi<P extends { [path: string]: { [key: string]: Pos } } = {}>
|
|
|
97
106
|
// 使用泛型来推断类型
|
|
98
107
|
post<T extends Pos>(
|
|
99
108
|
pos: T,
|
|
100
|
-
data?:
|
|
109
|
+
data?: ExtractArgsFromMetadata<T>,
|
|
101
110
|
opts?: DataOpts
|
|
102
111
|
) {
|
|
103
112
|
const _pos = pick(pos, ['path', 'key', 'id']);
|
|
@@ -121,8 +130,11 @@ export class QueryApi<P extends { [path: string]: { [key: string]: Pos } } = {}>
|
|
|
121
130
|
}
|
|
122
131
|
|
|
123
132
|
for (const [key, pos] of Object.entries(methods)) {
|
|
124
|
-
that[path][key] = (data?:
|
|
133
|
+
that[path][key] = (data?: ExtractArgsFromMetadata<typeof pos>, opts: DataOpts = {}) => {
|
|
125
134
|
const _pos = pick(pos, ['path', 'key', 'id']);
|
|
135
|
+
if (pos.metadata?.viewItem?.api?.url && !opts.url) {
|
|
136
|
+
opts.url = pos.metadata.viewItem.api.url;
|
|
137
|
+
}
|
|
126
138
|
return that.query.post({
|
|
127
139
|
..._pos,
|
|
128
140
|
payload: data
|
package/src/query-browser.ts
CHANGED
|
@@ -19,23 +19,8 @@ type QueryOpts = {
|
|
|
19
19
|
* 前端调用后端QueryRouter, 封装 beforeRequest 和 wss
|
|
20
20
|
*/
|
|
21
21
|
export class QueryClient extends Query {
|
|
22
|
-
|
|
23
|
-
storage: Storage;
|
|
24
|
-
token: string;
|
|
25
|
-
constructor(opts?: QueryOptions & { tokenName?: string; storage?: Storage; io?: boolean }) {
|
|
22
|
+
constructor(opts?: QueryOptions & { io?: boolean }) {
|
|
26
23
|
super(opts);
|
|
27
|
-
this.tokenName = opts?.tokenName || 'token';
|
|
28
|
-
this.storage = opts?.storage || globalThis.localStorage;
|
|
29
|
-
this.beforeRequest = async (opts) => {
|
|
30
|
-
const token = this.token || this.getToken();
|
|
31
|
-
if (token) {
|
|
32
|
-
opts.headers = {
|
|
33
|
-
...opts.headers,
|
|
34
|
-
Authorization: `Bearer ${token}`,
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
return opts;
|
|
38
|
-
};
|
|
39
24
|
if (opts?.io) {
|
|
40
25
|
this.createWs();
|
|
41
26
|
}
|
|
@@ -43,15 +28,6 @@ export class QueryClient extends Query {
|
|
|
43
28
|
createWs(opts?: QueryWsOpts) {
|
|
44
29
|
this.qws = new QueryWs({ url: this.url, ...opts });
|
|
45
30
|
}
|
|
46
|
-
getToken() {
|
|
47
|
-
return this.storage.getItem(this.tokenName);
|
|
48
|
-
}
|
|
49
|
-
saveToken(token: string) {
|
|
50
|
-
this.storage.setItem(this.tokenName, token);
|
|
51
|
-
}
|
|
52
|
-
removeToken() {
|
|
53
|
-
this.storage.removeItem(this.tokenName);
|
|
54
|
-
}
|
|
55
31
|
}
|
|
56
32
|
// 移除默认生成的实例
|
|
57
33
|
// export const client = new QueryClient();
|
package/src/query.ts
CHANGED
|
@@ -24,6 +24,8 @@ export type QueryOptions = {
|
|
|
24
24
|
headers?: Record<string, string>;
|
|
25
25
|
timeout?: number;
|
|
26
26
|
isClient?: boolean;
|
|
27
|
+
tokenName?: string;
|
|
28
|
+
storage?: Storage;
|
|
27
29
|
beforeRequest?: Fn;
|
|
28
30
|
}
|
|
29
31
|
export type Data = {
|
|
@@ -46,34 +48,11 @@ export type DataOpts = Partial<QueryOpts> & {
|
|
|
46
48
|
*/
|
|
47
49
|
noStop?: boolean;
|
|
48
50
|
};
|
|
49
|
-
|
|
50
|
-
* 设置基础响应, 设置 success 和 showError,
|
|
51
|
-
* success 是 code 是否等于 200
|
|
52
|
-
* showError 是 如果 success 为 false 且 noMsg 为 false, 则调用 showError
|
|
53
|
-
* @param res 响应
|
|
54
|
-
*/
|
|
55
|
-
export const setBaseResponse = (res: Partial<Result & { success?: boolean; showError?: (fn?: () => void) => void; noMsg?: boolean }>) => {
|
|
56
|
-
res.success = res.code === 200;
|
|
57
|
-
/**
|
|
58
|
-
* 显示错误
|
|
59
|
-
* @param fn 错误处理函数
|
|
60
|
-
*/
|
|
61
|
-
res.showError = (fn?: () => void) => {
|
|
62
|
-
if (!res.success && !res.noMsg) {
|
|
63
|
-
fn?.();
|
|
64
|
-
}
|
|
65
|
-
};
|
|
66
|
-
return res as Result;
|
|
67
|
-
};
|
|
51
|
+
|
|
68
52
|
export const wrapperError = ({ code, message }: { code?: number; message?: string }) => {
|
|
69
53
|
const result = {
|
|
70
54
|
code: code || 500,
|
|
71
|
-
|
|
72
|
-
message: message || 'api request error',
|
|
73
|
-
showError: (fn?: () => void) => {
|
|
74
|
-
//
|
|
75
|
-
},
|
|
76
|
-
noMsg: true,
|
|
55
|
+
message: message || '请求错误'
|
|
77
56
|
};
|
|
78
57
|
return result;
|
|
79
58
|
};
|
|
@@ -105,12 +84,13 @@ export class Query {
|
|
|
105
84
|
stop?: boolean;
|
|
106
85
|
// 默认不使用ws
|
|
107
86
|
qws: QueryWs;
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
isClient = false;
|
|
87
|
+
tokenName: string;
|
|
88
|
+
storage: Storage;
|
|
89
|
+
token: string;
|
|
112
90
|
constructor(opts?: QueryOptions) {
|
|
113
91
|
this.adapter = opts?.adapter || adapter;
|
|
92
|
+
this.tokenName = opts?.tokenName || 'token';
|
|
93
|
+
this.storage = opts?.storage || globalThis?.localStorage;
|
|
114
94
|
const defaultURL = opts?.isClient ? '/client/router' : '/api/router';
|
|
115
95
|
this.url = opts?.url || defaultURL;
|
|
116
96
|
this.headers = opts?.headers || {
|
|
@@ -121,7 +101,7 @@ export class Query {
|
|
|
121
101
|
this.beforeRequest = opts.beforeRequest;
|
|
122
102
|
} else {
|
|
123
103
|
this.beforeRequest = async (opts) => {
|
|
124
|
-
const token =
|
|
104
|
+
const token = this.token || this.storage?.getItem?.(this.tokenName);
|
|
125
105
|
if (token) {
|
|
126
106
|
opts.headers = {
|
|
127
107
|
...opts.headers,
|
|
@@ -162,7 +142,6 @@ export class Query {
|
|
|
162
142
|
*/
|
|
163
143
|
async post<R = any, P = any>(body: Data & P, options?: DataOpts): Promise<Result<R>> {
|
|
164
144
|
const url = options?.url || this.url;
|
|
165
|
-
console.log('query post', url, body, options);
|
|
166
145
|
const { headers, adapter, beforeRequest, afterResponse, timeout, ...rest } = options || {};
|
|
167
146
|
const _headers = { ...this.headers, ...headers };
|
|
168
147
|
const _adapter = adapter || this.adapter;
|
|
@@ -182,7 +161,7 @@ export class Query {
|
|
|
182
161
|
if (res === false) {
|
|
183
162
|
return wrapperError({
|
|
184
163
|
code: 500,
|
|
185
|
-
message: '
|
|
164
|
+
message: '请求取消',
|
|
186
165
|
// @ts-ignore
|
|
187
166
|
req: req,
|
|
188
167
|
});
|
|
@@ -192,14 +171,14 @@ export class Query {
|
|
|
192
171
|
console.error('request beforeFn error', e, req);
|
|
193
172
|
return wrapperError({
|
|
194
173
|
code: 500,
|
|
195
|
-
message: '
|
|
174
|
+
message: '请求在请求前处理时发生错误',
|
|
196
175
|
// @ts-ignore
|
|
197
176
|
req: req,
|
|
198
177
|
});
|
|
199
178
|
}
|
|
200
179
|
if (this.stop && !options?.noStop) {
|
|
201
180
|
const that = this;
|
|
202
|
-
await new Promise((resolve) => {
|
|
181
|
+
const res = await new Promise((resolve) => {
|
|
203
182
|
let timer = 0;
|
|
204
183
|
const detect = setInterval(() => {
|
|
205
184
|
if (!that.stop) {
|
|
@@ -207,11 +186,21 @@ export class Query {
|
|
|
207
186
|
resolve(true);
|
|
208
187
|
}
|
|
209
188
|
timer++;
|
|
210
|
-
if (timer >
|
|
211
|
-
console.error('
|
|
189
|
+
if (timer > 5) {
|
|
190
|
+
console.error('等待请求失败:', req.url, timer);
|
|
191
|
+
clearInterval(detect);
|
|
192
|
+
resolve(false);
|
|
212
193
|
}
|
|
213
194
|
}, 1000);
|
|
214
195
|
});
|
|
196
|
+
if (!res) {
|
|
197
|
+
return wrapperError({
|
|
198
|
+
code: 500,
|
|
199
|
+
message: '请求取消,可能是因为用户未登录或者token过期',
|
|
200
|
+
// @ts-ignore
|
|
201
|
+
req: req,
|
|
202
|
+
});
|
|
203
|
+
}
|
|
215
204
|
}
|
|
216
205
|
return _adapter(req).then(async (res) => {
|
|
217
206
|
try {
|
|
@@ -225,10 +214,10 @@ export class Query {
|
|
|
225
214
|
|
|
226
215
|
return res;
|
|
227
216
|
} catch (e) {
|
|
228
|
-
console.error('
|
|
217
|
+
console.error('请求在响应后处理时发生错误', e, req);
|
|
229
218
|
return wrapperError({
|
|
230
219
|
code: 500,
|
|
231
|
-
message: '
|
|
220
|
+
message: '请求在响应后处理时发生错误',
|
|
232
221
|
// @ts-ignore
|
|
233
222
|
req: req,
|
|
234
223
|
});
|