@kevisual/router 0.0.85 → 0.0.87

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.
@@ -450,7 +450,52 @@ declare class QueryRouterServer<C extends SimpleObject = SimpleObject> extends Q
450
450
  key?: string;
451
451
  payload?: any;
452
452
  }, ctx?: Partial<RouteContext<C>>): Promise<any>;
453
+ runAction<T extends {
454
+ id?: string;
455
+ path?: string;
456
+ key?: string;
457
+ metadata?: {
458
+ args?: any;
459
+ };
460
+ } = {}>(api: T, payload: RunActionPayload<T>, ctx?: RouteContext<C>): Promise<any>;
453
461
  }
462
+ /** JSON Schema 基本类型映射到 TypeScript 类型 */
463
+ type JsonSchemaTypeToTS<T> = T extends {
464
+ type: "string";
465
+ } ? string : T extends {
466
+ type: "boolean";
467
+ } ? boolean : T extends {
468
+ type: "number";
469
+ } ? number : T extends {
470
+ type: "integer";
471
+ } ? number : T extends {
472
+ type: "object";
473
+ } ? object : T extends {
474
+ type: "array";
475
+ } ? any[] : any;
476
+ /** 将 args shape(key -> JSON Schema 类型)转换为 payload 类型,支持 optional: true 的字段为可选 */
477
+ type ArgsShapeToPayload<T> = {
478
+ [K in keyof T as T[K] extends {
479
+ optional: true;
480
+ } ? never : K]: JsonSchemaTypeToTS<T[K]>;
481
+ } & {
482
+ [K in keyof T as T[K] extends {
483
+ optional: true;
484
+ } ? K : never]?: JsonSchemaTypeToTS<T[K]>;
485
+ };
486
+ /** 处理两种 args 格式:完整 JSON Schema(含 properties)或简单 key->type 映射 */
487
+ type ArgsToPayload<T> = T extends {
488
+ type: "object";
489
+ properties: infer P;
490
+ } ? ArgsShapeToPayload<P> : ArgsShapeToPayload<T>;
491
+ /** 从 API 定义中提取 metadata.args */
492
+ type ExtractArgs<T> = T extends {
493
+ metadata: {
494
+ args: infer A;
495
+ };
496
+ } ? A : {};
497
+ /** runAction 第二个参数的类型,根据第一个参数的 metadata.args 推断 */
498
+ type RunActionPayload<T> = ArgsToPayload<ExtractArgs<T>>;
454
499
 
455
500
  type Cors = {
456
501
  /**
@@ -522,9 +522,54 @@ declare class QueryRouterServer<C extends SimpleObject$1 = SimpleObject$1> exten
522
522
  key?: string;
523
523
  payload?: any;
524
524
  }, ctx?: Partial<RouteContext<C>>): Promise<any>;
525
+ runAction<T extends {
526
+ id?: string;
527
+ path?: string;
528
+ key?: string;
529
+ metadata?: {
530
+ args?: any;
531
+ };
532
+ } = {}>(api: T, payload: RunActionPayload<T>, ctx?: RouteContext<C>): Promise<any>;
525
533
  }
526
534
  declare class Mini extends QueryRouterServer {
527
535
  }
536
+ /** JSON Schema 基本类型映射到 TypeScript 类型 */
537
+ type JsonSchemaTypeToTS<T> = T extends {
538
+ type: "string";
539
+ } ? string : T extends {
540
+ type: "boolean";
541
+ } ? boolean : T extends {
542
+ type: "number";
543
+ } ? number : T extends {
544
+ type: "integer";
545
+ } ? number : T extends {
546
+ type: "object";
547
+ } ? object : T extends {
548
+ type: "array";
549
+ } ? any[] : any;
550
+ /** 将 args shape(key -> JSON Schema 类型)转换为 payload 类型,支持 optional: true 的字段为可选 */
551
+ type ArgsShapeToPayload<T> = {
552
+ [K in keyof T as T[K] extends {
553
+ optional: true;
554
+ } ? never : K]: JsonSchemaTypeToTS<T[K]>;
555
+ } & {
556
+ [K in keyof T as T[K] extends {
557
+ optional: true;
558
+ } ? K : never]?: JsonSchemaTypeToTS<T[K]>;
559
+ };
560
+ /** 处理两种 args 格式:完整 JSON Schema(含 properties)或简单 key->type 映射 */
561
+ type ArgsToPayload<T> = T extends {
562
+ type: "object";
563
+ properties: infer P;
564
+ } ? ArgsShapeToPayload<P> : ArgsShapeToPayload<T>;
565
+ /** 从 API 定义中提取 metadata.args */
566
+ type ExtractArgs<T> = T extends {
567
+ metadata: {
568
+ args: infer A;
569
+ };
570
+ } ? A : {};
571
+ /** runAction 第二个参数的类型,根据第一个参数的 metadata.args 推断 */
572
+ type RunActionPayload<T> = ArgsToPayload<ExtractArgs<T>>;
528
573
 
529
574
  type BaseRule = {
530
575
  value?: any;
@@ -14583,6 +14583,10 @@ class QueryRouterServer extends QueryRouter {
14583
14583
  }
14584
14584
  return super.run(msg, ctx);
14585
14585
  }
14586
+ async runAction(api2, payload, ctx) {
14587
+ const { path, key, id } = api2;
14588
+ return this.run({ path, key, id, payload }, ctx);
14589
+ }
14586
14590
  }
