@kevisual/query 0.0.40 → 0.0.42
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.js +107 -124
- package/dist/query-api.d.ts +317 -0
- package/dist/query-api.js +449 -0
- package/dist/query-browser.d.ts +1 -8
- package/dist/query-browser.js +489 -582
- package/dist/query-ws.js +163 -181
- package/dist/query.d.ts +1 -8
- package/dist/query.js +278 -359
- package/package.json +13 -25
- package/src/adapter.ts +1 -1
- package/src/create-query/index.ts +159 -0
- package/src/query-api.ts +136 -0
- package/src/query-browser.ts +2 -2
- package/src/query.ts +1 -10
|
@@ -0,0 +1,449 @@
|
|
|
1
|
+
// src/adapter.ts
|
|
2
|
+
var isTextForContentType = (contentType) => {
|
|
3
|
+
if (!contentType)
|
|
4
|
+
return false;
|
|
5
|
+
const textTypes = ["text/", "xml", "html", "javascript", "css", "csv", "plain", "x-www-form-urlencoded", "md"];
|
|
6
|
+
return textTypes.some((type) => contentType.includes(type));
|
|
7
|
+
};
|
|
8
|
+
var adapter = async (opts = {}, overloadOpts) => {
|
|
9
|
+
const controller = new AbortController;
|
|
10
|
+
const signal = controller.signal;
|
|
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
|
+
const timeout = opts.timeout || 60000 * 3;
|
|
19
|
+
const timer = setTimeout(() => {
|
|
20
|
+
controller.abort();
|
|
21
|
+
}, timeout);
|
|
22
|
+
let method = overloadOpts?.method || opts?.method || "POST";
|
|
23
|
+
let headers = { ...opts?.headers, ...overloadOpts?.headers };
|
|
24
|
+
let origin = "";
|
|
25
|
+
let url;
|
|
26
|
+
if (opts?.url?.startsWith("http")) {
|
|
27
|
+
url = new URL(opts.url);
|
|
28
|
+
} else {
|
|
29
|
+
origin = globalThis?.location?.origin || "http://localhost:51515";
|
|
30
|
+
url = new URL(opts?.url || "", origin);
|
|
31
|
+
}
|
|
32
|
+
const isGet = method === "GET";
|
|
33
|
+
const oldSearchParams = url.searchParams;
|
|
34
|
+
if (isGet) {
|
|
35
|
+
let searchParams = new URLSearchParams({ ...Object.fromEntries(oldSearchParams), ...opts?.params, ...opts?.body });
|
|
36
|
+
url.search = searchParams.toString();
|
|
37
|
+
} else {
|
|
38
|
+
const params = {
|
|
39
|
+
...Object.fromEntries(oldSearchParams),
|
|
40
|
+
...opts.params
|
|
41
|
+
};
|
|
42
|
+
const searchParams = new URLSearchParams(params);
|
|
43
|
+
if (typeof opts.body === "object" && opts.body !== null) {
|
|
44
|
+
let body2 = opts.body || {};
|
|
45
|
+
if (!params.path && body2?.path) {
|
|
46
|
+
searchParams.set("path", body2.path);
|
|
47
|
+
if (body2?.key) {
|
|
48
|
+
searchParams.set("key", body2.key);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
url.search = searchParams.toString();
|
|
53
|
+
}
|
|
54
|
+
let body = undefined;
|
|
55
|
+
if (isGet) {
|
|
56
|
+
body = undefined;
|
|
57
|
+
} else if (isPostFile) {
|
|
58
|
+
body = opts.body;
|
|
59
|
+
} else {
|
|
60
|
+
if (opts.body && typeof opts.body === "object" && !(opts.body instanceof FormData)) {
|
|
61
|
+
headers = {
|
|
62
|
+
"Content-Type": "application/json",
|
|
63
|
+
...headers
|
|
64
|
+
};
|
|
65
|
+
body = JSON.stringify(opts.body);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return fetch(url, {
|
|
69
|
+
method: method.toUpperCase(),
|
|
70
|
+
signal,
|
|
71
|
+
body,
|
|
72
|
+
...overloadOpts,
|
|
73
|
+
headers
|
|
74
|
+
}).then(async (response) => {
|
|
75
|
+
const contentType = response.headers.get("Content-Type");
|
|
76
|
+
if (responseType === "blob") {
|
|
77
|
+
return await response.blob();
|
|
78
|
+
}
|
|
79
|
+
const isText = responseType === "text";
|
|
80
|
+
const isJson = contentType && contentType.includes("application/json");
|
|
81
|
+
if (isJson && !isText) {
|
|
82
|
+
return await response.json();
|
|
83
|
+
} else if (isTextForContentType(contentType)) {
|
|
84
|
+
return {
|
|
85
|
+
code: response.status,
|
|
86
|
+
status: response.status,
|
|
87
|
+
data: await response.text()
|
|
88
|
+
};
|
|
89
|
+
} else {
|
|
90
|
+
return response;
|
|
91
|
+
}
|
|
92
|
+
}).catch((err) => {
|
|
93
|
+
if (err.name === "AbortError") {
|
|
94
|
+
return {
|
|
95
|
+
code: 408,
|
|
96
|
+
message: "请求超时"
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
return {
|
|
100
|
+
code: 500,
|
|
101
|
+
message: err.message || "网络错误"
|
|
102
|
+
};
|
|
103
|
+
}).finally(() => {
|
|
104
|
+
clearTimeout(timer);
|
|
105
|
+
});
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
// src/query.ts
|
|
109
|
+
var wrapperError = ({ code, message }) => {
|
|
110
|
+
const result = {
|
|
111
|
+
code: code || 500,
|
|
112
|
+
success: false,
|
|
113
|
+
message: message || "api request error",
|
|
114
|
+
showError: (fn) => {},
|
|
115
|
+
noMsg: true
|
|
116
|
+
};
|
|
117
|
+
return result;
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
class Query {
|
|
121
|
+
adapter;
|
|
122
|
+
url;
|
|
123
|
+
beforeRequest;
|
|
124
|
+
afterResponse;
|
|
125
|
+
headers;
|
|
126
|
+
timeout;
|
|
127
|
+
stop;
|
|
128
|
+
qws;
|
|
129
|
+
isClient = false;
|
|
130
|
+
constructor(opts) {
|
|
131
|
+
this.adapter = opts?.adapter || adapter;
|
|
132
|
+
const defaultURL = opts?.isClient ? "/client/router" : "/api/router";
|
|
133
|
+
this.url = opts?.url || defaultURL;
|
|
134
|
+
this.headers = opts?.headers || {
|
|
135
|
+
"Content-Type": "application/json"
|
|
136
|
+
};
|
|
137
|
+
this.timeout = opts?.timeout || 60000 * 3;
|
|
138
|
+
if (opts?.beforeRequest) {
|
|
139
|
+
this.beforeRequest = opts.beforeRequest;
|
|
140
|
+
} else {
|
|
141
|
+
this.beforeRequest = async (opts2) => {
|
|
142
|
+
const token = globalThis?.localStorage?.getItem("token");
|
|
143
|
+
if (token) {
|
|
144
|
+
opts2.headers = {
|
|
145
|
+
...opts2.headers,
|
|
146
|
+
Authorization: `Bearer ${token}`
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
return opts2;
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
setQueryWs(qws) {
|
|
154
|
+
this.qws = qws;
|
|
155
|
+
}
|
|
156
|
+
setStop(stop) {
|
|
157
|
+
this.stop = stop;
|
|
158
|
+
}
|
|
159
|
+
async get(params, options) {
|
|
160
|
+
return this.post(params, options);
|
|
161
|
+
}
|
|
162
|
+
async post(body, options) {
|
|
163
|
+
const url = options?.url || this.url;
|
|
164
|
+
console.log("query post", url, body, options);
|
|
165
|
+
const { headers, adapter: adapter2, beforeRequest, afterResponse, timeout, ...rest } = options || {};
|
|
166
|
+
const _headers = { ...this.headers, ...headers };
|
|
167
|
+
const _adapter = adapter2 || this.adapter;
|
|
168
|
+
const _beforeRequest = beforeRequest || this.beforeRequest;
|
|
169
|
+
const _afterResponse = afterResponse || this.afterResponse;
|
|
170
|
+
const _timeout = timeout || this.timeout;
|
|
171
|
+
const req = {
|
|
172
|
+
url,
|
|
173
|
+
headers: _headers,
|
|
174
|
+
body,
|
|
175
|
+
timeout: _timeout,
|
|
176
|
+
...rest
|
|
177
|
+
};
|
|
178
|
+
try {
|
|
179
|
+
if (_beforeRequest) {
|
|
180
|
+
const res = await _beforeRequest(req);
|
|
181
|
+
if (res === false) {
|
|
182
|
+
return wrapperError({
|
|
183
|
+
code: 500,
|
|
184
|
+
message: "request is cancel",
|
|
185
|
+
req
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
} catch (e) {
|
|
190
|
+
console.error("request beforeFn error", e, req);
|
|
191
|
+
return wrapperError({
|
|
192
|
+
code: 500,
|
|
193
|
+
message: "api request beforeFn error",
|
|
194
|
+
req
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
if (this.stop && !options?.noStop) {
|
|
198
|
+
const that = this;
|
|
199
|
+
await new Promise((resolve) => {
|
|
200
|
+
let timer = 0;
|
|
201
|
+
const detect = setInterval(() => {
|
|
202
|
+
if (!that.stop) {
|
|
203
|
+
clearInterval(detect);
|
|
204
|
+
resolve(true);
|
|
205
|
+
}
|
|
206
|
+
timer++;
|
|
207
|
+
if (timer > 30) {
|
|
208
|
+
console.error("request stop: timeout", req.url, timer);
|
|
209
|
+
}
|
|
210
|
+
}, 1000);
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
return _adapter(req).then(async (res) => {
|
|
214
|
+
try {
|
|
215
|
+
if (_afterResponse) {
|
|
216
|
+
return await _afterResponse(res, {
|
|
217
|
+
req,
|
|
218
|
+
res,
|
|
219
|
+
fetch: adapter2
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
return res;
|
|
223
|
+
} catch (e) {
|
|
224
|
+
console.error("request afterFn error", e, req);
|
|
225
|
+
return wrapperError({
|
|
226
|
+
code: 500,
|
|
227
|
+
message: "api request afterFn error",
|
|
228
|
+
req
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
before(fn) {
|
|
234
|
+
this.beforeRequest = fn;
|
|
235
|
+
}
|
|
236
|
+
after(fn) {
|
|
237
|
+
this.afterResponse = fn;
|
|
238
|
+
}
|
|
239
|
+
async fetchText(urlOrOptions, options) {
|
|
240
|
+
let _options = { ...options };
|
|
241
|
+
if (typeof urlOrOptions === "string" && !_options.url) {
|
|
242
|
+
_options.url = urlOrOptions;
|
|
243
|
+
}
|
|
244
|
+
if (typeof urlOrOptions === "object") {
|
|
245
|
+
_options = { ...urlOrOptions, ..._options };
|
|
246
|
+
}
|
|
247
|
+
const res = await adapter({
|
|
248
|
+
method: "GET",
|
|
249
|
+
..._options,
|
|
250
|
+
headers: {
|
|
251
|
+
...this.headers,
|
|
252
|
+
..._options?.headers || {}
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
if (res && !res.code) {
|
|
256
|
+
return {
|
|
257
|
+
code: 200,
|
|
258
|
+
data: res
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
return res;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// src/create-query/index.ts
|
|
266
|
+
var removeViewItemFromRoutes = (list) => {
|
|
267
|
+
for (const route of list) {
|
|
268
|
+
if (route.metadata?.viewItem) {
|
|
269
|
+
if (route.metadata.viewItem?.api?.query) {
|
|
270
|
+
delete route.metadata.viewItem.api.query;
|
|
271
|
+
}
|
|
272
|
+
if (route.metadata.viewItem?.worker?.worker) {
|
|
273
|
+
delete route.metadata.viewItem.worker.worker;
|
|
274
|
+
}
|
|
275
|
+
if (route.metadata.viewItem?.context?.router) {
|
|
276
|
+
delete route.metadata.viewItem.context.router;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
return list;
|
|
281
|
+
};
|
|
282
|
+
var createQueryByRoutes = (list) => {
|
|
283
|
+
const obj = {};
|
|
284
|
+
list = removeViewItemFromRoutes(list);
|
|
285
|
+
for (const route of list) {
|
|
286
|
+
if (!obj[route.path]) {
|
|
287
|
+
obj[route.path] = {};
|
|
288
|
+
}
|
|
289
|
+
obj[route.path][route.key] = route;
|
|
290
|
+
}
|
|
291
|
+
const code = `
|
|
292
|
+
import { createQueryApi } from '@kevisual/query/api';
|
|
293
|
+
const api = ${generateApiCode(obj)} as const;
|
|
294
|
+
const queryApi = createQueryApi({ api });
|
|
295
|
+
export { queryApi };
|
|
296
|
+
`;
|
|
297
|
+
return code;
|
|
298
|
+
};
|
|
299
|
+
function generateApiCode(obj) {
|
|
300
|
+
let code = `{
|
|
301
|
+
`;
|
|
302
|
+
const paths = Object.keys(obj);
|
|
303
|
+
for (let i = 0;i < paths.length; i++) {
|
|
304
|
+
const path = paths[i];
|
|
305
|
+
const methods = obj[path];
|
|
306
|
+
code += ` "${path}": {
|
|
307
|
+
`;
|
|
308
|
+
const keys = Object.keys(methods);
|
|
309
|
+
for (let j = 0;j < keys.length; j++) {
|
|
310
|
+
const key = keys[j];
|
|
311
|
+
const route = methods[key];
|
|
312
|
+
if (route?.id) {
|
|
313
|
+
if (route.id.startsWith("rand-")) {
|
|
314
|
+
delete route.id;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
const description = route?.metadata?.summary || route?.description || "";
|
|
318
|
+
const args = route?.metadata?.args || {};
|
|
319
|
+
if (description || Object.keys(args).length > 0) {
|
|
320
|
+
code += ` /**
|
|
321
|
+
`;
|
|
322
|
+
if (description) {
|
|
323
|
+
const escapedDescription = description.replace(/\\/g, "\\\\").replace(/\*/g, "\\*").replace(/\n/g, `
|
|
324
|
+
* `);
|
|
325
|
+
code += ` * ${escapedDescription}
|
|
326
|
+
`;
|
|
327
|
+
}
|
|
328
|
+
if (Object.keys(args).length > 0) {
|
|
329
|
+
if (description) {
|
|
330
|
+
code += ` *
|
|
331
|
+
`;
|
|
332
|
+
}
|
|
333
|
+
code += ` * @param data - Request parameters
|
|
334
|
+
`;
|
|
335
|
+
for (const [argName, schema] of Object.entries(args)) {
|
|
336
|
+
const argSchema = schema;
|
|
337
|
+
const argType = argSchema.type || "unknown";
|
|
338
|
+
const argDesc = argSchema.description || "";
|
|
339
|
+
let typeInfo = argType;
|
|
340
|
+
if (argType === "string" && argSchema.enum) {
|
|
341
|
+
typeInfo = argSchema.enum.map((v) => `"${v}"`).join(" | ");
|
|
342
|
+
} else if (argType === "number" || argType === "integer") {
|
|
343
|
+
const constraints = [];
|
|
344
|
+
if (argSchema.minimum !== undefined)
|
|
345
|
+
constraints.push(`min: ${argSchema.minimum}`);
|
|
346
|
+
if (argSchema.maximum !== undefined)
|
|
347
|
+
constraints.push(`max: ${argSchema.maximum}`);
|
|
348
|
+
if (argSchema.exclusiveMinimum !== undefined)
|
|
349
|
+
constraints.push(`> ${argSchema.exclusiveMinimum}`);
|
|
350
|
+
if (argSchema.exclusiveMaximum !== undefined)
|
|
351
|
+
constraints.push(`< ${argSchema.exclusiveMaximum}`);
|
|
352
|
+
if (constraints.length > 0)
|
|
353
|
+
typeInfo += ` (${constraints.join(", ")})`;
|
|
354
|
+
} else if (argType === "string") {
|
|
355
|
+
const constraints = [];
|
|
356
|
+
if (argSchema.minLength !== undefined)
|
|
357
|
+
constraints.push(`minLength: ${argSchema.minLength}`);
|
|
358
|
+
if (argSchema.maxLength !== undefined)
|
|
359
|
+
constraints.push(`maxLength: ${argSchema.maxLength}`);
|
|
360
|
+
if (argSchema.format)
|
|
361
|
+
constraints.push(`format: ${argSchema.format}`);
|
|
362
|
+
if (constraints.length > 0)
|
|
363
|
+
typeInfo += ` (${constraints.join(", ")})`;
|
|
364
|
+
}
|
|
365
|
+
const escapedArgDesc = argDesc.replace(/\\/g, "\\\\").replace(/\*/g, "\\*").replace(/\n/g, " ");
|
|
366
|
+
code += ` * @param data.${argName} - {${typeInfo}}${escapedArgDesc ? " " + escapedArgDesc : ""}
|
|
367
|
+
`;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
code += ` */
|
|
371
|
+
`;
|
|
372
|
+
}
|
|
373
|
+
code += ` "${key}": ${JSON.stringify(route, null, 2).split(`
|
|
374
|
+
`).map((line, idx) => idx === 0 ? line : " " + line).join(`
|
|
375
|
+
`)}`;
|
|
376
|
+
if (j < keys.length - 1) {
|
|
377
|
+
code += ",";
|
|
378
|
+
}
|
|
379
|
+
code += `
|
|
380
|
+
`;
|
|
381
|
+
}
|
|
382
|
+
code += ` }`;
|
|
383
|
+
if (i < paths.length - 1) {
|
|
384
|
+
code += ",";
|
|
385
|
+
}
|
|
386
|
+
code += `
|
|
387
|
+
`;
|
|
388
|
+
}
|
|
389
|
+
code += "}";
|
|
390
|
+
return code;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// node_modules/.pnpm/es-toolkit@1.44.0/node_modules/es-toolkit/dist/object/pick.mjs
|
|
394
|
+
function pick(obj, keys) {
|
|
395
|
+
const result = {};
|
|
396
|
+
for (let i = 0;i < keys.length; i++) {
|
|
397
|
+
const key = keys[i];
|
|
398
|
+
if (Object.hasOwn(obj, key)) {
|
|
399
|
+
result[key] = obj[key];
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
return result;
|
|
403
|
+
}
|
|
404
|
+
// src/query-api.ts
|
|
405
|
+
class QueryApi {
|
|
406
|
+
query;
|
|
407
|
+
constructor(opts) {
|
|
408
|
+
this.query = opts?.query ?? new Query;
|
|
409
|
+
if (opts?.api) {
|
|
410
|
+
this.createApi(opts.api);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
post(pos, data, opts) {
|
|
414
|
+
const _pos = pick(pos, ["path", "key", "id"]);
|
|
415
|
+
return this.query.post({
|
|
416
|
+
..._pos,
|
|
417
|
+
payload: data
|
|
418
|
+
}, opts);
|
|
419
|
+
}
|
|
420
|
+
createApi(api) {
|
|
421
|
+
const that = this;
|
|
422
|
+
const apiEntries = Object.entries(api);
|
|
423
|
+
const keepPaths = ["createApi", "query", "post"];
|
|
424
|
+
for (const [path, methods] of apiEntries) {
|
|
425
|
+
if (keepPaths.includes(path))
|
|
426
|
+
continue;
|
|
427
|
+
if (!that[path]) {
|
|
428
|
+
that[path] = {};
|
|
429
|
+
}
|
|
430
|
+
for (const [key, pos] of Object.entries(methods)) {
|
|
431
|
+
that[path][key] = (data, opts) => {
|
|
432
|
+
const _pos = pick(pos, ["path", "key", "id"]);
|
|
433
|
+
return that.query.post({
|
|
434
|
+
..._pos,
|
|
435
|
+
payload: data
|
|
436
|
+
}, opts);
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
function createQueryApi(opts) {
|
|
443
|
+
return new QueryApi(opts);
|
|
444
|
+
}
|
|
445
|
+
export {
|
|
446
|
+
createQueryByRoutes,
|
|
447
|
+
createQueryApi,
|
|
448
|
+
QueryApi
|
|
449
|
+
};
|
package/dist/query-browser.d.ts
CHANGED
|
@@ -239,13 +239,6 @@ declare class BaseQuery<T extends Query = Query, R extends {
|
|
|
239
239
|
post<R = any, P = any>(data: P, options?: DataOpts): Promise<Result<R>>;
|
|
240
240
|
get<R = any, P = any>(data: P, options?: DataOpts): Promise<Result<R>>;
|
|
241
241
|
}
|
|
242
|
-
/**
|
|
243
|
-
* @deprecated
|
|
244
|
-
* 前端调用后端QueryRouter, 默认路径 /client/router
|
|
245
|
-
*/
|
|
246
|
-
declare class ClientQuery extends Query {
|
|
247
|
-
constructor(opts?: QueryOpts$1);
|
|
248
|
-
}
|
|
249
242
|
|
|
250
243
|
type QueryOpts = {
|
|
251
244
|
url?: string;
|
|
@@ -272,5 +265,5 @@ declare class QueryClient extends Query {
|
|
|
272
265
|
removeToken(): void;
|
|
273
266
|
}
|
|
274
267
|
|
|
275
|
-
export { BaseQuery,
|
|
268
|
+
export { BaseQuery, Query, QueryClient, QueryWs, adapter, wrapperError };
|
|
276
269
|
export type { Data, DataOpts, QueryOptions, QueryOpts, QueryWsOpts, Result };
|