@kevisual/api 0.0.12 → 0.0.15
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 +4 -4
- package/query/query-proxy/index.ts +116 -27
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kevisual/api",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.15",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "mod.ts",
|
|
6
6
|
"scripts": {
|
|
@@ -18,12 +18,12 @@
|
|
|
18
18
|
"keywords": [],
|
|
19
19
|
"author": "abearxiong <xiongxiao@xiongxiao.me> (https://www.xiongxiao.me)",
|
|
20
20
|
"license": "MIT",
|
|
21
|
-
"packageManager": "pnpm@10.
|
|
21
|
+
"packageManager": "pnpm@10.27.0",
|
|
22
22
|
"type": "module",
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"@kevisual/cache": "^0.0.4",
|
|
25
25
|
"@kevisual/query": "^0.0.33",
|
|
26
|
-
"@kevisual/router": "^0.0.
|
|
26
|
+
"@kevisual/router": "^0.0.52",
|
|
27
27
|
"@kevisual/types": "^0.0.10",
|
|
28
28
|
"@kevisual/use-config": "^1.0.21",
|
|
29
29
|
"@types/bun": "^1.3.5",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"fast-glob": "^3.3.3"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@kevisual/js-filter": "^0.0.
|
|
35
|
+
"@kevisual/js-filter": "^0.0.3",
|
|
36
36
|
"@kevisual/load": "^0.0.6",
|
|
37
37
|
"es-toolkit": "^1.43.0",
|
|
38
38
|
"eventemitter3": "^5.0.1",
|
|
@@ -3,39 +3,59 @@ 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
|
|
7
|
-
export type
|
|
6
|
+
export const RouteTypeList = ['api', 'context', 'worker', 'page'] as const;
|
|
7
|
+
export type RouterViewItem = RouterViewApi | RouterViewContext | RouterViewWorker | RouteViewPage;
|
|
8
|
+
|
|
9
|
+
type RouteViewBase = {
|
|
10
|
+
id: string;
|
|
8
11
|
title: string;
|
|
9
12
|
description: string;
|
|
13
|
+
enabled?: boolean;
|
|
14
|
+
}
|
|
15
|
+
export type RouterViewApi = {
|
|
10
16
|
type: 'api',
|
|
11
17
|
api: {
|
|
12
18
|
url: string,
|
|
13
|
-
// 已初始化的query
|
|
19
|
+
// 已初始化的query实例,不需要编辑配置
|
|
14
20
|
query?: Query
|
|
15
21
|
}
|
|
16
|
-
}
|
|
22
|
+
} & RouteViewBase;
|
|
17
23
|
|
|
18
24
|
export type RouterViewContext = {
|
|
19
|
-
title: string;
|
|
20
|
-
description: string;
|
|
21
25
|
type: 'context',
|
|
22
26
|
context: {
|
|
23
27
|
key: string,
|
|
24
|
-
// 从context中获取router
|
|
28
|
+
// 从context中获取router,不需要编辑配置
|
|
25
29
|
router?: QueryRouterServer
|
|
26
30
|
}
|
|
27
|
-
}
|
|
31
|
+
} & RouteViewBase;
|
|
28
32
|
export type RouterViewWorker = {
|
|
29
|
-
title: string;
|
|
30
|
-
description: string;
|
|
31
33
|
type: 'worker',
|
|
32
34
|
worker: {
|
|
33
35
|
type: 'Worker' | 'SharedWorker' | 'serviceWorker',
|
|
34
36
|
url: string,
|
|
35
|
-
// 已初始化的worker
|
|
36
|
-
worker?: Worker | SharedWorker | ServiceWorker
|
|
37
|
+
// 已初始化的worker实例,不需要编辑配置
|
|
38
|
+
worker?: Worker | SharedWorker | ServiceWorker,
|
|
39
|
+
/**
|
|
40
|
+
* worker选项
|
|
41
|
+
* default: { type: 'module' }
|
|
42
|
+
*/
|
|
43
|
+
workerOptions?: {
|
|
44
|
+
type: 'module' | 'classic'
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
} & RouteViewBase;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* 注入 js 的url地址,使用importScripts加载
|
|
51
|
+
*/
|
|
52
|
+
export type RouteViewPage = {
|
|
53
|
+
type: 'page',
|
|
54
|
+
page: {
|
|
55
|
+
url: string,
|
|
37
56
|
}
|
|
38
|
-
}
|
|
57
|
+
} & RouteViewBase;
|
|
58
|
+
|
|
39
59
|
export type RouterViewQuery = {
|
|
40
60
|
id: string,
|
|
41
61
|
query: string,
|
|
@@ -72,7 +92,7 @@ export class QueryProxy {
|
|
|
72
92
|
}
|
|
73
93
|
}
|
|
74
94
|
async initRouterViewQuery() {
|
|
75
|
-
this.routerViewItems = this.routerViewItems
|
|
95
|
+
this.routerViewItems = this.routerViewItems.map(item => {
|
|
76
96
|
if (item.type === 'api' && item.api?.url) {
|
|
77
97
|
const url = item.api.url;
|
|
78
98
|
if (item?.api?.query) return item;
|
|
@@ -80,16 +100,19 @@ export class QueryProxy {
|
|
|
80
100
|
}
|
|
81
101
|
if (item.type === 'worker' && item.worker?.url) {
|
|
82
102
|
let viewItem = item as RouterViewWorker;
|
|
103
|
+
if (!item.worker?.workerOptions?.type) {
|
|
104
|
+
item.worker.workerOptions = { ...item.worker.workerOptions, type: 'module' };
|
|
105
|
+
}
|
|
83
106
|
if (item.worker.worker) {
|
|
84
107
|
return item;
|
|
85
108
|
}
|
|
86
109
|
let worker: Worker | SharedWorker | ServiceWorker | undefined = undefined;
|
|
87
110
|
if (item.worker.type === 'SharedWorker') {
|
|
88
|
-
worker = new SharedWorker(item.worker.url);
|
|
111
|
+
worker = new SharedWorker(item.worker.url, item.worker.workerOptions);
|
|
89
112
|
worker.port.start();
|
|
90
113
|
} else if (viewItem.worker.type === 'serviceWorker') {
|
|
91
114
|
if ('serviceWorker' in navigator) {
|
|
92
|
-
navigator.serviceWorker.register(viewItem.worker.url).then(function (registration) {
|
|
115
|
+
navigator.serviceWorker.register(viewItem.worker.url, item.worker.workerOptions).then(function (registration) {
|
|
93
116
|
console.debug('注册serviceWorker成功 ', registration.scope);
|
|
94
117
|
}, function (err) {
|
|
95
118
|
console.debug('注册 serviceWorker 失败: ', err);
|
|
@@ -98,7 +121,7 @@ export class QueryProxy {
|
|
|
98
121
|
console.warn('当前浏览器不支持serviceWorker');
|
|
99
122
|
}
|
|
100
123
|
} else {
|
|
101
|
-
worker = new Worker(viewItem.worker.url);
|
|
124
|
+
worker = new Worker(viewItem.worker.url, item.worker.workerOptions);
|
|
102
125
|
}
|
|
103
126
|
viewItem['worker']['worker'] = worker;
|
|
104
127
|
}
|
|
@@ -114,6 +137,9 @@ export class QueryProxy {
|
|
|
114
137
|
}
|
|
115
138
|
}
|
|
116
139
|
return item;
|
|
140
|
+
}).filter(item => {
|
|
141
|
+
const enabled = item.enabled ?? true;
|
|
142
|
+
return enabled;
|
|
117
143
|
});
|
|
118
144
|
}
|
|
119
145
|
|
|
@@ -124,21 +150,44 @@ export class QueryProxy {
|
|
|
124
150
|
async init() {
|
|
125
151
|
const routerViewItems = this.routerViewItems || [];
|
|
126
152
|
if (routerViewItems.length === 0) {
|
|
153
|
+
// 默认初始化api类型路由
|
|
127
154
|
await this.initApi();
|
|
128
155
|
return;
|
|
129
156
|
}
|
|
130
157
|
for (const item of routerViewItems) {
|
|
131
158
|
switch (item.type) {
|
|
132
159
|
case 'api':
|
|
133
|
-
this.initApi(item);
|
|
160
|
+
await this.initApi(item);
|
|
134
161
|
break;
|
|
135
162
|
case 'context':
|
|
163
|
+
await this.initContext(item);
|
|
136
164
|
break;
|
|
137
165
|
case 'worker':
|
|
138
|
-
this.initWorker(item);
|
|
166
|
+
await this.initWorker(item);
|
|
167
|
+
break;
|
|
168
|
+
case 'page':
|
|
169
|
+
await this.initPage(item);
|
|
139
170
|
break;
|
|
140
171
|
}
|
|
141
172
|
}
|
|
173
|
+
this.emitter.emit('initComplete');
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* 监听初始化完成
|
|
177
|
+
* @returns
|
|
178
|
+
*/
|
|
179
|
+
async listenInitComplete(): Promise<boolean> {
|
|
180
|
+
return new Promise((resolve) => {
|
|
181
|
+
const timer = setTimeout(() => {
|
|
182
|
+
this.emitter.removeAllListeners('initComplete');
|
|
183
|
+
resolve(false);
|
|
184
|
+
}, 3 * 60000); // 3分钟超时
|
|
185
|
+
const func = () => {
|
|
186
|
+
clearTimeout(timer);
|
|
187
|
+
resolve(true);
|
|
188
|
+
}
|
|
189
|
+
this.emitter.once('initComplete', func);
|
|
190
|
+
});
|
|
142
191
|
}
|
|
143
192
|
async initApi(item?: RouterViewApi) {
|
|
144
193
|
const that = this;
|
|
@@ -152,12 +201,14 @@ export class QueryProxy {
|
|
|
152
201
|
for (const r of _list) {
|
|
153
202
|
if (r.path || r.id) {
|
|
154
203
|
console.debug(`注册路由: [${r.path}] ${r?.key}`, 'API');
|
|
204
|
+
let metadata = r.metadata || {};
|
|
205
|
+
metadata.viewItem = item;
|
|
155
206
|
this.router.route({
|
|
156
207
|
path: r.path,
|
|
157
208
|
key: r.key || '',
|
|
158
209
|
id: r.id,
|
|
159
210
|
description: r.description,
|
|
160
|
-
metadata:
|
|
211
|
+
metadata: metadata,
|
|
161
212
|
}).define(async (ctx) => {
|
|
162
213
|
const msg = { ...ctx.query };
|
|
163
214
|
if (msg.token === undefined && that.token !== undefined) {
|
|
@@ -180,12 +231,14 @@ export class QueryProxy {
|
|
|
180
231
|
const routes = router.getList();
|
|
181
232
|
for (const r of routes) {
|
|
182
233
|
console.debug(`注册路由: [${r.path}] ${r?.key}`, 'Context');
|
|
234
|
+
let metadata = r.metadata || {};
|
|
235
|
+
metadata.viewItem = item;
|
|
183
236
|
this.router.route({
|
|
184
237
|
path: r.path,
|
|
185
238
|
key: r.key || '',
|
|
186
239
|
id: r.id,
|
|
187
240
|
description: r.description,
|
|
188
|
-
metadata:
|
|
241
|
+
metadata: metadata,
|
|
189
242
|
}).define(async (ctx) => {
|
|
190
243
|
const res = await router.run({ path: r.path, key: r.key, ...ctx.query });
|
|
191
244
|
ctx.forward(res)
|
|
@@ -207,14 +260,23 @@ export class QueryProxy {
|
|
|
207
260
|
console.warn('Worker not initialized');
|
|
208
261
|
return;
|
|
209
262
|
}
|
|
210
|
-
|
|
211
|
-
const
|
|
212
|
-
|
|
213
|
-
const msg = e.data;
|
|
263
|
+
const callResponse = (e: MessageEvent) => {
|
|
264
|
+
const msg = e.data;
|
|
265
|
+
if (msg.requestId) {
|
|
214
266
|
const requestId = msg.requestId;
|
|
215
267
|
that.emitter.emit(requestId, msg);
|
|
216
|
-
}
|
|
268
|
+
} else {
|
|
269
|
+
that.router.run(msg);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
if (item.worker.type === 'SharedWorker') {
|
|
273
|
+
const port = (worker as SharedWorker).port;
|
|
274
|
+
port.onmessage = callResponse;
|
|
217
275
|
port.start();
|
|
276
|
+
} else if (item.worker.type === 'serviceWorker') {
|
|
277
|
+
navigator.serviceWorker.addEventListener('message', callResponse);
|
|
278
|
+
} else {
|
|
279
|
+
(worker as Worker).onmessage = callResponse;
|
|
218
280
|
}
|
|
219
281
|
const callWorker = async (msg: any, viewItem: RouterViewWorker['worker']): Promise<Result> => {
|
|
220
282
|
const requestId = this.generateId();
|
|
@@ -243,6 +305,7 @@ export class QueryProxy {
|
|
|
243
305
|
});
|
|
244
306
|
});
|
|
245
307
|
}
|
|
308
|
+
|
|
246
309
|
const res = await callWorker({
|
|
247
310
|
path: "router",
|
|
248
311
|
key: 'list',
|
|
@@ -257,12 +320,14 @@ export class QueryProxy {
|
|
|
257
320
|
for (const r of _list) {
|
|
258
321
|
if (r.path || r.id) {
|
|
259
322
|
console.debug(`注册路由: [${r.path}] ${r?.key}`, 'API');
|
|
323
|
+
let metadata = r.metadata || {};
|
|
324
|
+
metadata.viewItem = item;
|
|
260
325
|
this.router.route({
|
|
261
326
|
path: r.path,
|
|
262
327
|
key: r.key || '',
|
|
263
328
|
id: r.id,
|
|
264
329
|
description: r.description,
|
|
265
|
-
metadata:
|
|
330
|
+
metadata: metadata,
|
|
266
331
|
}).define(async (ctx) => {
|
|
267
332
|
const msg = { ...ctx.query };
|
|
268
333
|
if (msg.token === undefined && that.token !== undefined) {
|
|
@@ -274,6 +339,23 @@ export class QueryProxy {
|
|
|
274
339
|
}
|
|
275
340
|
}
|
|
276
341
|
}
|
|
342
|
+
async initPage(item?: RouteViewPage) {
|
|
343
|
+
if (!item?.page?.url) {
|
|
344
|
+
console.warn('Page地址未提供');
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
const url = item.page.url;
|
|
348
|
+
try {
|
|
349
|
+
if (typeof window !== 'undefined') {
|
|
350
|
+
await import(url).then((module) => { }).catch((err) => {
|
|
351
|
+
console.error('引入Page脚本失败:', url, err);
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
} catch (e) {
|
|
355
|
+
console.warn('引入Page脚本失败:', url, e);
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
277
359
|
/**
|
|
278
360
|
* 列出路由
|
|
279
361
|
* @param filter
|
|
@@ -296,6 +378,13 @@ export class QueryProxy {
|
|
|
296
378
|
}
|
|
297
379
|
return routes;
|
|
298
380
|
}
|
|
381
|
+
async getViewQuery(viewId: string) {
|
|
382
|
+
const view = this.views.find(v => v.id === viewId);
|
|
383
|
+
if (view) {
|
|
384
|
+
return view.query;
|
|
385
|
+
}
|
|
386
|
+
return undefined;
|
|
387
|
+
}
|
|
299
388
|
/**
|
|
300
389
|
* 运行路由
|
|
301
390
|
* @param msg
|