14587
14591
 
14588
14592
  class Mini extends QueryRouterServer {
@@ -447,7 +447,52 @@ declare class QueryRouterServer<C extends SimpleObject$1 = SimpleObject$1> exten
447
447
  key?: string;
448
448
  payload?: any;
449
449
  }, ctx?: Partial<RouteContext<C>>): Promise<any>;
450
+ runAction<T extends {
451
+ id?: string;
452
+ path?: string;
453
+ key?: string;
454
+ metadata?: {
455
+ args?: any;
456
+ };
457
+ } = {}>(api: T, payload: RunActionPayload<T>, ctx?: RouteContext<C>): Promise<any>;
450
458
  }
459
+ /** JSON Schema 基本类型映射到 TypeScript 类型 */
460
+ type JsonSchemaTypeToTS<T> = T extends {
461
+ type: "string";
462
+ } ? string : T extends {
463
+ type: "boolean";
464
+ } ? boolean : T extends {
465
+ type: "number";
466
+ } ? number : T extends {
467
+ type: "integer";
468
+ } ? number : T extends {
469
+ type: "object";
470
+ } ? object : T extends {
471
+ type: "array";
472
+ } ? any[] : any;
473
+ /** 将 args shape(key -> JSON Schema 类型)转换为 payload 类型,支持 optional: true 的字段为可选 */
474
+ type ArgsShapeToPayload<T> = {
475
+ [K in keyof T as T[K] extends {
476
+ optional: true;
477
+ } ? never : K]: JsonSchemaTypeToTS<T[K]>;
478
+ } & {
479
+ [K in keyof T as T[K] extends {
480
+ optional: true;
481
+ } ? K : never]?: JsonSchemaTypeToTS<T[K]>;
482
+ };
483
+ /** 处理两种 args 格式:完整 JSON Schema(含 properties)或简单 key->type 映射 */
484
+ type ArgsToPayload<T> = T extends {
485
+ type: "object";
486
+ properties: infer P;
487
+ } ? ArgsShapeToPayload<P> : ArgsShapeToPayload<T>;
488
+ /** 从 API 定义中提取 metadata.args */
489
+ type ExtractArgs<T> = T extends {
490
+ metadata: {
491
+ args: infer A;
492
+ };
493
+ } ? A : {};
494
+ /** runAction 第二个参数的类型,根据第一个参数的 metadata.args 推断 */
495
+ type RunActionPayload<T> = ArgsToPayload<ExtractArgs<T>>;
451
496
 
452
497
  type RouteObject = {
453
498
  [key: string]: RouteOpts;
package/dist/router.d.ts CHANGED
@@ -528,9 +528,54 @@ declare class QueryRouterServer<C extends SimpleObject$1 = SimpleObject$1> exten
528
528
  key?: string;
529
529
  payload?: any;
530
530
  }, ctx?: Partial<RouteContext<C>>): Promise<any>;
