@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kevisual/api",
3
- "version": "0.0.14",
3
+ "version": "0.0.16",
4
4
  "description": "",
5
5
  "main": "mod.ts",
6
6
  "scripts": {
@@ -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 type RouterViewItem = RouterViewApi | RouterViewContext | RouterViewWorker;
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
- async initRouterViewQuery() {
131
+ initRouterViewQuery() {
83
132
  this.routerViewItems = this.routerViewItems.map(item => {
84
- if (item.type === 'api' && item.api?.url) {
85
- const url = item.api.url;
86
- if (item?.api?.query) return item;
87
- item['api'] = { url: url, query: new Query({ url: url }) };
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.type === 'worker' && item.worker?.url) {
90
- let viewItem = item as RouterViewWorker;
91
- if (!item.worker?.workerOptions?.type) {
92
- item.worker.workerOptions = { ...item.worker.workerOptions, type: 'module' };
93
- }
94
- if (item.worker.worker) {
95
- return item;
96
- }
97
- let worker: Worker | SharedWorker | ServiceWorker | undefined = undefined;
98
- if (item.worker.type === 'SharedWorker') {
99
- worker = new SharedWorker(item.worker.url, item.worker.workerOptions);
100
- worker.port.start();
101
- } else if (viewItem.worker.type === 'serviceWorker') {
102
- if ('serviceWorker' in navigator) {
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
- worker = new Worker(viewItem.worker.url, item.worker.workerOptions);
166
+ console.warn('当前浏览器不支持serviceWorker');
113
167
  }
114
- viewItem['worker']['worker'] = worker;
168
+ } else {
169
+ worker = new Worker(viewItem.worker.url, item.worker.workerOptions);
115
170
  }
116
- if (item.type === 'context' && item.context?.key) {
117
- if (item.context?.router) {
118
- return item;
119
- }
120
- // @ts-ignore
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
- return item;
128
- }).filter(item => {
129
- const enabled = item.enabled ?? true;
130
- return enabled;
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 initWorker(item?: RouterViewWorker) {
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
- const callWorker = async (msg: any, viewItem: RouterViewWorker['worker']): Promise<Result> => {
247
- const requestId = this.generateId();
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 = {