@dangao/bun-server 1.5.0 → 1.6.0

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.
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/core/server.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,KAAK,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AACtE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAErE;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;OAEG;IACH,KAAK,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IAE1D;;OAEG;IACH,iBAAiB,CAAC,EAAE,wBAAwB,CAAC;IAE7C;;;OAGG;IACH,uBAAuB,CAAC,EAAE,MAAM,CAAC;CAClC;AAED;;;GAGG;AACH,qBAAa,SAAS;IACpB,OAAO,CAAC,MAAM,CAAC,CAAkC;IACjD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgB;IACxC,OAAO,CAAC,cAAc,CAAa;IACnC,OAAO,CAAC,cAAc,CAAkB;IACxC,OAAO,CAAC,eAAe,CAAC,CAAgB;IACxC,OAAO,CAAC,eAAe,CAAC,CAAa;gBAElB,OAAO,EAAE,aAAa;IAIzC;;OAEG;IACI,KAAK,IAAI,IAAI;IA2FpB;;OAEG;IACI,IAAI,IAAI,IAAI;IAWnB;;;;;OAKG;IACU,gBAAgB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA2C9D;;OAEG;IACI,iBAAiB,IAAI,MAAM;IAIlC;;OAEG;IACI,mBAAmB,IAAI,OAAO;IAIrC;;;OAGG;IACI,SAAS,IAAI,MAAM,CAAC,uBAAuB,CAAC,GAAG,SAAS;IAI/D;;;OAGG;IACI,SAAS,IAAI,OAAO;IAI3B;;OAEG;IACI,OAAO,IAAI,MAAM;IAIxB;;OAEG;IACI,WAAW,IAAI,MAAM,GAAG,SAAS;CAGzC"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/core/server.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,KAAK,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AACtE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAErE;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;OAEG;IACH,KAAK,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IAE1D;;OAEG;IACH,iBAAiB,CAAC,EAAE,wBAAwB,CAAC;IAE7C;;;OAGG;IACH,uBAAuB,CAAC,EAAE,MAAM,CAAC;CAClC;AAED;;;GAGG;AACH,qBAAa,SAAS;IACpB,OAAO,CAAC,MAAM,CAAC,CAAkC;IACjD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgB;IACxC,OAAO,CAAC,cAAc,CAAa;IACnC,OAAO,CAAC,cAAc,CAAkB;IACxC,OAAO,CAAC,eAAe,CAAC,CAAgB;IACxC,OAAO,CAAC,eAAe,CAAC,CAAa;gBAElB,OAAO,EAAE,aAAa;IAIzC;;OAEG;IACI,KAAK,IAAI,IAAI;IAmGpB;;OAEG;IACI,IAAI,IAAI,IAAI;IAWnB;;;;;OAKG;IACU,gBAAgB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA2C9D;;OAEG;IACI,iBAAiB,IAAI,MAAM;IAIlC;;OAEG;IACI,mBAAmB,IAAI,OAAO;IAIrC;;;OAGG;IACI,SAAS,IAAI,MAAM,CAAC,uBAAuB,CAAC,GAAG,SAAS;IAI/D;;;OAGG;IACI,SAAS,IAAI,OAAO;IAI3B;;OAEG;IACI,OAAO,IAAI,MAAM;IAIxB;;OAEG;IACI,WAAW,IAAI,MAAM,GAAG,SAAS;CAGzC"}
package/dist/index.js CHANGED
@@ -3264,8 +3264,14 @@ class BunServer {
3264
3264
  if (!this.options.websocketRegistry.hasGateway(url.pathname)) {
3265
3265
  return new Response("WebSocket gateway not found", { status: 404 });
3266
3266
  }
3267
+ const context2 = new Context(request);
3268
+ const queryParams = new URLSearchParams(url.searchParams);
3267
3269
  const upgraded = server.upgrade(request, {
3268
- data: { path: url.pathname }
3270
+ data: {
3271
+ path: url.pathname,
3272
+ query: queryParams,
3273
+ context: context2
3274
+ }
3269
3275
  });
3270
3276
  if (upgraded) {
3271
3277
  return;
@@ -3291,14 +3297,14 @@ class BunServer {
3291
3297
  return responsePromise;
3292
3298
  },
3293
3299
  websocket: {
3294
- open: (ws) => {
3295
- this.options.websocketRegistry?.handleOpen(ws);
3300
+ open: async (ws) => {
3301
+ await this.options.websocketRegistry?.handleOpen(ws);
3296
3302
  },
3297
- message: (ws, message) => {
3298
- this.options.websocketRegistry?.handleMessage(ws, message);
3303
+ message: async (ws, message) => {
3304
+ await this.options.websocketRegistry?.handleMessage(ws, message);
3299
3305
  },
3300
- close: (ws, code, reason) => {
3301
- this.options.websocketRegistry?.handleClose(ws, code, reason);
3306
+ close: async (ws, code, reason) => {
3307
+ await this.options.websocketRegistry?.handleClose(ws, code, reason);
3302
3308
  }
3303
3309
  }
3304
3310
  });
@@ -3403,6 +3409,8 @@ function getHandlerMetadata(target) {
3403
3409
  }
3404
3410
 
3405
3411
  // src/websocket/registry.ts
3412
+ init_param_binder();
3413
+ init_decorators2();
3406
3414
  class WebSocketGatewayRegistry {
3407
3415
  static instance;
3408
3416
  container;
@@ -3443,47 +3451,90 @@ class WebSocketGatewayRegistry {
3443
3451
  getGateway(path) {
3444
3452
  return this.gateways.get(path);
3445
3453
  }
3446
- getGatewayInstance(definition) {
3447
- if (!definition.instance) {
3448
- definition.instance = this.container.resolve(definition.gatewayClass);
3449
- }
3450
- return definition.instance;
3454
+ createGatewayInstance(definition) {
3455
+ return this.container.resolve(definition.gatewayClass);
3451
3456
  }
3452
- invokeHandler(ws, definition, handlerName, ...args) {
3457
+ async invokeHandler(ws, definition, handlerName, ...args) {
3453
3458
  if (!handlerName) {
3454
3459
  return;
3455
3460
  }
3456
- const instance = this.getGatewayInstance(definition);
3461
+ const instance = this.createGatewayInstance(definition);
3457
3462
  const handler = instance[handlerName];
3458
- if (typeof handler === "function") {
3463
+ if (typeof handler !== "function") {
3464
+ return;
3465
+ }
3466
+ const prototype = definition.gatewayClass.prototype;
3467
+ const paramMetadata = getParamMetadata(prototype, handlerName);
3468
+ if (paramMetadata.length > 0) {
3469
+ let context = ws.data?.context;
3470
+ if (!context) {
3471
+ const url = new URL(ws.data.path || "/", "http://localhost");
3472
+ if (ws.data.query) {
3473
+ ws.data.query.forEach((value, key) => {
3474
+ url.searchParams.set(key, value);
3475
+ });
3476
+ }
3477
+ const request = new Request(url.toString(), {
3478
+ method: "GET",
3479
+ headers: new Headers
3480
+ });
3481
+ context = new Context(request);
3482
+ ws.data.context = context;
3483
+ }
3484
+ const boundParams = await ParamBinder.bind(prototype, handlerName, context, this.container);
3485
+ const finalArgs = [];
3486
+ const maxParamIndex = paramMetadata.length > 0 ? Math.max(...paramMetadata.map((m) => m.index)) : -1;
3487
+ const totalParamCount = Math.max(maxParamIndex + 1, args.length + 1);
3488
+ for (let i = 0;i < totalParamCount; i++) {
3489
+ const meta = paramMetadata.find((m) => m.index === i);
3490
+ if (meta) {
3491
+ finalArgs[i] = boundParams[i];
3492
+ } else if (i === 0) {
3493
+ finalArgs[i] = ws;
3494
+ } else {
3495
+ const argIndex = i - 1;
3496
+ if (argIndex < args.length) {
3497
+ finalArgs[i] = args[argIndex];
3498
+ } else {
3499
+ finalArgs[i] = undefined;
3500
+ }
3501
+ }
3502
+ }
3503
+ if (args.length > maxParamIndex) {
3504
+ for (let i = maxParamIndex + 1;i < args.length; i++) {
3505
+ finalArgs.push(args[i]);
3506
+ }
3507
+ }
3508
+ handler.apply(instance, finalArgs);
3509
+ } else {
3459
3510
  handler.apply(instance, [ws, ...args]);
3460
3511
  }
3461
3512
  }
3462
- handleOpen(ws) {
3513
+ async handleOpen(ws) {
3463
3514
  const path = ws.data?.path;
3464
3515
  const definition = path ? this.getGateway(path) : undefined;
3465
3516
  if (!definition) {
3466
3517
  ws.close(1008, "Gateway not found");
3467
3518
  return;
3468
3519
  }
3469
- this.invokeHandler(ws, definition, definition.handlers.open);
3520
+ await this.invokeHandler(ws, definition, definition.handlers.open);
3470
3521
  }
3471
- handleMessage(ws, message) {
3522
+ async handleMessage(ws, message) {
3472
3523
  const path = ws.data?.path;
3473
3524
  const definition = path ? this.getGateway(path) : undefined;
3474
3525
  if (!definition) {
3475
3526
  ws.close(1008, "Gateway not found");
3476
3527
  return;
3477
3528
  }
3478
- this.invokeHandler(ws, definition, definition.handlers.message, message);
3529
+ await this.invokeHandler(ws, definition, definition.handlers.message, message);
3479
3530
  }
3480
- handleClose(ws, code, reason) {
3531
+ async handleClose(ws, code, reason) {
3481
3532
  const path = ws.data?.path;
3482
3533
  const definition = path ? this.getGateway(path) : undefined;
3483
3534
  if (!definition) {
3484
3535
  return;
3485
3536
  }
3486
- this.invokeHandler(ws, definition, definition.handlers.close, code, reason);
3537
+ await this.invokeHandler(ws, definition, definition.handlers.close, code, reason);
3487
3538
  }
3488
3539
  }
3489
3540
 
@@ -1,7 +1,10 @@
1
1
  import type { ServerWebSocket } from 'bun';
2
+ import { Context } from '../core/context';
2
3
  import type { Constructor } from '@/core/types';
3
4
  export interface WebSocketConnectionData {
4
5
  path: string;
6
+ query?: URLSearchParams;
7
+ context?: Context;
5
8
  }
6
9
  export declare class WebSocketGatewayRegistry {
7
10
  private static instance;
@@ -13,10 +16,22 @@ export declare class WebSocketGatewayRegistry {
13
16
  hasGateway(path: string): boolean;
14
17
  clear(): void;
15
18
  private getGateway;
16
- private getGatewayInstance;
19
+ /**
20
+ * 动态创建网关实例(每次连接创建新实例)
21
+ * @param definition - 网关定义
22
+ * @returns 网关实例
23
+ */
24
+ private createGatewayInstance;
25
+ /**
26
+ * 调用处理器,支持参数绑定
27
+ * @param ws - WebSocket 连接
28
+ * @param definition - 网关定义
29
+ * @param handlerName - 处理器方法名
30
+ * @param args - 原始参数(message, code, reason 等,不包括 ws)
31
+ */
17
32
  private invokeHandler;
18
- handleOpen(ws: ServerWebSocket<WebSocketConnectionData>): void;
19
- handleMessage(ws: ServerWebSocket<WebSocketConnectionData>, message: string | ArrayBuffer | ArrayBufferView): void;
20
- handleClose(ws: ServerWebSocket<WebSocketConnectionData>, code: number, reason: string): void;
33
+ handleOpen(ws: ServerWebSocket<WebSocketConnectionData>): Promise<void>;
34
+ handleMessage(ws: ServerWebSocket<WebSocketConnectionData>, message: string | ArrayBuffer | ArrayBufferView): Promise<void>;
35
+ handleClose(ws: ServerWebSocket<WebSocketConnectionData>, code: number, reason: string): Promise<void>;
21
36
  }
22
37
  //# sourceMappingURL=registry.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/websocket/registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,KAAK,CAAC;AAK3C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAa/C,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,MAAM,CAAC;CACd;AAED,qBAAa,wBAAwB;IACnC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAA2B;IAClD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAY;IACtC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAwC;IAEjE,OAAO;WAIO,WAAW,IAAI,wBAAwB;IAO9C,QAAQ,CAAC,YAAY,EAAE,WAAW,CAAC,OAAO,CAAC,GAAG,IAAI;IAsBlD,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAIjC,KAAK,IAAI,IAAI;IAIpB,OAAO,CAAC,UAAU;IAIlB,OAAO,CAAC,kBAAkB;IAO1B,OAAO,CAAC,aAAa;IAgBd,UAAU,CAAC,EAAE,EAAE,eAAe,CAAC,uBAAuB,CAAC,GAAG,IAAI;IAU9D,aAAa,CAClB,EAAE,EAAE,eAAe,CAAC,uBAAuB,CAAC,EAC5C,OAAO,EAAE,MAAM,GAAG,WAAW,GAAG,eAAe,GAC9C,IAAI;IAUA,WAAW,CAChB,EAAE,EAAE,eAAe,CAAC,uBAAuB,CAAC,EAC5C,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,GACb,IAAI;CAQR"}
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/websocket/registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,KAAK,CAAC;AAO3C,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC1C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAYhD,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,eAAe,CAAC;IACxB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,qBAAa,wBAAwB;IACnC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAA2B;IAClD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAY;IACtC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAwC;IAEjE,OAAO;WAIO,WAAW,IAAI,wBAAwB;IAO9C,QAAQ,CAAC,YAAY,EAAE,WAAW,CAAC,OAAO,CAAC,GAAG,IAAI;IAsBlD,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAIjC,KAAK,IAAI,IAAI;IAIpB,OAAO,CAAC,UAAU;IAIlB;;;;OAIG;IACH,OAAO,CAAC,qBAAqB;IAI7B;;;;;;OAMG;YACW,aAAa;IAiGd,UAAU,CAAC,EAAE,EAAE,eAAe,CAAC,uBAAuB,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAUvE,aAAa,CACxB,EAAE,EAAE,eAAe,CAAC,uBAAuB,CAAC,EAC5C,OAAO,EAAE,MAAM,GAAG,WAAW,GAAG,eAAe,GAC9C,OAAO,CAAC,IAAI,CAAC;IAUH,WAAW,CACtB,EAAE,EAAE,eAAe,CAAC,uBAAuB,CAAC,EAC5C,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC;CAQjB"}
package/docs/zh/guide.md CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  ```ts
8
8
  import "reflect-metadata";
9
- import { Application } from "../src";
9
+ import { Application } from "@dangao/bun-server";
10
10
 
11
11
  const app = new Application({ port: 3000 });
12
12
  app.listen();
@@ -26,7 +26,7 @@ import {
26
26
  Injectable,
27
27
  Param,
28
28
  POST,
29
- } from "../src";
29
+ } from "@dangao/bun-server";
30
30
 
31
31
  @Injectable()
32
32
  class UserService {
@@ -64,7 +64,7 @@ app.listen();
64
64
  ## 3. 使用中间件
65
65
 
66
66
  ```ts
67
- import { createCorsMiddleware, createLoggerMiddleware } from "../src";
67
+ import { createCorsMiddleware, createLoggerMiddleware } from "@dangao/bun-server";
68
68
 
69
69
  const app = new Application();
70
70
  app.use(createLoggerMiddleware({ prefix: "[Example]" }));
@@ -74,7 +74,7 @@ app.use(createCorsMiddleware({ origin: "*" }));
74
74
  `@UseMiddleware()` 可作用于单个控制器或方法:
75
75
 
76
76
  ```ts
77
- import { UseMiddleware } from '../src';
77
+ import { UseMiddleware } from "@dangao/bun-server";
78
78
 
79
79
  const auth = async (ctx, next) => {
80
80
  if (ctx.getHeader('authorization') !== 'token') {
@@ -92,7 +92,7 @@ class SecureController { ... }
92
92
  ## 4. 参数验证
93
93
 
94
94
  ```ts
95
- import { Validate, IsEmail, MinLength } from '../src';
95
+ import { Validate, IsEmail, MinLength } from "@dangao/bun-server";
96
96
 
97
97
  @POST('/register')
98
98
  public register(
@@ -108,32 +108,37 @@ public register(
108
108
  ## 5. WebSocket 网关
109
109
 
110
110
  ```ts
111
- import { OnMessage, WebSocketGateway } from "../src";
111
+ import { OnMessage, WebSocketGateway } from "@dangao/bun-server";
112
+ import type { ServerWebSocket } from "bun";
112
113
 
113
- @WebSocketGateway("/ws/chat")
114
+ @WebSocketGateway("/ws")
114
115
  class ChatGateway {
115
116
  @OnMessage
116
- public onMessage(ws, message: string) {
117
- ws.send(`echo: ${message}`);
117
+ public handleMessage(ws: ServerWebSocket, message: string) {
118
+ ws.send(`Echo: ${message}`);
118
119
  }
119
120
  }
120
121
 
122
+ const app = new Application({ port: 3000 });
121
123
  app.registerWebSocketGateway(ChatGateway);
124
+ app.listen();
122
125
  ```
123
126
 
124
127
  ## 6. 文件上传与静态资源
125
128
 
126
129
  ```ts
127
- import { createFileUploadMiddleware, createStaticFileMiddleware } from "../src";
130
+ import {
131
+ createFileUploadMiddleware,
132
+ createStaticFileMiddleware,
133
+ } from "@dangao/bun-server";
128
134
 
135
+ const app = new Application({ port: 3000 });
136
+
137
+ // File upload
129
138
  app.use(createFileUploadMiddleware({ maxSize: 5 * 1024 * 1024 }));
130
- app.use(
131
- createStaticFileMiddleware({
132
- root: "./public",
133
- prefix: "/assets",
134
- enableCache: true,
135
- }),
136
- );
139
+
140
+ // Static files
141
+ app.use(createStaticFileMiddleware({ root: "./public", prefix: "/assets" }));
137
142
  ```
138
143
 
139
144
  上传后的文件可在 `context.body.files` 中读取;静态资源请求会自动设置
@@ -142,7 +147,7 @@ Content-Type 与缓存头。
142
147
  ## 7. 错误处理与自定义过滤器
143
148
 
144
149
  ```ts
145
- import { ExceptionFilterRegistry, HttpException } from "../src";
150
+ import { ExceptionFilterRegistry, HttpException } from "@dangao/bun-server";
146
151
 
147
152
  ExceptionFilterRegistry.getInstance().register({
148
153
  catch(error, context) {
@@ -63,9 +63,15 @@
63
63
 
64
64
  - 将原本在 `Bun.serve({ websocket })` 中手写的逻辑迁移到 `@WebSocketGateway`:
65
65
  ```ts
66
- @WebSocketGateway('/ws/chat')
66
+ import { OnMessage, WebSocketGateway } from "@dangao/bun-server";
67
+ import type { ServerWebSocket } from "bun";
68
+
69
+ @WebSocketGateway('/ws')
67
70
  class ChatGateway {
68
- @OnMessage onMessage(ws, msg) { ws.send(msg); }
71
+ @OnMessage
72
+ public handleMessage(ws: ServerWebSocket, message: string) {
73
+ ws.send(`Echo: ${message}`);
74
+ }
69
75
  }
70
76
  app.registerWebSocketGateway(ChatGateway);
71
77
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dangao/bun-server",
3
- "version": "1.5.0",
3
+ "version": "1.6.0",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -89,8 +89,16 @@ export class BunServer {
89
89
  if (!this.options.websocketRegistry.hasGateway(url.pathname)) {
90
90
  return new Response("WebSocket gateway not found", { status: 404 });
91
91
  }
92
+ // 创建 Context 以便在 WebSocket 处理器中使用
93
+ const context = new Context(request);
94
+ // 创建 Bun 兼容的 URLSearchParams(需要 toJSON 方法)
95
+ const queryParams = new URLSearchParams(url.searchParams);
92
96
  const upgraded = server.upgrade(request, {
93
- data: { path: url.pathname },
97
+ data: {
98
+ path: url.pathname,
99
+ query: queryParams,
100
+ context,
101
+ },
94
102
  });
95
103
  if (upgraded) {
96
104
  return undefined;
@@ -128,14 +136,14 @@ export class BunServer {
128
136
  return responsePromise;
129
137
  },
130
138
  websocket: {
131
- open: (ws) => {
132
- this.options.websocketRegistry?.handleOpen(ws);
139
+ open: async (ws) => {
140
+ await this.options.websocketRegistry?.handleOpen(ws);
133
141
  },
134
- message: (ws, message) => {
135
- this.options.websocketRegistry?.handleMessage(ws, message);
142
+ message: async (ws, message) => {
143
+ await this.options.websocketRegistry?.handleMessage(ws, message);
136
144
  },
137
- close: (ws, code, reason) => {
138
- this.options.websocketRegistry?.handleClose(ws, code, reason);
145
+ close: async (ws, code, reason) => {
146
+ await this.options.websocketRegistry?.handleClose(ws, code, reason);
139
147
  },
140
148
  },
141
149
  });
@@ -3,7 +3,10 @@ import type { ServerWebSocket } from 'bun';
3
3
  import { Container } from '../di/container';
4
4
  import { ControllerRegistry } from '../controller/controller';
5
5
  import { getGatewayMetadata, getHandlerMetadata } from './decorators';
6
- import type { Constructor } from '@/core/types'
6
+ import { ParamBinder } from '../controller/param-binder';
7
+ import { getParamMetadata } from '../controller/decorators';
8
+ import { Context } from '../core/context';
9
+ import type { Constructor } from '@/core/types';
7
10
 
8
11
  interface GatewayDefinition {
9
12
  path: string;
@@ -13,11 +16,12 @@ interface GatewayDefinition {
13
16
  message?: string;
14
17
  close?: string;
15
18
  };
16
- instance?: unknown;
17
19
  }
18
20
 
19
21
  export interface WebSocketConnectionData {
20
22
  path: string;
23
+ query?: URLSearchParams;
24
+ context?: Context;
21
25
  }
22
26
 
23
27
  export class WebSocketGatewayRegistry {
@@ -70,63 +74,153 @@ export class WebSocketGatewayRegistry {
70
74
  return this.gateways.get(path);
71
75
  }
72
76
 
73
- private getGatewayInstance(definition: GatewayDefinition): unknown {
74
- if (!definition.instance) {
75
- definition.instance = this.container.resolve(definition.gatewayClass);
76
- }
77
- return definition.instance;
77
+ /**
78
+ * 动态创建网关实例(每次连接创建新实例)
79
+ * @param definition - 网关定义
80
+ * @returns 网关实例
81
+ */
82
+ private createGatewayInstance(definition: GatewayDefinition): unknown {
83
+ return this.container.resolve(definition.gatewayClass);
78
84
  }
79
85
 
80
- private invokeHandler(
86
+ /**
87
+ * 调用处理器,支持参数绑定
88
+ * @param ws - WebSocket 连接
89
+ * @param definition - 网关定义
90
+ * @param handlerName - 处理器方法名
91
+ * @param args - 原始参数(message, code, reason 等,不包括 ws)
92
+ */
93
+ private async invokeHandler(
81
94
  ws: ServerWebSocket<WebSocketConnectionData>,
82
95
  definition: GatewayDefinition,
83
96
  handlerName: string | undefined,
84
97
  ...args: unknown[]
85
- ): void {
98
+ ): Promise<void> {
86
99
  if (!handlerName) {
87
100
  return;
88
101
  }
89
- const instance = this.getGatewayInstance(definition);
102
+
103
+ // 动态创建实例(每次连接创建新实例)
104
+ const instance = this.createGatewayInstance(definition);
90
105
  const handler = (instance as Record<string, unknown>)[handlerName];
91
- if (typeof handler === 'function') {
106
+
107
+ if (typeof handler !== 'function') {
108
+ return;
109
+ }
110
+
111
+ // 获取参数元数据
112
+ const prototype = definition.gatewayClass.prototype;
113
+ const paramMetadata = getParamMetadata(prototype, handlerName);
114
+
115
+ // 如果有参数装饰器,使用参数绑定
116
+ if (paramMetadata.length > 0) {
117
+ // 创建或获取 Context
118
+ let context = ws.data?.context;
119
+ if (!context) {
120
+ // 从 WebSocket 连接数据创建 Context
121
+ const url = new URL(ws.data.path || '/', 'http://localhost');
122
+ if (ws.data.query) {
123
+ ws.data.query.forEach((value, key) => {
124
+ url.searchParams.set(key, value);
125
+ });
126
+ }
127
+ const request = new Request(url.toString(), {
128
+ method: 'GET',
129
+ headers: new Headers(),
130
+ });
131
+ context = new Context(request);
132
+ ws.data.context = context;
133
+ }
134
+
135
+ // 使用 ParamBinder 绑定参数
136
+ const boundParams = await ParamBinder.bind(
137
+ prototype,
138
+ handlerName,
139
+ context,
140
+ this.container,
141
+ );
142
+
143
+ // 构建最终参数数组
144
+ // WebSocket 处理器的参数顺序:ws, [message/code/reason], [装饰器参数...]
145
+ const finalArgs: unknown[] = [];
146
+ const maxParamIndex = paramMetadata.length > 0
147
+ ? Math.max(...paramMetadata.map((m) => m.index))
148
+ : -1;
149
+
150
+ // 按方法签名顺序填充参数
151
+ // 首先确定方法的总参数数量(包括 ws 和原始参数)
152
+ const totalParamCount = Math.max(
153
+ maxParamIndex + 1,
154
+ args.length + 1, // +1 是 ws 参数
155
+ );
156
+
157
+ for (let i = 0; i < totalParamCount; i++) {
158
+ const meta = paramMetadata.find((m) => m.index === i);
159
+ if (meta) {
160
+ // 使用绑定参数(从 ParamBinder 获取)
161
+ finalArgs[i] = boundParams[i];
162
+ } else if (i === 0) {
163
+ // 第一个参数是 ws
164
+ finalArgs[i] = ws;
165
+ } else {
166
+ // 其他参数是原始参数(message, code, reason 等)
167
+ const argIndex = i - 1;
168
+ if (argIndex < args.length) {
169
+ finalArgs[i] = args[argIndex];
170
+ } else {
171
+ finalArgs[i] = undefined;
172
+ }
173
+ }
174
+ }
175
+
176
+ // 如果方法签名参数少于实际参数,添加剩余参数
177
+ if (args.length > maxParamIndex) {
178
+ for (let i = maxParamIndex + 1; i < args.length; i++) {
179
+ finalArgs.push(args[i]);
180
+ }
181
+ }
182
+
183
+ handler.apply(instance, finalArgs);
184
+ } else {
185
+ // 没有参数装饰器,直接调用:ws, ...args
92
186
  handler.apply(instance, [ws, ...args]);
93
187
  }
94
188
  }
95
189
 
96
- public handleOpen(ws: ServerWebSocket<WebSocketConnectionData>): void {
190
+ public async handleOpen(ws: ServerWebSocket<WebSocketConnectionData>): Promise<void> {
97
191
  const path = ws.data?.path;
98
192
  const definition = path ? this.getGateway(path) : undefined;
99
193
  if (!definition) {
100
194
  ws.close(1008, 'Gateway not found');
101
195
  return;
102
196
  }
103
- this.invokeHandler(ws, definition, definition.handlers.open);
197
+ await this.invokeHandler(ws, definition, definition.handlers.open);
104
198
  }
105
199
 
106
- public handleMessage(
200
+ public async handleMessage(
107
201
  ws: ServerWebSocket<WebSocketConnectionData>,
108
202
  message: string | ArrayBuffer | ArrayBufferView,
109
- ): void {
203
+ ): Promise<void> {
110
204
  const path = ws.data?.path;
111
205
  const definition = path ? this.getGateway(path) : undefined;
112
206
  if (!definition) {
113
207
  ws.close(1008, 'Gateway not found');
114
208
  return;
115
209
  }
116
- this.invokeHandler(ws, definition, definition.handlers.message, message);
210
+ await this.invokeHandler(ws, definition, definition.handlers.message, message);
117
211
  }
118
212
 
119
- public handleClose(
213
+ public async handleClose(
120
214
  ws: ServerWebSocket<WebSocketConnectionData>,
121
215
  code: number,
122
216
  reason: string,
123
- ): void {
217
+ ): Promise<void> {
124
218
  const path = ws.data?.path;
125
219
  const definition = path ? this.getGateway(path) : undefined;
126
220
  if (!definition) {
127
221
  return;
128
222
  }
129
- this.invokeHandler(ws, definition, definition.handlers.close, code, reason);
223
+ await this.invokeHandler(ws, definition, definition.handlers.close, code, reason);
130
224
  }
131
225
  }
132
226