531
+ runAction<T extends {
532
+ id?: string;
533
+ path?: string;
534
+ key?: string;
535
+ metadata?: {
536
+ args?: any;
537
+ };
538
+ } = {}>(api: T, payload: RunActionPayload<T>, ctx?: RouteContext<C>): Promise<any>;
531
539
  }
532
540
  declare class Mini extends QueryRouterServer {
533
541
  }
542
+ /** JSON Schema 基本类型映射到 TypeScript 类型 */
543
+ type JsonSchemaTypeToTS<T> = T extends {
544
+ type: "string";
545
+ } ? string : T extends {
546
+ type: "boolean";
547
+ } ? boolean : T extends {
548
+ type: "number";
549
+ } ? number : T extends {
550
+ type: "integer";
551
+ } ? number : T extends {
552
+ type: "object";
553
+ } ? object : T extends {
554
+ type: "array";
555
+ } ? any[] : any;
556
+ /** 将 args shape(key -> JSON Schema 类型)转换为 payload 类型,支持 optional: true 的字段为可选 */
557
+ type ArgsShapeToPayload<T> = {
558
+ [K in keyof T as T[K] extends {
559
+ optional: true;
560
+ } ? never : K]: JsonSchemaTypeToTS<T[K]>;
561
+ } & {
562
+ [K in keyof T as T[K] extends {
563
+ optional: true;
564
+ } ? K : never]?: JsonSchemaTypeToTS<T[K]>;
565
+ };
566
+ /** 处理两种 args 格式:完整 JSON Schema(含 properties)或简单 key->type 映射 */
567
+ type ArgsToPayload<T> = T extends {
568
+ type: "object";
569
+ properties: infer P;
570
+ } ? ArgsShapeToPayload<P> : ArgsShapeToPayload<T>;
571
+ /** 从 API 定义中提取 metadata.args */
572
+ type ExtractArgs<T> = T extends {
573
+ metadata: {
574
+ args: infer A;
575
+ };
576
+ } ? A : {};
577
+ /** runAction 第二个参数的类型,根据第一个参数的 metadata.args 推断 */
578
+ type RunActionPayload<T> = ArgsToPayload<ExtractArgs<T>>;
534
579
 
535
580
  type BaseRule = {
536
581
  value?: any;
package/dist/router.js CHANGED
@@ -17416,6 +17416,10 @@ class QueryRouterServer extends QueryRouter {
17416
17416
  }
17417
17417
  return super.run(msg, ctx);
17418
17418
  }
17419
+ async runAction(api2, payload, ctx) {
17420
+ const { path, key, id } = api2;
17421
+ return this.run({ path, key, id, payload }, ctx);
17422
+ }
17419
17423
  }
17420
17424
 
17421
17425
  class Mini extends QueryRouterServer {
package/dist/ws.d.ts CHANGED
@@ -497,7 +497,52 @@ declare class QueryRouterServer<C extends SimpleObject = SimpleObject> extends Q
497
497
  key?: string;
498
498
  payload?: any;
499
499
  }, ctx?: Partial<RouteContext<C>>): Promise<any>;
500
+ runAction<T extends {
501
+ id?: string;
502
+ path?: string;
503
+ key?: string;
504
+ metadata?: {
505
+ args?: any;
506
+ };
507
+ } = {}>(api: T, payload: RunActionPayload<T>, ctx?: RouteContext<C>): Promise<any>;
500
508
  }
