@kevisual/query 0.0.39 → 0.0.41
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 -122
- package/dist/query-api.d.ts +305 -0
- package/dist/query-api.js +432 -0
- package/dist/query-browser.d.ts +4 -8
- package/dist/query-browser.js +483 -571
- package/dist/query-ws.js +163 -181
- package/dist/query.d.ts +4 -8
- package/dist/query.js +278 -354
- package/package.json +14 -26
- package/src/adapter.ts +10 -8
- package/src/create-query/index.ts +130 -0
- package/src/query-api.ts +136 -0
- package/src/query-browser.ts +2 -2
- package/src/query.ts +5 -11
package/dist/query-browser.js
CHANGED
|
@@ -1,129 +1,114 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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));
|
|
6
7
|
};
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
...opts.params,
|
|
49
|
-
};
|
|
50
|
-
const searchParams = new URLSearchParams(params);
|
|
51
|
-
if (typeof opts.body === 'object' && opts.body !== null) {
|
|
52
|
-
// 浏览器环境下,自动将 body 中的 path 和 key 提取到查询参数中, 更容易排查问题
|
|
53
|
-
let body = opts.body || {};
|
|
54
|
-
if (!params.path && body?.path) {
|
|
55
|
-
searchParams.set('path', body.path);
|
|
56
|
-
if (body?.key) {
|
|
57
|
-
searchParams.set('key', body.key);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
url.search = searchParams.toString();
|
|
62
|
-
}
|
|
63
|
-
let body = undefined;
|
|
64
|
-
if (isGet) {
|
|
65
|
-
body = undefined;
|
|
66
|
-
}
|
|
67
|
-
else if (isPostFile) {
|
|
68
|
-
body = opts.body; // 如果是文件上传,直接使用 FormData
|
|
69
|
-
}
|
|
70
|
-
else {
|
|
71
|
-
headers = {
|
|
72
|
-
'Content-Type': 'application/json',
|
|
73
|
-
...headers,
|
|
74
|
-
};
|
|
75
|
-
body = JSON.stringify(opts.body); // 否则将对象转换为 JSON 字符串
|
|
76
|
-
}
|
|
77
|
-
return fetch(url, {
|
|
78
|
-
method: method.toUpperCase(),
|
|
79
|
-
signal,
|
|
80
|
-
body: body,
|
|
81
|
-
...overloadOpts,
|
|
82
|
-
headers: headers,
|
|
83
|
-
})
|
|
84
|
-
.then(async (response) => {
|
|
85
|
-
// 获取 Content-Type 头部信息
|
|
86
|
-
const contentType = response.headers.get('Content-Type');
|
|
87
|
-
if (responseType === 'blob') {
|
|
88
|
-
return await response.blob(); // 直接返回 Blob 对象
|
|
89
|
-
}
|
|
90
|
-
const isText = responseType === 'text';
|
|
91
|
-
const isJson = contentType && contentType.includes('application/json');
|
|
92
|
-
// 判断返回的数据类型
|
|
93
|
-
if (isJson && !isText) {
|
|
94
|
-
return await response.json(); // 解析为 JSON
|
|
95
|
-
}
|
|
96
|
-
else if (isTextForContentType(contentType)) {
|
|
97
|
-
return {
|
|
98
|
-
code: response.status,
|
|
99
|
-
status: response.status,
|
|
100
|
-
data: await response.text(), // 直接返回文本内容
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
else {
|
|
104
|
-
return response;
|
|
105
|
-
}
|
|
106
|
-
})
|
|
107
|
-
.catch((err) => {
|
|
108
|
-
if (err.name === 'AbortError') {
|
|
109
|
-
return {
|
|
110
|
-
code: 408,
|
|
111
|
-
message: '请求超时',
|
|
112
|
-
};
|
|
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);
|
|
113
49
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
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
|
+
});
|
|
122
106
|
};
|
|
123
107
|
|
|
124
|
-
|
|
108
|
+
// node_modules/.pnpm/zustand@5.0.11/node_modules/zustand/esm/vanilla.mjs
|
|
109
|
+
var createStoreImpl = (createState) => {
|
|
125
110
|
let state;
|
|
126
|
-
const listeners = /* @__PURE__ */ new Set
|
|
111
|
+
const listeners = /* @__PURE__ */ new Set;
|
|
127
112
|
const setState = (partial, replace) => {
|
|
128
113
|
const nextState = typeof partial === "function" ? partial(state) : partial;
|
|
129
114
|
if (!Object.is(nextState, state)) {
|
|
@@ -142,476 +127,403 @@ const createStoreImpl = (createState) => {
|
|
|
142
127
|
const initialState = state = createState(setState, getState, api);
|
|
143
128
|
return api;
|
|
144
129
|
};
|
|
145
|
-
|
|
130
|
+
var createStore = (createState) => createState ? createStoreImpl(createState) : createStoreImpl;
|
|
146
131
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
132
|
+
// src/utils.ts
|
|
133
|
+
var parseWsUrl = (url) => {
|
|
134
|
+
try {
|
|
135
|
+
new URL(url);
|
|
136
|
+
return url;
|
|
137
|
+
} catch (e) {
|
|
138
|
+
const _url = new URL(url, location.origin);
|
|
139
|
+
if (_url.protocol === "http:") {
|
|
140
|
+
_url.protocol = "ws:";
|
|
141
|
+
}
|
|
142
|
+
if (_url.protocol === "https:") {
|
|
143
|
+
_url.protocol = "wss:";
|
|
144
|
+
}
|
|
145
|
+
return _url.href;
|
|
146
|
+
}
|
|
162
147
|
};
|
|
163
148
|
|
|
149
|
+
// src/ws.ts
|
|
164
150
|
class QueryWs {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
151
|
+
url;
|
|
152
|
+
store;
|
|
153
|
+
ws;
|
|
154
|
+
constructor(opts) {
|
|
155
|
+
const url = opts?.url || "/api/router";
|
|
156
|
+
if (opts?.store) {
|
|
157
|
+
this.store = opts.store;
|
|
158
|
+
} else {
|
|
159
|
+
const store = createStore((set) => ({
|
|
160
|
+
connected: false,
|
|
161
|
+
status: "connecting",
|
|
162
|
+
setConnected: (connected) => set({ connected }),
|
|
163
|
+
setStatus: (status) => set({ status })
|
|
164
|
+
}));
|
|
165
|
+
this.store = store;
|
|
166
|
+
}
|
|
167
|
+
const wsUrl = parseWsUrl(url);
|
|
168
|
+
if (opts?.ws && opts.ws instanceof WebSocket) {
|
|
169
|
+
this.ws = opts.ws;
|
|
170
|
+
} else {
|
|
171
|
+
this.ws = new WebSocket(wsUrl);
|
|
172
|
+
}
|
|
173
|
+
this.connect();
|
|
174
|
+
}
|
|
175
|
+
async connect(opts) {
|
|
176
|
+
const store = this.store;
|
|
177
|
+
const that = this;
|
|
178
|
+
const connected = store.getState().connected;
|
|
179
|
+
if (connected) {
|
|
180
|
+
return Promise.resolve(true);
|
|
181
|
+
}
|
|
182
|
+
return new Promise((resolve, reject) => {
|
|
183
|
+
const ws = that.ws || new WebSocket(that.url);
|
|
184
|
+
const timeout = opts?.timeout || 5 * 60 * 1000;
|
|
185
|
+
let timer = setTimeout(() => {
|
|
186
|
+
const isOpen = ws.readyState === WebSocket.OPEN;
|
|
187
|
+
if (isOpen) {
|
|
188
|
+
console.log("WebSocket 连接成功 in timer");
|
|
189
|
+
resolve(true);
|
|
190
|
+
return;
|
|
172
191
|
}
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
if (opts?.ws && opts.ws instanceof WebSocket) {
|
|
184
|
-
this.ws = opts.ws;
|
|
185
|
-
}
|
|
186
|
-
else {
|
|
187
|
-
this.ws = new WebSocket(wsUrl);
|
|
188
|
-
}
|
|
189
|
-
this.connect();
|
|
190
|
-
}
|
|
191
|
-
/**
|
|
192
|
-
* 连接 WebSocket
|
|
193
|
-
*/
|
|
194
|
-
async connect(opts) {
|
|
195
|
-
const store = this.store;
|
|
196
|
-
const that = this;
|
|
197
|
-
const connected = store.getState().connected;
|
|
198
|
-
if (connected) {
|
|
199
|
-
return Promise.resolve(true);
|
|
200
|
-
}
|
|
201
|
-
return new Promise((resolve, reject) => {
|
|
202
|
-
const ws = that.ws || new WebSocket(that.url);
|
|
203
|
-
const timeout = opts?.timeout || 5 * 60 * 1000; // 默认 5 分钟
|
|
204
|
-
let timer = setTimeout(() => {
|
|
205
|
-
const isOpen = ws.readyState === WebSocket.OPEN;
|
|
206
|
-
if (isOpen) {
|
|
207
|
-
console.log('WebSocket 连接成功 in timer');
|
|
208
|
-
resolve(true);
|
|
209
|
-
return;
|
|
210
|
-
}
|
|
211
|
-
console.error('WebSocket 连接超时', that.url);
|
|
212
|
-
resolve(false);
|
|
213
|
-
}, timeout);
|
|
214
|
-
ws.onopen = (ev) => {
|
|
215
|
-
store.getState().setConnected(true);
|
|
216
|
-
store.getState().setStatus('connected');
|
|
217
|
-
resolve(true);
|
|
218
|
-
clearTimeout(timer);
|
|
219
|
-
};
|
|
220
|
-
ws.onclose = (ev) => {
|
|
221
|
-
store.getState().setConnected(false);
|
|
222
|
-
store.getState().setStatus('disconnected');
|
|
223
|
-
this.ws = null;
|
|
224
|
-
};
|
|
225
|
-
});
|
|
226
|
-
}
|
|
227
|
-
/**
|
|
228
|
-
* ws.onopen 必须用这个去获取,否者会丢失链接信息
|
|
229
|
-
* @param callback
|
|
230
|
-
* @returns
|
|
231
|
-
*/
|
|
232
|
-
listenConnect(callback) {
|
|
233
|
-
const store = this.store;
|
|
234
|
-
const { connected } = store.getState();
|
|
235
|
-
if (connected) {
|
|
236
|
-
callback();
|
|
237
|
-
return;
|
|
238
|
-
}
|
|
239
|
-
const subscriptionOne = (selector, listener) => {
|
|
240
|
-
const unsubscribe = store.subscribe((newState, oldState) => {
|
|
241
|
-
if (selector(newState) !== selector(oldState)) {
|
|
242
|
-
listener(newState, oldState);
|
|
243
|
-
unsubscribe();
|
|
244
|
-
}
|
|
245
|
-
});
|
|
246
|
-
return unsubscribe;
|
|
247
|
-
};
|
|
248
|
-
const cancel = subscriptionOne((state) => state.connected, () => {
|
|
249
|
-
callback();
|
|
250
|
-
});
|
|
251
|
-
return cancel;
|
|
252
|
-
}
|
|
253
|
-
listenClose(callback) {
|
|
254
|
-
const store = this.store;
|
|
255
|
-
const { status } = store.getState();
|
|
256
|
-
if (status === 'disconnected') {
|
|
257
|
-
callback();
|
|
258
|
-
}
|
|
259
|
-
const subscriptionOne = (selector, listener) => {
|
|
260
|
-
const unsubscribe = store.subscribe((newState, oldState) => {
|
|
261
|
-
if (selector(newState) !== selector(oldState)) {
|
|
262
|
-
listener(newState, oldState);
|
|
263
|
-
unsubscribe();
|
|
264
|
-
}
|
|
265
|
-
});
|
|
266
|
-
return unsubscribe;
|
|
267
|
-
};
|
|
268
|
-
const cancel = subscriptionOne((state) => state.status, (newState, oldState) => {
|
|
269
|
-
if (newState.status === 'disconnected') {
|
|
270
|
-
callback();
|
|
271
|
-
}
|
|
272
|
-
});
|
|
273
|
-
return cancel;
|
|
274
|
-
}
|
|
275
|
-
onMessage(fn, opts) {
|
|
276
|
-
const ws = this.ws;
|
|
277
|
-
const isJson = opts?.isJson ?? true;
|
|
278
|
-
const selector = opts?.selector;
|
|
279
|
-
const parseIfJson = (data) => {
|
|
280
|
-
try {
|
|
281
|
-
return JSON.parse(data);
|
|
282
|
-
}
|
|
283
|
-
catch (e) {
|
|
284
|
-
return data;
|
|
285
|
-
}
|
|
286
|
-
};
|
|
287
|
-
const listener = (event) => {
|
|
288
|
-
const received = parseIfJson(event.data);
|
|
289
|
-
if (typeof received === 'string' && !isJson) {
|
|
290
|
-
fn(received, event);
|
|
291
|
-
}
|
|
292
|
-
else if (typeof received === 'object' && isJson) {
|
|
293
|
-
fn(selector ? selector(received) : received, event);
|
|
294
|
-
}
|
|
295
|
-
else ;
|
|
296
|
-
};
|
|
297
|
-
ws.addEventListener('message', listener);
|
|
298
|
-
return () => {
|
|
299
|
-
ws.removeEventListener('message', listener);
|
|
300
|
-
};
|
|
301
|
-
}
|
|
302
|
-
close() {
|
|
303
|
-
const ws = this.ws;
|
|
304
|
-
const store = this.store;
|
|
305
|
-
ws?.close?.();
|
|
306
|
-
this.ws = null;
|
|
192
|
+
console.error("WebSocket 连接超时", that.url);
|
|
193
|
+
resolve(false);
|
|
194
|
+
}, timeout);
|
|
195
|
+
ws.onopen = (ev) => {
|
|
196
|
+
store.getState().setConnected(true);
|
|
197
|
+
store.getState().setStatus("connected");
|
|
198
|
+
resolve(true);
|
|
199
|
+
clearTimeout(timer);
|
|
200
|
+
};
|
|
201
|
+
ws.onclose = (ev) => {
|
|
307
202
|
store.getState().setConnected(false);
|
|
308
|
-
store.getState().setStatus(
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
if (isJson) {
|
|
326
|
-
ws.send(JSON.stringify(wrapper ? wrapper(data) : data));
|
|
327
|
-
}
|
|
328
|
-
else {
|
|
329
|
-
ws.send(data);
|
|
203
|
+
store.getState().setStatus("disconnected");
|
|
204
|
+
this.ws = null;
|
|
205
|
+
};
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
listenConnect(callback) {
|
|
209
|
+
const store = this.store;
|
|
210
|
+
const { connected } = store.getState();
|
|
211
|
+
if (connected) {
|
|
212
|
+
callback();
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
const subscriptionOne = (selector, listener) => {
|
|
216
|
+
const unsubscribe = store.subscribe((newState, oldState) => {
|
|
217
|
+
if (selector(newState) !== selector(oldState)) {
|
|
218
|
+
listener(newState, oldState);
|
|
219
|
+
unsubscribe();
|
|
330
220
|
}
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
221
|
+
});
|
|
222
|
+
return unsubscribe;
|
|
223
|
+
};
|
|
224
|
+
const cancel = subscriptionOne((state) => state.connected, () => {
|
|
225
|
+
callback();
|
|
226
|
+
});
|
|
227
|
+
return cancel;
|
|
228
|
+
}
|
|
229
|
+
listenClose(callback) {
|
|
230
|
+
const store = this.store;
|
|
231
|
+
const { status } = store.getState();
|
|
232
|
+
if (status === "disconnected") {
|
|
233
|
+
callback();
|
|
234
|
+
}
|
|
235
|
+
const subscriptionOne = (selector, listener) => {
|
|
236
|
+
const unsubscribe = store.subscribe((newState, oldState) => {
|
|
237
|
+
if (selector(newState) !== selector(oldState)) {
|
|
238
|
+
listener(newState, oldState);
|
|
239
|
+
unsubscribe();
|
|
335
240
|
}
|
|
336
|
-
|
|
337
|
-
|
|
241
|
+
});
|
|
242
|
+
return unsubscribe;
|
|
243
|
+
};
|
|
244
|
+
const cancel = subscriptionOne((state) => state.status, (newState, oldState) => {
|
|
245
|
+
if (newState.status === "disconnected") {
|
|
246
|
+
callback();
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
return cancel;
|
|
250
|
+
}
|
|
251
|
+
onMessage(fn, opts) {
|
|
252
|
+
const ws = this.ws;
|
|
253
|
+
const isJson = opts?.isJson ?? true;
|
|
254
|
+
const selector = opts?.selector;
|
|
255
|
+
const parseIfJson = (data) => {
|
|
256
|
+
try {
|
|
257
|
+
return JSON.parse(data);
|
|
258
|
+
} catch (e) {
|
|
259
|
+
return data;
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
const listener = (event) => {
|
|
263
|
+
const received = parseIfJson(event.data);
|
|
264
|
+
if (typeof received === "string" && !isJson) {
|
|
265
|
+
fn(received, event);
|
|
266
|
+
} else if (typeof received === "object" && isJson) {
|
|
267
|
+
fn(selector ? selector(received) : received, event);
|
|
268
|
+
} else {}
|
|
269
|
+
};
|
|
270
|
+
ws.addEventListener("message", listener);
|
|
271
|
+
return () => {
|
|
272
|
+
ws.removeEventListener("message", listener);
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
close() {
|
|
276
|
+
const ws = this.ws;
|
|
277
|
+
const store = this.store;
|
|
278
|
+
ws?.close?.();
|
|
279
|
+
this.ws = null;
|
|
280
|
+
store.getState().setConnected(false);
|
|
281
|
+
store.getState().setStatus("disconnected");
|
|
282
|
+
}
|
|
283
|
+
send(data, opts) {
|
|
284
|
+
const ws = this.ws;
|
|
285
|
+
const isJson = opts?.isJson ?? true;
|
|
286
|
+
const wrapper = opts?.wrapper;
|
|
287
|
+
if (!ws || ws.readyState !== WebSocket.OPEN) {
|
|
288
|
+
console.error("WebSocket is not open");
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
if (isJson) {
|
|
292
|
+
ws.send(JSON.stringify(wrapper ? wrapper(data) : data));
|
|
293
|
+
} else {
|
|
294
|
+
ws.send(data);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
getOpen() {
|
|
298
|
+
if (!this.ws) {
|
|
299
|
+
return false;
|
|
300
|
+
}
|
|
301
|
+
return this.ws.readyState === WebSocket.OPEN;
|
|
302
|
+
}
|
|
338
303
|
}
|
|
339
304
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
return result;
|
|
305
|
+
// src/query.ts
|
|
306
|
+
var wrapperError = ({ code, message }) => {
|
|
307
|
+
const result = {
|
|
308
|
+
code: code || 500,
|
|
309
|
+
success: false,
|
|
310
|
+
message: message || "api request error",
|
|
311
|
+
showError: (fn) => {},
|
|
312
|
+
noMsg: true
|
|
313
|
+
};
|
|
314
|
+
return result;
|
|
351
315
|
};
|
|
352
|
-
|
|
353
|
-
* const query = new Query();
|
|
354
|
-
* const res = await query.post({
|
|
355
|
-
* path: 'demo',
|
|
356
|
-
* key: '1',
|
|
357
|
-
* });
|
|
358
|
-
*
|
|
359
|
-
* U是参数 V是返回值
|
|
360
|
-
*/
|
|
316
|
+
|
|
361
317
|
class Query {
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
const
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
if (opts.beforeRequest) {
|
|
390
|
-
this.beforeRequest = opts.beforeRequest;
|
|
391
|
-
}
|
|
392
|
-
else {
|
|
393
|
-
this.beforeRequest = async (opts) => {
|
|
394
|
-
const token = globalThis?.localStorage?.getItem('token');
|
|
395
|
-
if (token) {
|
|
396
|
-
opts.headers = {
|
|
397
|
-
...opts.headers,
|
|
398
|
-
Authorization: `Bearer ${token}`,
|
|
399
|
-
};
|
|
400
|
-
}
|
|
401
|
-
return opts;
|
|
402
|
-
};
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
setQueryWs(qws) {
|
|
406
|
-
this.qws = qws;
|
|
407
|
-
}
|
|
408
|
-
/**
|
|
409
|
-
* 突然停止请求
|
|
410
|
-
*/
|
|
411
|
-
setStop(stop) {
|
|
412
|
-
this.stop = stop;
|
|
413
|
-
}
|
|
414
|
-
/**
|
|
415
|
-
* 发送 get 请求,转到 post 请求
|
|
416
|
-
* T是请求类型自定义
|
|
417
|
-
* S是返回类型自定义
|
|
418
|
-
* @param params 请求参数
|
|
419
|
-
* @param options 请求配置
|
|
420
|
-
* @returns 请求结果
|
|
421
|
-
*/
|
|
422
|
-
async get(params, options) {
|
|
423
|
-
return this.post(params, options);
|
|
424
|
-
}
|
|
425
|
-
/**
|
|
426
|
-
* 发送 post 请求
|
|
427
|
-
* T是请求类型自定义
|
|
428
|
-
* S是返回类型自定义
|
|
429
|
-
* @param body 请求体
|
|
430
|
-
* @param options 请求配置
|
|
431
|
-
* @returns 请求结果
|
|
432
|
-
*/
|
|
433
|
-
async post(body, options) {
|
|
434
|
-
const url = options?.url || this.url;
|
|
435
|
-
const { headers, adapter, beforeRequest, afterResponse, timeout, ...rest } = options || {};
|
|
436
|
-
const _headers = { ...this.headers, ...headers };
|
|
437
|
-
const _adapter = adapter || this.adapter;
|
|
438
|
-
const _beforeRequest = beforeRequest || this.beforeRequest;
|
|
439
|
-
const _afterResponse = afterResponse || this.afterResponse;
|
|
440
|
-
const _timeout = timeout || this.timeout;
|
|
441
|
-
const req = {
|
|
442
|
-
url: url,
|
|
443
|
-
headers: _headers,
|
|
444
|
-
body,
|
|
445
|
-
timeout: _timeout,
|
|
446
|
-
...rest,
|
|
447
|
-
};
|
|
448
|
-
try {
|
|
449
|
-
if (_beforeRequest) {
|
|
450
|
-
const res = await _beforeRequest(req);
|
|
451
|
-
if (res === false) {
|
|
452
|
-
return wrapperError({
|
|
453
|
-
code: 500,
|
|
454
|
-
message: 'request is cancel',
|
|
455
|
-
// @ts-ignore
|
|
456
|
-
req: req,
|
|
457
|
-
});
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
catch (e) {
|
|
462
|
-
console.error('request beforeFn error', e, req);
|
|
463
|
-
return wrapperError({
|
|
464
|
-
code: 500,
|
|
465
|
-
message: 'api request beforeFn error'});
|
|
466
|
-
}
|
|
467
|
-
if (this.stop && !options?.noStop) {
|
|
468
|
-
const that = this;
|
|
469
|
-
await new Promise((resolve) => {
|
|
470
|
-
let timer = 0;
|
|
471
|
-
const detect = setInterval(() => {
|
|
472
|
-
if (!that.stop) {
|
|
473
|
-
clearInterval(detect);
|
|
474
|
-
resolve(true);
|
|
475
|
-
}
|
|
476
|
-
timer++;
|
|
477
|
-
if (timer > 30) {
|
|
478
|
-
console.error('request stop: timeout', req.url, timer);
|
|
479
|
-
}
|
|
480
|
-
}, 1000);
|
|
481
|
-
});
|
|
482
|
-
}
|
|
483
|
-
return _adapter(req).then(async (res) => {
|
|
484
|
-
try {
|
|
485
|
-
if (_afterResponse) {
|
|
486
|
-
return await _afterResponse(res, {
|
|
487
|
-
req,
|
|
488
|
-
res,
|
|
489
|
-
fetch: adapter,
|
|
490
|
-
});
|
|
491
|
-
}
|
|
492
|
-
return res;
|
|
493
|
-
}
|
|
494
|
-
catch (e) {
|
|
495
|
-
console.error('request afterFn error', e, req);
|
|
496
|
-
return wrapperError({
|
|
497
|
-
code: 500,
|
|
498
|
-
message: 'api request afterFn error'});
|
|
499
|
-
}
|
|
500
|
-
});
|
|
501
|
-
}
|
|
502
|
-
/**
|
|
503
|
-
* 设置请求前处理,设置请求前处理函数
|
|
504
|
-
* @param fn 处理函数
|
|
505
|
-
*/
|
|
506
|
-
before(fn) {
|
|
507
|
-
this.beforeRequest = fn;
|
|
508
|
-
}
|
|
509
|
-
/**
|
|
510
|
-
* 设置请求后处理,设置请求后处理函数
|
|
511
|
-
* @param fn 处理函数
|
|
512
|
-
*/
|
|
513
|
-
after(fn) {
|
|
514
|
-
this.afterResponse = fn;
|
|
515
|
-
}
|
|
516
|
-
async fetchText(urlOrOptions, options) {
|
|
517
|
-
let _options = { ...options };
|
|
518
|
-
if (typeof urlOrOptions === 'string' && !_options.url) {
|
|
519
|
-
_options.url = urlOrOptions;
|
|
318
|
+
adapter;
|
|
319
|
+
url;
|
|
320
|
+
beforeRequest;
|
|
321
|
+
afterResponse;
|
|
322
|
+
headers;
|
|
323
|
+
timeout;
|
|
324
|
+
stop;
|
|
325
|
+
qws;
|
|
326
|
+
isClient = false;
|
|
327
|
+
constructor(opts) {
|
|
328
|
+
this.adapter = opts?.adapter || adapter;
|
|
329
|
+
const defaultURL = opts?.isClient ? "/client/router" : "/api/router";
|
|
330
|
+
this.url = opts?.url || defaultURL;
|
|
331
|
+
this.headers = opts?.headers || {
|
|
332
|
+
"Content-Type": "application/json"
|
|
333
|
+
};
|
|
334
|
+
this.timeout = opts?.timeout || 60000 * 3;
|
|
335
|
+
if (opts?.beforeRequest) {
|
|
336
|
+
this.beforeRequest = opts.beforeRequest;
|
|
337
|
+
} else {
|
|
338
|
+
this.beforeRequest = async (opts2) => {
|
|
339
|
+
const token = globalThis?.localStorage?.getItem("token");
|
|
340
|
+
if (token) {
|
|
341
|
+
opts2.headers = {
|
|
342
|
+
...opts2.headers,
|
|
343
|
+
Authorization: `Bearer ${token}`
|
|
344
|
+
};
|
|
520
345
|
}
|
|
521
|
-
|
|
522
|
-
|
|
346
|
+
return opts2;
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
setQueryWs(qws) {
|
|
351
|
+
this.qws = qws;
|
|
352
|
+
}
|
|
353
|
+
setStop(stop) {
|
|
354
|
+
this.stop = stop;
|
|
355
|
+
}
|
|
356
|
+
async get(params, options) {
|
|
357
|
+
return this.post(params, options);
|
|
358
|
+
}
|
|
359
|
+
async post(body, options) {
|
|
360
|
+
const url = options?.url || this.url;
|
|
361
|
+
console.log("query post", url, body, options);
|
|
362
|
+
const { headers, adapter: adapter2, beforeRequest, afterResponse, timeout, ...rest } = options || {};
|
|
363
|
+
const _headers = { ...this.headers, ...headers };
|
|
364
|
+
const _adapter = adapter2 || this.adapter;
|
|
365
|
+
const _beforeRequest = beforeRequest || this.beforeRequest;
|
|
366
|
+
const _afterResponse = afterResponse || this.afterResponse;
|
|
367
|
+
const _timeout = timeout || this.timeout;
|
|
368
|
+
const req = {
|
|
369
|
+
url,
|
|
370
|
+
headers: _headers,
|
|
371
|
+
body,
|
|
372
|
+
timeout: _timeout,
|
|
373
|
+
...rest
|
|
374
|
+
};
|
|
375
|
+
try {
|
|
376
|
+
if (_beforeRequest) {
|
|
377
|
+
const res = await _beforeRequest(req);
|
|
378
|
+
if (res === false) {
|
|
379
|
+
return wrapperError({
|
|
380
|
+
code: 500,
|
|
381
|
+
message: "request is cancel",
|
|
382
|
+
req
|
|
383
|
+
});
|
|
523
384
|
}
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
385
|
+
}
|
|
386
|
+
} catch (e) {
|
|
387
|
+
console.error("request beforeFn error", e, req);
|
|
388
|
+
return wrapperError({
|
|
389
|
+
code: 500,
|
|
390
|
+
message: "api request beforeFn error",
|
|
391
|
+
req
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
if (this.stop && !options?.noStop) {
|
|
395
|
+
const that = this;
|
|
396
|
+
await new Promise((resolve) => {
|
|
397
|
+
let timer = 0;
|
|
398
|
+
const detect = setInterval(() => {
|
|
399
|
+
if (!that.stop) {
|
|
400
|
+
clearInterval(detect);
|
|
401
|
+
resolve(true);
|
|
402
|
+
}
|
|
403
|
+
timer++;
|
|
404
|
+
if (timer > 30) {
|
|
405
|
+
console.error("request stop: timeout", req.url, timer);
|
|
406
|
+
}
|
|
407
|
+
}, 1000);
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
return _adapter(req).then(async (res) => {
|
|
411
|
+
try {
|
|
412
|
+
if (_afterResponse) {
|
|
413
|
+
return await _afterResponse(res, {
|
|
414
|
+
req,
|
|
415
|
+
res,
|
|
416
|
+
fetch: adapter2
|
|
417
|
+
});
|
|
537
418
|
}
|
|
538
419
|
return res;
|
|
539
|
-
|
|
420
|
+
} catch (e) {
|
|
421
|
+
console.error("request afterFn error", e, req);
|
|
422
|
+
return wrapperError({
|
|
423
|
+
code: 500,
|
|
424
|
+
message: "api request afterFn error",
|
|
425
|
+
req
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
before(fn) {
|
|
431
|
+
this.beforeRequest = fn;
|
|
432
|
+
}
|
|
433
|
+
after(fn) {
|
|
434
|
+
this.afterResponse = fn;
|
|
435
|
+
}
|
|
436
|
+
async fetchText(urlOrOptions, options) {
|
|
437
|
+
let _options = { ...options };
|
|
438
|
+
if (typeof urlOrOptions === "string" && !_options.url) {
|
|
439
|
+
_options.url = urlOrOptions;
|
|
440
|
+
}
|
|
441
|
+
if (typeof urlOrOptions === "object") {
|
|
442
|
+
_options = { ...urlOrOptions, ..._options };
|
|
443
|
+
}
|
|
444
|
+
const res = await adapter({
|
|
445
|
+
method: "GET",
|
|
446
|
+
..._options,
|
|
447
|
+
headers: {
|
|
448
|
+
...this.headers,
|
|
449
|
+
..._options?.headers || {}
|
|
450
|
+
}
|
|
451
|
+
});
|
|
452
|
+
if (res && !res.code) {
|
|
453
|
+
return {
|
|
454
|
+
code: 200,
|
|
455
|
+
data: res
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
return res;
|
|
459
|
+
}
|
|
540
460
|
}
|
|
541
461
|
class BaseQuery {
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
post(data, options)
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
get(data, options)
|
|
563
|
-
|
|
564
|
-
}
|
|
565
|
-
}
|
|
566
|
-
/**
|
|
567
|
-
* @deprecated
|
|
568
|
-
* 前端调用后端QueryRouter, 默认路径 /client/router
|
|
569
|
-
*/
|
|
570
|
-
class ClientQuery extends Query {
|
|
571
|
-
constructor(opts) {
|
|
572
|
-
super({ ...opts, url: opts?.url || '/client/router' });
|
|
573
|
-
}
|
|
462
|
+
query;
|
|
463
|
+
queryDefine;
|
|
464
|
+
constructor(opts) {
|
|
465
|
+
if (opts?.clientQuery) {
|
|
466
|
+
this.query = opts.clientQuery;
|
|
467
|
+
} else {
|
|
468
|
+
this.query = opts?.query;
|
|
469
|
+
}
|
|
470
|
+
if (opts.queryDefine) {
|
|
471
|
+
this.queryDefine = opts.queryDefine;
|
|
472
|
+
this.queryDefine.query = this.query;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
get chain() {
|
|
476
|
+
return this.queryDefine.queryChain;
|
|
477
|
+
}
|
|
478
|
+
post(data, options) {
|
|
479
|
+
return this.query.post(data, options);
|
|
480
|
+
}
|
|
481
|
+
get(data, options) {
|
|
482
|
+
return this.query.get(data, options);
|
|
483
|
+
}
|
|
574
484
|
}
|
|
575
485
|
|
|
576
|
-
|
|
577
|
-
* 前端调用后端QueryRouter, 封装 beforeRequest 和 wss
|
|
578
|
-
*/
|
|
486
|
+
// src/query-browser.ts
|
|
579
487
|
class QueryClient extends Query {
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
};
|
|
594
|
-
}
|
|
595
|
-
return opts;
|
|
488
|
+
tokenName;
|
|
489
|
+
storage;
|
|
490
|
+
token;
|
|
491
|
+
constructor(opts) {
|
|
492
|
+
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}`
|
|
596
501
|
};
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
502
|
+
}
|
|
503
|
+
return opts2;
|
|
504
|
+
};
|
|
505
|
+
if (opts?.io) {
|
|
506
|
+
this.createWs();
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
createWs(opts) {
|
|
510
|
+
this.qws = new QueryWs({ url: this.url, ...opts });
|
|
511
|
+
}
|
|
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
|
+
}
|
|
613
521
|
}
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
522
|
+
export {
|
|
523
|
+
wrapperError,
|
|
524
|
+
adapter,
|
|
525
|
+
QueryWs,
|
|
526
|
+
QueryClient,
|
|
527
|
+
Query,
|
|
528
|
+
BaseQuery
|
|
529
|
+
};
|