@kevisual/router 0.0.84 → 0.0.86
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/app.js +24 -7
- package/dist/opencode.d.ts +63 -1
- package/dist/router-browser.d.ts +46 -1
- package/dist/router-browser.js +8 -4
- package/dist/router-define.d.ts +46 -1
- package/dist/router.d.ts +65 -1
- package/dist/router.js +23 -6
- package/dist/ws.d.ts +63 -1
- package/package.json +7 -7
- package/src/route.ts +38 -12
- package/src/server/server-base.ts +4 -1
- package/src/server/server-bun.ts +6 -2
- package/src/server/server-type.ts +17 -0
- package/src/server/ws-server.ts +8 -1
- package/src/test/run-schema.ts +87 -0
package/dist/app.js
CHANGED
|
@@ -3091,7 +3091,7 @@ function pick(obj, keys) {
|
|
|
3091
3091
|
// node_modules/.pnpm/eventemitter3@5.0.4/node_modules/eventemitter3/index.mjs
|
|
3092
3092
|
var import__ = __toESM(require_eventemitter3(), 1);
|
|
3093
3093
|
|
|
3094
|
-
// node_modules/.pnpm/es-toolkit@1.
|
|
3094
|
+
// node_modules/.pnpm/es-toolkit@1.45.1/node_modules/es-toolkit/dist/predicate/isPlainObject.mjs
|
|
3095
3095
|
function isPlainObject(value) {
|
|
3096
3096
|
if (!value || typeof value !== "object") {
|
|
3097
3097
|
return false;
|
|
@@ -3104,12 +3104,12 @@ function isPlainObject(value) {
|
|
|
3104
3104
|
return Object.prototype.toString.call(value) === "[object Object]";
|
|
3105
3105
|
}
|
|
3106
3106
|
|
|
3107
|
-
// node_modules/.pnpm/es-toolkit@1.
|
|
3107
|
+
// node_modules/.pnpm/es-toolkit@1.45.1/node_modules/es-toolkit/dist/_internal/isUnsafeProperty.mjs
|
|
3108
3108
|
function isUnsafeProperty(key) {
|
|
3109
3109
|
return key === "__proto__";
|
|
3110
3110
|
}
|
|
3111
3111
|
|
|
3112
|
-
// node_modules/.pnpm/es-toolkit@1.
|
|
3112
|
+
// node_modules/.pnpm/es-toolkit@1.45.1/node_modules/es-toolkit/dist/object/merge.mjs
|
|
3113
3113
|
function merge(target, source) {
|
|
3114
3114
|
const sourceKeys = Object.keys(source);
|
|
3115
3115
|
for (let i = 0;i < sourceKeys.length; i++) {
|
|
@@ -16932,7 +16932,7 @@ class Route {
|
|
|
16932
16932
|
if (opts) {
|
|
16933
16933
|
this.id = opts.id || randomId(12, "rand-");
|
|
16934
16934
|
if (!opts.id && opts.idUsePath) {
|
|
16935
|
-
const delimiter = opts.delimiter ?? "
|
|
16935
|
+
const delimiter = opts.delimiter ?? "$$";
|
|
16936
16936
|
this.id = path + delimiter + key;
|
|
16937
16937
|
}
|
|
16938
16938
|
this.run = opts.run;
|
|
@@ -17419,6 +17419,10 @@ class QueryRouterServer extends QueryRouter {
|
|
|
17419
17419
|
}
|
|
17420
17420
|
return super.run(msg, ctx);
|
|
17421
17421
|
}
|
|
17422
|
+
async runAction(api2, payload, ctx) {
|
|
17423
|
+
const { path, key, id } = api2;
|
|
17424
|
+
return this.run({ path, key, id, payload }, ctx);
|
|
17425
|
+
}
|
|
17422
17426
|
}
|
|
17423
17427
|
|
|
17424
17428
|
// src/server/server.ts
|
|
@@ -17914,7 +17918,7 @@ class ServerBase {
|
|
|
17914
17918
|
}
|
|
17915
17919
|
}
|
|
17916
17920
|
async onWsClose(ws) {
|
|
17917
|
-
const id = ws?.
|
|
17921
|
+
const id = ws?.wsId || "";
|
|
17918
17922
|
if (id) {
|
|
17919
17923
|
this.emitter.emit("close--" + id, { type: "close", ws, id });
|
|
17920
17924
|
setTimeout(() => {
|
|
@@ -17927,6 +17931,9 @@ class ServerBase {
|
|
|
17927
17931
|
if (this.showConnected)
|
|
17928
17932
|
ws.send(JSON.stringify({ type: "connected" }));
|
|
17929
17933
|
}
|
|
17934
|
+
createId() {
|
|
17935
|
+
return Math.random().toString(36).substring(2, 15);
|
|
17936
|
+
}
|
|
17930
17937
|
}
|
|
17931
17938
|
|
|
17932
17939
|
// node_modules/.pnpm/@kevisual+ws@8.0.0/node_modules/@kevisual/ws/wrapper.mjs
|
|
@@ -17967,6 +17974,9 @@ class WsServerBase {
|
|
|
17967
17974
|
token,
|
|
17968
17975
|
id
|
|
17969
17976
|
};
|
|
17977
|
+
if (!ws.wsId) {
|
|
17978
|
+
ws.wsId = this.createId();
|
|
17979
|
+
}
|
|
17970
17980
|
ws.on("message", async (message) => {
|
|
17971
17981
|
await this.server.onWebSocket({ ws, message, pathname, token, id });
|
|
17972
17982
|
});
|
|
@@ -17977,6 +17987,9 @@ class WsServerBase {
|
|
|
17977
17987
|
});
|
|
17978
17988
|
});
|
|
17979
17989
|
}
|
|
17990
|
+
createId() {
|
|
17991
|
+
return Math.random().toString(36).substring(2, 15);
|
|
17992
|
+
}
|
|
17980
17993
|
}
|
|
17981
17994
|
|
|
17982
17995
|
class WsServer extends WsServerBase {
|
|
@@ -18292,10 +18305,14 @@ class BunServer extends ServerBase {
|
|
|
18292
18305
|
open: (ws) => {
|
|
18293
18306
|
this.sendConnected(ws);
|
|
18294
18307
|
},
|
|
18295
|
-
message: async (
|
|
18308
|
+
message: async (bunWs, message) => {
|
|
18309
|
+
const ws = bunWs;
|
|
18296
18310
|
const pathname = ws.data.pathname || "";
|
|
18297
18311
|
const token = ws.data.token || "";
|
|
18298
18312
|
const id = ws.data.id || "";
|
|
18313
|
+
if (!ws.wsId) {
|
|
18314
|
+
ws.wsId = this.createId();
|
|
18315
|
+
}
|
|
18299
18316
|
await this.onWebSocket({ ws, message, pathname, token, id });
|
|
18300
18317
|
},
|
|
18301
18318
|
close: (ws) => {
|
|
@@ -19562,7 +19579,7 @@ app
|
|
|
19562
19579
|
10. **中间件找不到会返回 404**,错误信息中会包含找不到的中间件列表。
|
|
19563
19580
|
`;
|
|
19564
19581
|
// package.json
|
|
19565
|
-
var version2 = "0.0.
|
|
19582
|
+
var version2 = "0.0.86";
|
|
19566
19583
|
|
|
19567
19584
|
// agent/routes/route-create.ts
|
|
19568
19585
|
app.route({
|
package/dist/opencode.d.ts
CHANGED
|
@@ -146,7 +146,7 @@ type RouteOpts<U = {}, T = SimpleObject> = {
|
|
|
146
146
|
description?: string;
|
|
147
147
|
metadata?: T;
|
|
148
148
|
middleware?: RouteMiddleware[];
|
|
149
|
-
type?: 'route' | 'middleware';
|
|
149
|
+
type?: 'route' | 'middleware' | 'compound';
|
|
150
150
|
/**
|
|
151
151
|
* $#$ will be used to split path and key
|
|
152
152
|
*/
|
|
@@ -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
|
/**
|
|
@@ -503,15 +548,32 @@ type OnWebSocketOptions<T = {}> = {
|
|
|
503
548
|
message: string | Buffer;
|
|
504
549
|
pathname: string;
|
|
505
550
|
token?: string;
|
|
551
|
+
/** data 的id提取出来 */
|
|
506
552
|
id?: string;
|
|
507
553
|
};
|
|
508
554
|
type WS<T = {}> = {
|
|
509
555
|
send: (data: any) => void;
|
|
510
556
|
close: (code?: number, reason?: string) => void;
|
|
557
|
+
/**
|
|
558
|
+
* ws 自己生成的一个id,主要是为了区分不同的ws连接,方便在onWebSocket中使用
|
|
559
|
+
*/
|
|
560
|
+
wsId?: string;
|
|
511
561
|
data?: {
|
|
562
|
+
/**
|
|
563
|
+
* ws连接时的url,包含pathname和searchParams
|
|
564
|
+
*/
|
|
512
565
|
url: URL;
|
|
566
|
+
/**
|
|
567
|
+
* ws连接时的pathname
|
|
568
|
+
*/
|
|
513
569
|
pathname: string;
|
|
570
|
+
/**
|
|
571
|
+
* ws连接时的url中的token参数
|
|
572
|
+
*/
|
|
514
573
|
token?: string;
|
|
574
|
+
/**
|
|
575
|
+
* ws连接时的url中的id参数.
|
|
576
|
+
*/
|
|
515
577
|
id?: string;
|
|
516
578
|
/**
|
|
517
579
|
* 鉴权后的获取的信息
|
package/dist/router-browser.d.ts
CHANGED
|
@@ -187,7 +187,7 @@ type RouteOpts<U = {}, T = SimpleObject$1> = {
|
|
|
187
187
|
description?: string;
|
|
188
188
|
metadata?: T;
|
|
189
189
|
middleware?: RouteMiddleware[];
|
|
190
|
-
type?: 'route' | 'middleware';
|
|
190
|
+
type?: 'route' | 'middleware' | 'compound';
|
|
191
191
|
/**
|
|
192
192
|
* $#$ will be used to split path and key
|
|
193
193
|
*/
|
|
@@ -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;
|
package/dist/router-browser.js
CHANGED
|
@@ -280,7 +280,7 @@ function pick(obj, keys) {
|
|
|
280
280
|
// node_modules/.pnpm/eventemitter3@5.0.4/node_modules/eventemitter3/index.mjs
|
|
281
281
|
var import__ = __toESM(require_eventemitter3(), 1);
|
|
282
282
|
|
|
283
|
-
// node_modules/.pnpm/es-toolkit@1.
|
|
283
|
+
// node_modules/.pnpm/es-toolkit@1.45.1/node_modules/es-toolkit/dist/predicate/isPlainObject.mjs
|
|
284
284
|
function isPlainObject(value) {
|
|
285
285
|
if (!value || typeof value !== "object") {
|
|
286
286
|
return false;
|
|
@@ -293,12 +293,12 @@ function isPlainObject(value) {
|
|
|
293
293
|
return Object.prototype.toString.call(value) === "[object Object]";
|
|
294
294
|
}
|
|
295
295
|
|
|
296
|
-
// node_modules/.pnpm/es-toolkit@1.
|
|
296
|
+
// node_modules/.pnpm/es-toolkit@1.45.1/node_modules/es-toolkit/dist/_internal/isUnsafeProperty.mjs
|
|
297
297
|
function isUnsafeProperty(key) {
|
|
298
298
|
return key === "__proto__";
|
|
299
299
|
}
|
|
300
300
|
|
|
301
|
-
// node_modules/.pnpm/es-toolkit@1.
|
|
301
|
+
// node_modules/.pnpm/es-toolkit@1.45.1/node_modules/es-toolkit/dist/object/merge.mjs
|
|
302
302
|
function merge(target, source) {
|
|
303
303
|
const sourceKeys = Object.keys(source);
|
|
304
304
|
for (let i = 0;i < sourceKeys.length; i++) {
|
|
@@ -14096,7 +14096,7 @@ class Route {
|
|
|
14096
14096
|
if (opts) {
|
|
14097
14097
|
this.id = opts.id || randomId(12, "rand-");
|
|
14098
14098
|
if (!opts.id && opts.idUsePath) {
|
|
14099
|
-
const delimiter = opts.delimiter ?? "
|
|
14099
|
+
const delimiter = opts.delimiter ?? "$$";
|
|
14100
14100
|
this.id = path + delimiter + key;
|
|
14101
14101
|
}
|
|
14102
14102
|
this.run = opts.run;
|
|
@@ -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 {
|
package/dist/router-define.d.ts
CHANGED
|
@@ -143,7 +143,7 @@ type RouteOpts<U = {}, T = SimpleObject$1> = {
|
|
|
143
143
|
description?: string;
|
|
144
144
|
metadata?: T;
|
|
145
145
|
middleware?: RouteMiddleware[];
|
|
146
|
-
type?: 'route' | 'middleware';
|
|
146
|
+
type?: 'route' | 'middleware' | 'compound';
|
|
147
147
|
/**
|
|
148
148
|
* $#$ will be used to split path and key
|
|
149
149
|
*/
|
|
@@ -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
|
@@ -193,7 +193,7 @@ type RouteOpts<U = {}, T = SimpleObject$1> = {
|
|
|
193
193
|
description?: string;
|
|
194
194
|
metadata?: T;
|
|
195
195
|
middleware?: RouteMiddleware[];
|
|
196
|
-
type?: 'route' | 'middleware';
|
|
196
|
+
type?: 'route' | 'middleware' | 'compound';
|
|
197
197
|
/**
|
|
198
198
|
* $#$ will be used to split path and key
|
|
199
199
|
*/
|
|
@@ -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;
|
|
@@ -688,16 +733,33 @@ type OnWebSocketOptions<T = {}> = {
|
|
|
688
733
|
message: string | Buffer;
|
|
689
734
|
pathname: string;
|
|
690
735
|
token?: string;
|
|
736
|
+
/** data 的id提取出来 */
|
|
691
737
|
id?: string;
|
|
692
738
|
};
|
|
693
739
|
type OnWebSocketFn = (options: OnWebSocketOptions) => Promise<void> | void;
|
|
694
740
|
type WS<T = {}> = {
|
|
695
741
|
send: (data: any) => void;
|
|
696
742
|
close: (code?: number, reason?: string) => void;
|
|
743
|
+
/**
|
|
744
|
+
* ws 自己生成的一个id,主要是为了区分不同的ws连接,方便在onWebSocket中使用
|
|
745
|
+
*/
|
|
746
|
+
wsId?: string;
|
|
697
747
|
data?: {
|
|
748
|
+
/**
|
|
749
|
+
* ws连接时的url,包含pathname和searchParams
|
|
750
|
+
*/
|
|
698
751
|
url: URL;
|
|
752
|
+
/**
|
|
753
|
+
* ws连接时的pathname
|
|
754
|
+
*/
|
|
699
755
|
pathname: string;
|
|
756
|
+
/**
|
|
757
|
+
* ws连接时的url中的token参数
|
|
758
|
+
*/
|
|
700
759
|
token?: string;
|
|
760
|
+
/**
|
|
761
|
+
* ws连接时的url中的id参数.
|
|
762
|
+
*/
|
|
701
763
|
id?: string;
|
|
702
764
|
/**
|
|
703
765
|
* 鉴权后的获取的信息
|
|
@@ -916,6 +978,7 @@ declare class ServerBase implements ServerType {
|
|
|
916
978
|
*/
|
|
917
979
|
onWsClose(ws: WS): Promise<void>;
|
|
918
980
|
sendConnected(ws: WS): Promise<void>;
|
|
981
|
+
createId(): string;
|
|
919
982
|
}
|
|
920
983
|
|
|
921
984
|
type WsServerBaseOpts = {
|
|
@@ -928,6 +991,7 @@ declare class WsServerBase {
|
|
|
928
991
|
server: ServerType;
|
|
929
992
|
constructor(opts: WsServerBaseOpts);
|
|
930
993
|
listen(): void;
|
|
994
|
+
createId(): string;
|
|
931
995
|
}
|
|
932
996
|
declare class WsServer extends WsServerBase {
|
|
933
997
|
constructor(server: ServerType);
|
package/dist/router.js
CHANGED
|
@@ -3091,7 +3091,7 @@ function pick(obj, keys) {
|
|
|
3091
3091
|
// node_modules/.pnpm/eventemitter3@5.0.4/node_modules/eventemitter3/index.mjs
|
|
3092
3092
|
var import__ = __toESM(require_eventemitter3(), 1);
|
|
3093
3093
|
|
|
3094
|
-
// node_modules/.pnpm/es-toolkit@1.
|
|
3094
|
+
// node_modules/.pnpm/es-toolkit@1.45.1/node_modules/es-toolkit/dist/predicate/isPlainObject.mjs
|
|
3095
3095
|
function isPlainObject(value) {
|
|
3096
3096
|
if (!value || typeof value !== "object") {
|
|
3097
3097
|
return false;
|
|
@@ -3104,12 +3104,12 @@ function isPlainObject(value) {
|
|
|
3104
3104
|
return Object.prototype.toString.call(value) === "[object Object]";
|
|
3105
3105
|
}
|
|
3106
3106
|
|
|
3107
|
-
// node_modules/.pnpm/es-toolkit@1.
|
|
3107
|
+
// node_modules/.pnpm/es-toolkit@1.45.1/node_modules/es-toolkit/dist/_internal/isUnsafeProperty.mjs
|
|
3108
3108
|
function isUnsafeProperty(key) {
|
|
3109
3109
|
return key === "__proto__";
|
|
3110
3110
|
}
|
|
3111
3111
|
|
|
3112
|
-
// node_modules/.pnpm/es-toolkit@1.
|
|
3112
|
+
// node_modules/.pnpm/es-toolkit@1.45.1/node_modules/es-toolkit/dist/object/merge.mjs
|
|
3113
3113
|
function merge(target, source) {
|
|
3114
3114
|
const sourceKeys = Object.keys(source);
|
|
3115
3115
|
for (let i = 0;i < sourceKeys.length; i++) {
|
|
@@ -16929,7 +16929,7 @@ class Route {
|
|
|
16929
16929
|
if (opts) {
|
|
16930
16930
|
this.id = opts.id || randomId(12, "rand-");
|
|
16931
16931
|
if (!opts.id && opts.idUsePath) {
|
|
16932
|
-
const delimiter = opts.delimiter ?? "
|
|
16932
|
+
const delimiter = opts.delimiter ?? "$$";
|
|
16933
16933
|
this.id = path + delimiter + key;
|
|
16934
16934
|
}
|
|
16935
16935
|
this.run = opts.run;
|
|
@@ -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 {
|
|
@@ -18088,7 +18092,7 @@ class ServerBase {
|
|
|
18088
18092
|
}
|
|
18089
18093
|
}
|
|
18090
18094
|
async onWsClose(ws) {
|
|
18091
|
-
const id = ws?.
|
|
18095
|
+
const id = ws?.wsId || "";
|
|
18092
18096
|
if (id) {
|
|
18093
18097
|
this.emitter.emit("close--" + id, { type: "close", ws, id });
|
|
18094
18098
|
setTimeout(() => {
|
|
@@ -18101,6 +18105,9 @@ class ServerBase {
|
|
|
18101
18105
|
if (this.showConnected)
|
|
18102
18106
|
ws.send(JSON.stringify({ type: "connected" }));
|
|
18103
18107
|
}
|
|
18108
|
+
createId() {
|
|
18109
|
+
return Math.random().toString(36).substring(2, 15);
|
|
18110
|
+
}
|
|
18104
18111
|
}
|
|
18105
18112
|
|
|
18106
18113
|
// node_modules/.pnpm/@kevisual+ws@8.0.0/node_modules/@kevisual/ws/wrapper.mjs
|
|
@@ -18141,6 +18148,9 @@ class WsServerBase {
|
|
|
18141
18148
|
token,
|
|
18142
18149
|
id
|
|
18143
18150
|
};
|
|
18151
|
+
if (!ws.wsId) {
|
|
18152
|
+
ws.wsId = this.createId();
|
|
18153
|
+
}
|
|
18144
18154
|
ws.on("message", async (message) => {
|
|
18145
18155
|
await this.server.onWebSocket({ ws, message, pathname, token, id });
|
|
18146
18156
|
});
|
|
@@ -18151,6 +18161,9 @@ class WsServerBase {
|
|
|
18151
18161
|
});
|
|
18152
18162
|
});
|
|
18153
18163
|
}
|
|
18164
|
+
createId() {
|
|
18165
|
+
return Math.random().toString(36).substring(2, 15);
|
|
18166
|
+
}
|
|
18154
18167
|
}
|
|
18155
18168
|
|
|
18156
18169
|
class WsServer extends WsServerBase {
|
|
@@ -18465,10 +18478,14 @@ class BunServer extends ServerBase {
|
|
|
18465
18478
|
open: (ws) => {
|
|
18466
18479
|
this.sendConnected(ws);
|
|
18467
18480
|
},
|
|
18468
|
-
message: async (
|
|
18481
|
+
message: async (bunWs, message) => {
|
|
18482
|
+
const ws = bunWs;
|
|
18469
18483
|
const pathname = ws.data.pathname || "";
|
|
18470
18484
|
const token = ws.data.token || "";
|
|
18471
18485
|
const id = ws.data.id || "";
|
|
18486
|
+
if (!ws.wsId) {
|
|
18487
|
+
ws.wsId = this.createId();
|
|
18488
|
+
}
|
|
18472
18489
|
await this.onWebSocket({ ws, message, pathname, token, id });
|
|
18473
18490
|
},
|
|
18474
18491
|
close: (ws) => {
|
package/dist/ws.d.ts
CHANGED
|
@@ -193,7 +193,7 @@ type RouteOpts<U = {}, T = SimpleObject> = {
|
|
|
193
193
|
description?: string;
|
|
194
194
|
metadata?: T;
|
|
195
195
|
middleware?: RouteMiddleware[];
|
|
196
|
-
type?: 'route' | 'middleware';
|
|
196
|
+
type?: 'route' | 'middleware' | 'compound';
|
|
197
197
|
/**
|
|
198
198
|
* $#$ will be used to split path and key
|
|
199
199
|
*/
|
|
@@ -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
|
/**
|
|
@@ -550,15 +595,32 @@ type OnWebSocketOptions<T = {}> = {
|
|
|
550
595
|
message: string | Buffer;
|
|
551
596
|
pathname: string;
|
|
552
597
|
token?: string;
|
|
598
|
+
/** data 的id提取出来 */
|
|
553
599
|
id?: string;
|
|
554
600
|
};
|
|
555
601
|
type WS<T = {}> = {
|
|
556
602
|
send: (data: any) => void;
|
|
557
603
|
close: (code?: number, reason?: string) => void;
|
|
604
|
+
/**
|
|
605
|
+
* ws 自己生成的一个id,主要是为了区分不同的ws连接,方便在onWebSocket中使用
|
|
606
|
+
*/
|
|
607
|
+
wsId?: string;
|
|
558
608
|
data?: {
|
|
609
|
+
/**
|
|
610
|
+
* ws连接时的url,包含pathname和searchParams
|
|
611
|
+
*/
|
|
559
612
|
url: URL;
|
|
613
|
+
/**
|
|
614
|
+
* ws连接时的pathname
|
|
615
|
+
*/
|
|
560
616
|
pathname: string;
|
|
617
|
+
/**
|
|
618
|
+
* ws连接时的url中的token参数
|
|
619
|
+
*/
|
|
561
620
|
token?: string;
|
|
621
|
+
/**
|
|
622
|
+
* ws连接时的url中的id参数.
|
|
623
|
+
*/
|
|
562
624
|
id?: string;
|
|
563
625
|
/**
|
|
564
626
|
* 鉴权后的获取的信息
|
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.
|
|
4
|
+
"version": "0.0.86",
|
|
5
5
|
"description": "",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "./dist/router.js",
|
|
@@ -27,17 +27,17 @@
|
|
|
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.
|
|
30
|
+
"@kevisual/query": "^0.0.53",
|
|
31
31
|
"@kevisual/use-config": "^1.0.30",
|
|
32
|
-
"@opencode-ai/plugin": "^1.2.
|
|
33
|
-
"@types/bun": "^1.3.
|
|
34
|
-
"@types/node": "^25.3.
|
|
32
|
+
"@opencode-ai/plugin": "^1.2.20",
|
|
33
|
+
"@types/bun": "^1.3.10",
|
|
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
38
|
"eventemitter3": "^5.0.4",
|
|
39
39
|
"fast-glob": "^3.3.3",
|
|
40
|
-
"hono": "^4.12.
|
|
40
|
+
"hono": "^4.12.5",
|
|
41
41
|
"nanoid": "^5.1.6",
|
|
42
42
|
"path-to-regexp": "^8.3.0",
|
|
43
43
|
"send": "^1.2.1",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"url": "git+https://github.com/abearxiong/kevisual-router.git"
|
|
52
52
|
},
|
|
53
53
|
"dependencies": {
|
|
54
|
-
"es-toolkit": "^1.
|
|
54
|
+
"es-toolkit": "^1.45.1"
|
|
55
55
|
},
|
|
56
56
|
"publishConfig": {
|
|
57
57
|
"access": "public"
|
package/src/route.ts
CHANGED
|
@@ -7,14 +7,6 @@ import * as schema from './validator/schema.ts';
|
|
|
7
7
|
|
|
8
8
|
export type RouterContextT = { code?: number;[key: string]: any };
|
|
9
9
|
|
|
10
|
-
type ExtractArgs<A> = A extends z.ZodTypeAny ? z.infer<A> : A;
|
|
11
|
-
|
|
12
|
-
type OptionalKeys<T> = {
|
|
13
|
-
[K in keyof T]-?: {} extends Pick<T, K> ? K : never;
|
|
14
|
-
}[keyof T];
|
|
15
|
-
|
|
16
|
-
type MakeOptional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
|
|
17
|
-
|
|
18
10
|
type BuildRouteContext<M, U> = M extends { args?: infer A }
|
|
19
11
|
? A extends z.ZodObject<any>
|
|
20
12
|
? RouteContext<{ args?: z.infer<A> }, U>
|
|
@@ -103,7 +95,7 @@ export type RouteOpts<U = {}, T = SimpleObject> = {
|
|
|
103
95
|
description?: string;
|
|
104
96
|
metadata?: T;
|
|
105
97
|
middleware?: RouteMiddleware[]; // middleware
|
|
106
|
-
type?: 'route' | 'middleware';
|
|
98
|
+
type?: 'route' | 'middleware' | 'compound'; // compound表示这个 route 作为一个聚合体,没有实际的 run,而是一个 router 的聚合列表
|
|
107
99
|
/**
|
|
108
100
|
* $#$ will be used to split path and key
|
|
109
101
|
*/
|
|
@@ -146,8 +138,6 @@ export const createSkill = <T = SimpleObject>(skill: Skill<T>): Skill<T> => {
|
|
|
146
138
|
|
|
147
139
|
export type RouteInfo = Pick<Route, (typeof pickValue)[number]>;
|
|
148
140
|
|
|
149
|
-
type ExtractMetadata<M> = M extends { metadata?: infer Meta } ? Meta extends SimpleObject ? Meta : SimpleObject : SimpleObject;
|
|
150
|
-
|
|
151
141
|
/**
|
|
152
142
|
* @M 是 route的 metadate的类型,默认是 SimpleObject
|
|
153
143
|
* @U 是 RouteContext 里 state的类型
|
|
@@ -183,7 +173,7 @@ export class Route<M extends SimpleObject = SimpleObject, U extends SimpleObject
|
|
|
183
173
|
if (opts) {
|
|
184
174
|
this.id = opts.id || randomId(12, 'rand-');
|
|
185
175
|
if (!opts.id && opts.idUsePath) {
|
|
186
|
-
const delimiter = opts.delimiter ?? '
|
|
176
|
+
const delimiter = opts.delimiter ?? '$$';
|
|
187
177
|
this.id = path + delimiter + key;
|
|
188
178
|
}
|
|
189
179
|
this.run = opts.run as Run<BuildRouteContext<M, U>>;
|
|
@@ -789,8 +779,44 @@ export class QueryRouterServer<C extends SimpleObject = SimpleObject> extends Qu
|
|
|
789
779
|
}
|
|
790
780
|
return super.run(msg, ctx as RouteContext<C>);
|
|
791
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
|
+
}
|
|
792
791
|
}
|
|
793
792
|
|
|
794
793
|
|
|
795
794
|
export class Mini extends QueryRouterServer { }
|
|
796
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>>;
|
|
@@ -284,7 +284,7 @@ export class ServerBase implements ServerType {
|
|
|
284
284
|
* @param ws
|
|
285
285
|
*/
|
|
286
286
|
async onWsClose(ws: WS) {
|
|
287
|
-
const id = ws?.
|
|
287
|
+
const id = ws?.wsId || '';
|
|
288
288
|
if (id) {
|
|
289
289
|
this.emitter.emit('close--' + id, { type: 'close', ws, id });
|
|
290
290
|
setTimeout(() => {
|
|
@@ -298,4 +298,7 @@ export class ServerBase implements ServerType {
|
|
|
298
298
|
if (this.showConnected)
|
|
299
299
|
ws.send(JSON.stringify({ type: 'connected' }));
|
|
300
300
|
}
|
|
301
|
+
createId() {
|
|
302
|
+
return Math.random().toString(36).substring(2, 15);
|
|
303
|
+
}
|
|
301
304
|
}
|
package/src/server/server-bun.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* @tags bun, server, websocket, http
|
|
5
5
|
* @createdAt 2025-12-20
|
|
6
6
|
*/
|
|
7
|
-
import { ServerType, type ServerOpts, type Cors, RouterRes, RouterReq } from './server-type.ts';
|
|
7
|
+
import { ServerType, type ServerOpts, type Cors, RouterRes, RouterReq, WS } from './server-type.ts';
|
|
8
8
|
import { ServerBase } from './server-base.ts';
|
|
9
9
|
|
|
10
10
|
export class BunServer extends ServerBase implements ServerType {
|
|
@@ -264,10 +264,14 @@ export class BunServer extends ServerBase implements ServerType {
|
|
|
264
264
|
open: (ws: any) => {
|
|
265
265
|
this.sendConnected(ws);
|
|
266
266
|
},
|
|
267
|
-
message: async (
|
|
267
|
+
message: async (bunWs: any, message: string | Buffer) => {
|
|
268
|
+
const ws = bunWs as WS;
|
|
268
269
|
const pathname = ws.data.pathname || '';
|
|
269
270
|
const token = ws.data.token || '';
|
|
270
271
|
const id = ws.data.id || '';
|
|
272
|
+
if (!ws.wsId) {
|
|
273
|
+
ws.wsId = this.createId();
|
|
274
|
+
}
|
|
271
275
|
await this.onWebSocket({ ws, message, pathname, token, id });
|
|
272
276
|
},
|
|
273
277
|
close: (ws: any) => {
|
|
@@ -49,16 +49,33 @@ export type OnWebSocketOptions<T = {}> = {
|
|
|
49
49
|
message: string | Buffer;
|
|
50
50
|
pathname: string,
|
|
51
51
|
token?: string,
|
|
52
|
+
/** data 的id提取出来 */
|
|
52
53
|
id?: string,
|
|
53
54
|
}
|
|
54
55
|
export type OnWebSocketFn = (options: OnWebSocketOptions) => Promise<void> | void;
|
|
55
56
|
export type WS<T = {}> = {
|
|
56
57
|
send: (data: any) => void;
|
|
57
58
|
close: (code?: number, reason?: string) => void;
|
|
59
|
+
/**
|
|
60
|
+
* ws 自己生成的一个id,主要是为了区分不同的ws连接,方便在onWebSocket中使用
|
|
61
|
+
*/
|
|
62
|
+
wsId?: string;
|
|
58
63
|
data?: {
|
|
64
|
+
/**
|
|
65
|
+
* ws连接时的url,包含pathname和searchParams
|
|
66
|
+
*/
|
|
59
67
|
url: URL;
|
|
68
|
+
/**
|
|
69
|
+
* ws连接时的pathname
|
|
70
|
+
*/
|
|
60
71
|
pathname: string;
|
|
72
|
+
/**
|
|
73
|
+
* ws连接时的url中的token参数
|
|
74
|
+
*/
|
|
61
75
|
token?: string;
|
|
76
|
+
/**
|
|
77
|
+
* ws连接时的url中的id参数.
|
|
78
|
+
*/
|
|
62
79
|
id?: string;
|
|
63
80
|
/**
|
|
64
81
|
* 鉴权后的获取的信息
|
package/src/server/ws-server.ts
CHANGED
|
@@ -56,6 +56,11 @@ export class WsServerBase {
|
|
|
56
56
|
token,
|
|
57
57
|
id,
|
|
58
58
|
}
|
|
59
|
+
// @ts-ignore
|
|
60
|
+
if (!ws.wsId) {
|
|
61
|
+
// @ts-ignore
|
|
62
|
+
ws.wsId = this.createId();
|
|
63
|
+
}
|
|
59
64
|
ws.on('message', async (message: string | Buffer) => {
|
|
60
65
|
await this.server.onWebSocket({ ws, message, pathname, token, id });
|
|
61
66
|
});
|
|
@@ -66,7 +71,9 @@ export class WsServerBase {
|
|
|
66
71
|
this.server.onWsClose(ws);
|
|
67
72
|
});
|
|
68
73
|
});
|
|
69
|
-
|
|
74
|
+
}
|
|
75
|
+
createId() {
|
|
76
|
+
return Math.random().toString(36).substring(2, 15);
|
|
70
77
|
}
|
|
71
78
|
}
|
|
72
79
|
// TODO: ws handle and path and routerContext
|
|
@@ -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 })
|