509
+ /** JSON Schema 基本类型映射到 TypeScript 类型 */
510
+ type JsonSchemaTypeToTS<T> = T extends {
511
+ type: "string";
512
+ } ? string : T extends {
513
+ type: "boolean";
514
+ } ? boolean : T extends {
515
+ type: "number";
516
+ } ? number : T extends {
517
+ type: "integer";
518
+ } ? number : T extends {
519
+ type: "object";
520
+ } ? object : T extends {
521
+ type: "array";
522
+ } ? any[] : any;
523
+ /** 将 args shape(key -> JSON Schema 类型)转换为 payload 类型,支持 optional: true 的字段为可选 */
524
+ type ArgsShapeToPayload<T> = {
525
+ [K in keyof T as T[K] extends {
526
+ optional: true;
527
+ } ? never : K]: JsonSchemaTypeToTS<T[K]>;
528
+ } & {
529
+ [K in keyof T as T[K] extends {
530
+ optional: true;
531
+ } ? K : never]?: JsonSchemaTypeToTS<T[K]>;
532
+ };
533
+ /** 处理两种 args 格式:完整 JSON Schema(含 properties)或简单 key->type 映射 */
534
+ type ArgsToPayload<T> = T extends {
535
+ type: "object";
536
+ properties: infer P;
537
+ } ? ArgsShapeToPayload<P> : ArgsShapeToPayload<T>;
538
+ /** 从 API 定义中提取 metadata.args */
539
+ type ExtractArgs<T> = T extends {
540
+ metadata: {
541
+ args: infer A;
542
+ };
543
+ } ? A : {};
544
+ /** runAction 第二个参数的类型,根据第一个参数的 metadata.args 推断 */
545
+ type RunActionPayload<T> = ArgsToPayload<ExtractArgs<T>>;
501
546
 
