@kevisual/api 0.0.14 → 0.0.16
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/package.json +1 -1
- package/query/query-proxy/index.ts +210 -74
package/package.json
CHANGED
|
@@ -3,12 +3,27 @@ import { QueryRouterServer, Route } from '@kevisual/router/src/route.ts';
|
|
|
3
3
|
import { filter } from '@kevisual/js-filter'
|
|
4
4
|
import { EventEmitter } from 'eventemitter3';
|
|
5
5
|
|
|
6
|
-
export
|
|
6
|
+
export const RouteTypeList = ['api', 'context', 'worker', 'page'] as const;
|
|
7
|
+
export type RouterViewItemInfo = RouterViewApi | RouterViewContext | RouterViewWorker | RouteViewPage;
|
|
8
|
+
export type RouterViewItem<T = {}> = RouterViewItemInfo & T;
|
|
9
|
+
|
|
7
10
|
type RouteViewBase = {
|
|
8
11
|
id: string;
|
|
9
12
|
title: string;
|
|
10
13
|
description: string;
|
|
11
14
|
enabled?: boolean;
|
|
15
|
+
/**
|
|
16
|
+
* 提示问题
|
|
17
|
+
*/
|
|
18
|
+
question?: string;
|
|
19
|
+
/**
|
|
20
|
+
* 响应数据
|
|
21
|
+
*/
|
|
22
|
+
response?: any;
|
|
23
|
+
/**
|
|
24
|
+
* 默认动作配置
|
|
25
|
+
*/
|
|
26
|
+
action?: { path?: string; key?: string; id?: string; payload?: any;[key: string]: any };
|
|
12
27
|
}
|
|
13
28
|
export type RouterViewApi = {
|
|
14
29
|
type: 'api',
|
|
@@ -44,6 +59,40 @@ export type RouterViewWorker = {
|
|
|
44
59
|
}
|
|
45
60
|
} & RouteViewBase;
|
|
46
61
|
|
|
62
|
+
/**
|
|
63
|
+
* 去掉不需要保存都服务器的数据
|
|
64
|
+
* @param item
|
|
65
|
+
* @returns
|
|
66
|
+
*/
|
|
67
|
+
export const pickRouterViewData = (item: RouterViewItem) => {
|
|
68
|
+
const { question, action, response, ...rest } = item;
|
|
69
|
+
if (rest.type === 'api') {
|
|
70
|
+
if (rest.api) {
|
|
71
|
+
delete rest.api.query;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (rest.type === 'worker') {
|
|
75
|
+
if (rest.worker) {
|
|
76
|
+
delete rest.worker.worker;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (rest.type === 'context') {
|
|
80
|
+
if (rest.context) {
|
|
81
|
+
delete rest.context.router;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return rest
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* 注入 js 的url地址,使用importScripts加载
|
|
88
|
+
*/
|
|
89
|
+
export type RouteViewPage = {
|
|
90
|
+
type: 'page',
|
|
91
|
+
page: {
|
|
92
|
+
url: string,
|
|
93
|
+
}
|
|
94
|
+
} & RouteViewBase;
|
|
95
|
+
|
|
47
96
|
export type RouterViewQuery = {
|
|
48
97
|
id: string,
|
|
49
98
|
query: string,
|
|
@@ -79,56 +128,61 @@ export class QueryProxy {
|
|
|
79
128
|
return undefined;
|
|
80
129
|
}
|
|
81
130
|
}
|
|
82
|
-
|
|
131
|
+
initRouterViewQuery() {
|
|
83
132
|
this.routerViewItems = this.routerViewItems.map(item => {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
133
|
+
return this.initRouterView(item);
|
|
134
|
+
}).filter(item => {
|
|
135
|
+
const enabled = item.enabled ?? true;
|
|
136
|
+
return enabled;
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
initRouterView(item: RouterViewItem) {
|
|
141
|
+
if (item.type === 'api' && item.api?.url) {
|
|
142
|
+
const url = item.api.url;
|
|
143
|
+
if (item?.api?.query) return item;
|
|
144
|
+
item['api'] = { url: url, query: new Query({ url: url }) };
|
|
145
|
+
}
|
|
146
|
+
if (item.type === 'worker' && item.worker?.url) {
|
|
147
|
+
let viewItem = item as RouterViewWorker;
|
|
148
|
+
if (!item.worker?.workerOptions?.type) {
|
|
149
|
+
item.worker.workerOptions = { ...item.worker.workerOptions, type: 'module' };
|
|
88
150
|
}
|
|
89
|
-
if (item.
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
navigator.serviceWorker.register(viewItem.worker.url, item.worker.workerOptions).then(function (registration) {
|
|
104
|
-
console.debug('注册serviceWorker成功 ', registration.scope);
|
|
105
|
-
}, function (err) {
|
|
106
|
-
console.debug('注册 serviceWorker 失败: ', err);
|
|
107
|
-
});
|
|
108
|
-
} else {
|
|
109
|
-
console.warn('当前浏览器不支持serviceWorker');
|
|
110
|
-
}
|
|
151
|
+
if (item.worker.worker) {
|
|
152
|
+
return item;
|
|
153
|
+
}
|
|
154
|
+
let worker: Worker | SharedWorker | ServiceWorker | undefined = undefined;
|
|
155
|
+
if (item.worker.type === 'SharedWorker') {
|
|
156
|
+
worker = new SharedWorker(item.worker.url, item.worker.workerOptions);
|
|
157
|
+
worker.port.start();
|
|
158
|
+
} else if (viewItem.worker.type === 'serviceWorker') {
|
|
159
|
+
if ('serviceWorker' in navigator) {
|
|
160
|
+
navigator.serviceWorker.register(viewItem.worker.url, item.worker.workerOptions).then(function (registration) {
|
|
161
|
+
console.debug('注册serviceWorker成功 ', registration.scope);
|
|
162
|
+
}, function (err) {
|
|
163
|
+
console.debug('注册 serviceWorker 失败: ', err);
|
|
164
|
+
});
|
|
111
165
|
} else {
|
|
112
|
-
|
|
166
|
+
console.warn('当前浏览器不支持serviceWorker');
|
|
113
167
|
}
|
|
114
|
-
|
|
168
|
+
} else {
|
|
169
|
+
worker = new Worker(viewItem.worker.url, item.worker.workerOptions);
|
|
115
170
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
const context = globalThis['context'] || {}
|
|
122
|
-
const router = context[item.context.key] as QueryRouterServer;
|
|
123
|
-
if (router) {
|
|
124
|
-
item['context']['router'] = router;
|
|
125
|
-
}
|
|
171
|
+
viewItem['worker']['worker'] = worker;
|
|
172
|
+
}
|
|
173
|
+
if (item.type === 'context' && item.context?.key) {
|
|
174
|
+
if (item.context?.router) {
|
|
175
|
+
return item;
|
|
126
176
|
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
const
|
|
130
|
-
|
|
131
|
-
|
|
177
|
+
// @ts-ignore
|
|
178
|
+
const context = globalThis['context'] || {}
|
|
179
|
+
const router = context[item.context.key] as QueryRouterServer;
|
|
180
|
+
if (router) {
|
|
181
|
+
item['context']['router'] = router;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return item;
|
|
185
|
+
|
|
132
186
|
}
|
|
133
187
|
|
|
134
188
|
/**
|
|
@@ -138,21 +192,44 @@ export class QueryProxy {
|
|
|
138
192
|
async init() {
|
|
139
193
|
const routerViewItems = this.routerViewItems || [];
|
|
140
194
|
if (routerViewItems.length === 0) {
|
|
195
|
+
// 默认初始化api类型路由
|
|
141
196
|
await this.initApi();
|
|
142
197
|
return;
|
|
143
198
|
}
|
|
144
199
|
for (const item of routerViewItems) {
|
|
145
200
|
switch (item.type) {
|
|
146
201
|
case 'api':
|
|
147
|
-
this.initApi(item);
|
|
202
|
+
await this.initApi(item);
|
|
148
203
|
break;
|
|
149
204
|
case 'context':
|
|
205
|
+
await this.initContext(item);
|
|
150
206
|
break;
|
|
151
207
|
case 'worker':
|
|
152
|
-
this.initWorker(item);
|
|
208
|
+
await this.initWorker(item);
|
|
209
|
+
break;
|
|
210
|
+
case 'page':
|
|
211
|
+
await this.initPage(item);
|
|
153
212
|
break;
|
|
154
213
|
}
|
|
155
214
|
}
|
|
215
|
+
this.emitter.emit('initComplete');
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* 监听初始化完成
|
|
219
|
+
* @returns
|
|
220
|
+
*/
|
|
221
|
+
async listenInitComplete(): Promise<boolean> {
|
|
222
|
+
return new Promise((resolve) => {
|
|
223
|
+
const timer = setTimeout(() => {
|
|
224
|
+
this.emitter.removeAllListeners('initComplete');
|
|
225
|
+
resolve(false);
|
|
226
|
+
}, 3 * 60000); // 3分钟超时
|
|
227
|
+
const func = () => {
|
|
228
|
+
clearTimeout(timer);
|
|
229
|
+
resolve(true);
|
|
230
|
+
}
|
|
231
|
+
this.emitter.once('initComplete', func);
|
|
232
|
+
});
|
|
156
233
|
}
|
|
157
234
|
async initApi(item?: RouterViewApi) {
|
|
158
235
|
const that = this;
|
|
@@ -213,7 +290,35 @@ export class QueryProxy {
|
|
|
213
290
|
generateId() {
|
|
214
291
|
return 'route_' + Math.random().toString(36).substring(2, 9);
|
|
215
292
|
}
|
|
216
|
-
async
|
|
293
|
+
async callWorker(msg: any, viewItem: RouterViewWorker['worker']): Promise<Result> {
|
|
294
|
+
const that = this;
|
|
295
|
+
const requestId = this.generateId();
|
|
296
|
+
const worker = viewItem?.worker;
|
|
297
|
+
if (!worker) {
|
|
298
|
+
return { code: 500, message: 'Worker未初始化' };
|
|
299
|
+
}
|
|
300
|
+
let port: MessagePort | Worker | ServiceWorker;
|
|
301
|
+
if (viewItem.type === 'SharedWorker') {
|
|
302
|
+
port = (worker as SharedWorker).port;
|
|
303
|
+
} else {
|
|
304
|
+
port = worker as Worker | ServiceWorker;
|
|
305
|
+
}
|
|
306
|
+
port.postMessage({
|
|
307
|
+
...msg,
|
|
308
|
+
requestId: requestId,
|
|
309
|
+
})
|
|
310
|
+
return new Promise((resolve) => {
|
|
311
|
+
const timer = setTimeout(() => {
|
|
312
|
+
that.emitter.removeAllListeners(requestId);
|
|
313
|
+
resolve({ code: 500, message: '请求超时' });
|
|
314
|
+
}, 3 * 60 * 1000); // 3分钟超时
|
|
315
|
+
that.emitter.once(requestId, (res: any) => {
|
|
316
|
+
clearTimeout(timer);
|
|
317
|
+
resolve(res);
|
|
318
|
+
});
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
async initWorker(item?: RouterViewWorker, initRoutes: boolean = true) {
|
|
217
322
|
const that = this;
|
|
218
323
|
if (!item?.worker?.url) {
|
|
219
324
|
console.warn('Worker URL not provided');
|
|
@@ -243,33 +348,10 @@ export class QueryProxy {
|
|
|
243
348
|
} else {
|
|
244
349
|
(worker as Worker).onmessage = callResponse;
|
|
245
350
|
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
const worker = viewItem?.worker;
|
|
249
|
-
if (!worker) {
|
|
250
|
-
return { code: 500, message: 'Worker未初始化' };
|
|
251
|
-
}
|
|
252
|
-
let port: MessagePort | Worker | ServiceWorker;
|
|
253
|
-
if (viewItem.type === 'SharedWorker') {
|
|
254
|
-
port = (worker as SharedWorker).port;
|
|
255
|
-
} else {
|
|
256
|
-
port = worker as Worker | ServiceWorker;
|
|
257
|
-
}
|
|
258
|
-
port.postMessage({
|
|
259
|
-
...msg,
|
|
260
|
-
requestId: requestId,
|
|
261
|
-
})
|
|
262
|
-
return new Promise((resolve) => {
|
|
263
|
-
const timer = setTimeout(() => {
|
|
264
|
-
that.emitter.removeAllListeners(requestId);
|
|
265
|
-
resolve({ code: 500, message: '请求超时' });
|
|
266
|
-
}, 3 * 60 * 1000); // 3分钟超时
|
|
267
|
-
that.emitter.once(requestId, (res: any) => {
|
|
268
|
-
clearTimeout(timer);
|
|
269
|
-
resolve(res);
|
|
270
|
-
});
|
|
271
|
-
});
|
|
351
|
+
if (!initRoutes) {
|
|
352
|
+
return;
|
|
272
353
|
}
|
|
354
|
+
const callWorker = this.callWorker.bind(this);
|
|
273
355
|
|
|
274
356
|
const res = await callWorker({
|
|
275
357
|
path: "router",
|
|
@@ -304,6 +386,23 @@ export class QueryProxy {
|
|
|
304
386
|
}
|
|
305
387
|
}
|
|
306
388
|
}
|
|
389
|
+
async initPage(item?: RouteViewPage) {
|
|
390
|
+
if (!item?.page?.url) {
|
|
391
|
+
console.warn('Page地址未提供');
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
const url = item.page.url;
|
|
395
|
+
try {
|
|
396
|
+
if (typeof window !== 'undefined') {
|
|
397
|
+
await import(url).then((module) => { }).catch((err) => {
|
|
398
|
+
console.error('引入Page脚本失败:', url, err);
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
} catch (e) {
|
|
402
|
+
console.warn('引入Page脚本失败:', url, e);
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
307
406
|
/**
|
|
308
407
|
* 列出路由
|
|
309
408
|
* @param filter
|
|
@@ -341,6 +440,43 @@ export class QueryProxy {
|
|
|
341
440
|
async run(msg: { id?: string, path?: string, key?: string }) {
|
|
342
441
|
return await this.router.run(msg);
|
|
343
442
|
}
|
|
443
|
+
|
|
444
|
+
async runByRouteView(routeView: RouterViewItem) {
|
|
445
|
+
if (routeView.response) {
|
|
446
|
+
return routeView;
|
|
447
|
+
}
|
|
448
|
+
const item = this.initRouterView(routeView);
|
|
449
|
+
if (item.type === 'api' && item.api?.url) {
|
|
450
|
+
const query = item.api.query!;
|
|
451
|
+
const res = await query.post<any>(item.action || {});
|
|
452
|
+
item.response = res;
|
|
453
|
+
return item;
|
|
454
|
+
} else if (item.type === 'api') {
|
|
455
|
+
item.response = { code: 500, message: 'API URL未配置' };
|
|
456
|
+
return item;
|
|
457
|
+
}
|
|
458
|
+
if (item.type === 'context' && item.context?.router) {
|
|
459
|
+
const router = item.context.router;
|
|
460
|
+
const res = await router.run(item.action || {});
|
|
461
|
+
item.response = res;
|
|
462
|
+
return item;
|
|
463
|
+
}
|
|
464
|
+
if (item.type === 'page') {
|
|
465
|
+
await this.initPage(item);
|
|
466
|
+
const res = await this.router.run(item.action || {});
|
|
467
|
+
item.response = res;
|
|
468
|
+
return item;
|
|
469
|
+
}
|
|
470
|
+
if (item.type === 'worker' && item.worker?.worker) {
|
|
471
|
+
await this.initWorker(item, false);
|
|
472
|
+
const callWorker = this.callWorker.bind(this);
|
|
473
|
+
const res = await callWorker(item.action || {}, item.worker);
|
|
474
|
+
item.response = res;
|
|
475
|
+
return item;
|
|
476
|
+
}
|
|
477
|
+
item.response = { code: 500, message: '无法处理的路由类型' };
|
|
478
|
+
return item;
|
|
479
|
+
}
|
|
344
480
|
}
|
|
345
481
|
|
|
346
482
|
type RouterItem = {
|