502
547
  type Cors = {
503
548
  /**
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package",
3
3
  "name": "@kevisual/router",
4
- "version": "0.0.85",
4
+ "version": "0.0.87",
5
5
  "description": "",
6
6
  "type": "module",
7
7
  "main": "./dist/router.js",
@@ -27,14 +27,15 @@
27
27
  "@kevisual/dts": "^0.0.4",
28
28
  "@kevisual/js-filter": "^0.0.5",
29
29
  "@kevisual/local-proxy": "^0.0.8",
30
- "@kevisual/query": "^0.0.52",
30
+ "@kevisual/query": "^0.0.53",
31
31
  "@kevisual/use-config": "^1.0.30",
32
- "@opencode-ai/plugin": "^1.2.16",
32
+ "@opencode-ai/plugin": "^1.2.20",
33
33
  "@types/bun": "^1.3.10",
34
- "@types/node": "^25.3.3",
34
+ "@types/node": "^25.3.5",
35
35
  "@types/send": "^1.2.1",
36
36
  "@types/ws": "^8.18.1",
37
37
  "@types/xml2js": "^0.4.14",
38
+ "commander": "^14.0.3",
38
39
  "eventemitter3": "^5.0.4",
39
40
  "fast-glob": "^3.3.3",
40
41
  "hono": "^4.12.5",
@@ -59,6 +60,7 @@
59
60
  "exports": {
60
61
  ".": "./dist/router.js",
61
62
  "./browser": "./dist/router-browser.js",
63
+ "./commander": "./dist/commander.js",
62
64
  "./simple": "./dist/router-simple.js",
63
65
  "./opencode": "./dist/opencode.js",
64
66
  "./skill": "./dist/app.js",
@@ -0,0 +1,115 @@
1
+ import { program } from 'commander';
2
+ import { App } from './app.ts';
3
+
4
+ export const groupByPath = (routes: App['routes']) => {
5
+ return routes.reduce((acc, route) => {
6
+ const path = route.path || 'default';
7
+ if (!acc[path]) {
8
+ acc[path] = [];
9
+ }
10
+ acc[path].push(route);
11
+ return acc;
12
+ }, {} as Record<string, typeof routes>);
13
+ }
14
+ export const parseArgs = (args: string) => {
15
+ try {
16
+ return JSON.parse(args);
17
+ } catch {
18
+ // 尝试解析 a=b b=c 格式
19
+ const result: Record<string, string> = {};
20
+ const pairs = args.match(/(\S+?)=(\S+)/g);
21
+ if (pairs && pairs.length > 0) {
22
+ for (const pair of pairs) {
23
+ const idx = pair.indexOf('=');
24
+ const key = pair.slice(0, idx);
25
+ const value = pair.slice(idx + 1);
26
+ result[key] = value;
27
+ }
28
+ return result;
29
+ }
30
+ throw new Error('Invalid arguments: expected JSON or key=value pairs (e.g. a=b c=d)');
31
+ }
32
+ }
33
+ export const parseDescription = (route: App['routes'][number]) => {
34
+ let desc = '';
35
+ if (route.metadata?.skill) {
36
+ desc += `\n\t=====${route.metadata.skill}=====\n`;
37
+ }
38
+ let hasSummary = false;
39
+ if (route.metadata?.summary) {
40
+ desc += `\t${route.metadata.summary}`;
41
+ hasSummary = true;
42
+ }
43
+ if (route.metadata?.args) {
44
+ const argsLines = Object.entries(route.metadata.args).map(([key, schema]: [string, any]) => {
45
+ const defType: string = schema?._def?.type ?? schema?.type ?? '';
46
+ const isOptional = defType === 'optional';
47
+ const innerType: string = isOptional
48
+ ? (schema?._def?.innerType?.type ?? schema?._def?.innerType?._def?.type ?? '')
49
+ : defType;
50
+ const description: string =
51
+ schema?.description ??
52
+ schema?._def?.description ??
53
+ '';
54
+ const optionalMark = isOptional ? '?' : '';
55
+ const descPart = description ? ` ${description}` : '';
56
+ return `\t - ${key}${optionalMark}: ${innerType}${descPart}`;
57
+ });
58
+ desc += '\n\targs:\n' + argsLines.join('\n');
59
+ }
60
+ if (route.description && !hasSummary) {
61
+ desc += `\t - ${route.description}`;
62
+ }
63
+ return desc;
64
+ }
65
+ export const createCommand = (opts: { app: App, program: typeof program }) => {
66
+ const { app, program } = opts;
67
+ const routes = app.routes;
68
+
69
+
70
+ const groupRoutes = groupByPath(routes);
71
+ for (const path in groupRoutes) {
72
+ const routeList = groupRoutes[path];
73
+ const keys = routeList.map(route => route.key).filter(Boolean);
74
+ const subProgram = program.command(path).description(`路由《${path}》 ${keys.length > 0 ? ': ' + keys.join(', ') : ''}`);
75
+ routeList.forEach(route => {
76
+ if (!route.key) return;
77
+ const description = parseDescription(route);
78
+ subProgram.command(route.key)
79
+ .description(description || '')
80
+ .option('--args <args>', 'JSON字符串参数,传递给命令执行')
81
+ .action(async (options) => {
82
+ const output = (data: any) => {
83
+ if (typeof data === 'object') {
84
+ process.stdout.write(JSON.stringify(data, null, 2) + '\n');
85
+ } else {
86
+ process.stdout.write(String(data) + '\n');
87
+ }
88
+ }
89
+ try {
90
+ const args = options.args ? parseArgs(options.args) : {};
91
+ // 这里可以添加实际的命令执行逻辑,例如调用对应的路由处理函数
92
+ const res = await app.run({ path, key: route.key, payload: args }, { appId: app.appId });
93
+ if (res.code === 200) {
94
+ output(res.data);
95
+ } else {
96
+ output(`Error: ${res.message}`);
97
+ }
98
+ } catch (error) {
99
+ output(`Execution error: ${error instanceof Error ? error.message : String(error)}`);
100
+ }
101
+ });
102
+ });
103
+ }
104
+ }
105
+
106
+ program.parse(process.argv);
107
+
108
+ export const parse = (opts: { app: App, description?: string, parse?: boolean }) => {
109
+ const { app, description, parse } = opts;
110
+ program.description(description || 'Router 命令行工具');
111
+ createCommand({ app: app as App, program });
112
+ if (parse) {
113
+ program.parse(process.argv);
114
+ }
115
+ }
package/src/route.ts CHANGED
@@ -779,8 +779,44 @@ export class QueryRouterServer<C extends SimpleObject = SimpleObject> extends Qu
779
779
  }
780
780
  return super.run(msg, ctx as RouteContext<C>);
781
781
  }
782
+
783
+ async runAction<T extends { id?: string; path?: string; key?: string; metadata?: { args?: any } } = {}>(
784
+ api: T,
785
+ payload: RunActionPayload<T>,
786
+ ctx?: RouteContext<C>
787
+ ) {
788
+ const { path, key, id } = api as any;
789
+ return this.run({ path, key, id, payload }, ctx);
790
+ }
782
791
  }
783
792
 
784
793
 
785
794
  export class Mini extends QueryRouterServer { }
786
795
 
796
+ /** JSON Schema 基本类型映射到 TypeScript 类型 */
797
+ type JsonSchemaTypeToTS<T> =
798
+ T extends { type: "string" } ? string :
799
+ T extends { type: "boolean" } ? boolean :
800
+ T extends { type: "number" } ? number :
801
+ T extends { type: "integer" } ? number :
802
+ T extends { type: "object" } ? object :
803
+ T extends { type: "array" } ? any[] :
804
+ any;
805
+
806
+ /** 将 args shape(key -> JSON Schema 类型)转换为 payload 类型,支持 optional: true 的字段为可选 */
807
+ type ArgsShapeToPayload<T> =
808
+ { [K in keyof T as T[K] extends { optional: true } ? never : K]: JsonSchemaTypeToTS<T[K]> } &
809
+ { [K in keyof T as T[K] extends { optional: true } ? K : never]?: JsonSchemaTypeToTS<T[K]> };
810
+
811
+ /** 处理两种 args 格式:完整 JSON Schema(含 properties)或简单 key->type 映射 */
812
+ type ArgsToPayload<T> =
813
+ T extends { type: "object"; properties: infer P }
814
+ ? ArgsShapeToPayload<P>
815
+ : ArgsShapeToPayload<T>;
816
+
817
+ /** 从 API 定义中提取 metadata.args */
818
+ type ExtractArgs<T> =
819
+ T extends { metadata: { args: infer A } } ? A : {};
820
+
821
+ /** runAction 第二个参数的类型,根据第一个参数的 metadata.args 推断 */
822
+ export type RunActionPayload<T> = ArgsToPayload<ExtractArgs<T>>;
@@ -0,0 +1,87 @@
1
+ import z from "zod";
2
+ import { App } from "../index.ts";
3
+
4
+ const app = new App();
5
+ const api = {
6
+ "app_domain_manager": {
7
+ /**
8
+ * 获取域名信息,可以通过id或者domain进行查询
9
+ *
10
+ * @param data - Request parameters
11
+ * @param data.data - {object}
12
+ */
13
+ "get": {
14
+ "path": "app_domain_manager",
15
+ "key": "get",
16
+ "description": "获取域名信息,可以通过id或者domain进行查询",
17
+ "metadata": {
18
+ "args": {
19
+ "data": {
20
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
21
+ "type": "object",
22
+ "properties": {
23
+ "id": {
24
+ "type": "string"
25
+ },
26
+ "domain": {
27
+ "type": "string"
28
+ }
29
+ },
30
+ "additionalProperties": false,
31
+ "required": ["id",]
32
+ }
33
+ },
34
+ "viewItem": {
35
+ "api": {
36
+ "url": "/api/router"
37
+ },
38
+ "type": "api",
39
+ "title": "路由"
40
+ },
41
+ "url": "/api/router",
42
+ "source": "query-proxy-api"
43
+ }
44
+ },
45
+ "delete": {
46
+ "path": "app_domain_manager",
47
+ "key": "delete",
48
+ "description": "删除域名",
49
+ "metadata": {
50
+ "args": {
51
+ "domainId": {
52
+ "type": "string",
53
+ "optional": true
54
+ }
55
+ }
56
+ }
57
+ }
58
+ },
59
+ "user_manager": {
60
+ "getUser": {
61
+ "path": "user_manager",
62
+ "key": "getUser",
63
+ "description": "获取用户信息",
64
+ "metadata": {
65
+ "args": {
66
+ "userId": {
67
+ "type": "string"
68
+ },
69
+ "includeProfile": {
70
+ "type": "boolean"
71
+ }
72
+ }
73
+ }
74
+ }
75
+ }
76
+ } as const;
77
+ type API = typeof api;
78
+
79
+ // 类型推断生效:payload 根据 metadata.args 自动推断
80
+ // get 的 args.data 是 type:"object",所以 payload 需要 { data: object }
81
+ app.runAction(api.app_domain_manager.get, { data: { id: "1" } })
82
+
83
+ // delete 的 args 是 { domainId: { type: "string" } },所以 payload 需要 { domainId: string }
84
+ app.runAction(api.app_domain_manager.delete, { domainId: "d1" })
85
+
86
+ // getUser 的 args 是 { userId: string, includeProfile: boolean }
87
+ app.runAction(api.user_manager.getUser, { userId: "u1", includeProfile: true })