@dangao/bun-server 1.5.0 → 1.7.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;IAoGpB;;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,10 +3409,14 @@ 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;
3409
3417
  gateways = new Map;
3418
+ staticGateways = new Map;
3419
+ dynamicGateways = [];
3410
3420
  constructor() {
3411
3421
  this.container = ControllerRegistry.getInstance().getContainer();
3412
3422
  }
@@ -3416,6 +3426,15 @@ class WebSocketGatewayRegistry {
3416
3426
  }
3417
3427
  return WebSocketGatewayRegistry.instance;
3418
3428
  }
3429
+ parsePath(path) {
3430
+ const paramNames = [];
3431
+ const patternString = path.replace(/:([^/]+)/g, (_, paramName) => {
3432
+ paramNames.push(paramName);
3433
+ return "([^/]+)";
3434
+ }).replace(/\*/g, ".*");
3435
+ const pattern = new RegExp(`^${patternString}$`);
3436
+ return { pattern, paramNames };
3437
+ }
3419
3438
  register(gatewayClass) {
3420
3439
  const metadata = getGatewayMetadata(gatewayClass);
3421
3440
  if (!metadata) {
@@ -3428,62 +3447,153 @@ class WebSocketGatewayRegistry {
3428
3447
  if (!this.container.isRegistered(gatewayClass)) {
3429
3448
  this.container.register(gatewayClass);
3430
3449
  }
3431
- this.gateways.set(metadata.path, {
3450
+ const { pattern, paramNames } = this.parsePath(metadata.path);
3451
+ const isStatic = !metadata.path.includes(":") && !metadata.path.includes("*");
3452
+ const definition = {
3432
3453
  path: metadata.path,
3433
3454
  gatewayClass,
3434
- handlers
3435
- });
3455
+ handlers,
3456
+ pattern,
3457
+ paramNames,
3458
+ isStatic
3459
+ };
3460
+ this.gateways.set(metadata.path, definition);
3461
+ if (isStatic) {
3462
+ this.staticGateways.set(metadata.path, definition);
3463
+ } else {
3464
+ this.dynamicGateways.push(definition);
3465
+ }
3436
3466
  }
3437
3467
  hasGateway(path) {
3438
- return this.gateways.has(path);
3468
+ if (this.staticGateways.has(path)) {
3469
+ return true;
3470
+ }
3471
+ for (const gateway of this.dynamicGateways) {
3472
+ if (gateway.pattern.test(path)) {
3473
+ return true;
3474
+ }
3475
+ }
3476
+ return false;
3439
3477
  }
3440
3478
  clear() {
3441
3479
  this.gateways.clear();
3480
+ this.staticGateways.clear();
3481
+ this.dynamicGateways.length = 0;
3442
3482
  }
3443
3483
  getGateway(path) {
3444
- return this.gateways.get(path);
3445
- }
3446
- getGatewayInstance(definition) {
3447
- if (!definition.instance) {
3448
- definition.instance = this.container.resolve(definition.gatewayClass);
3484
+ const staticGateway = this.staticGateways.get(path);
3485
+ if (staticGateway) {
3486
+ return { definition: staticGateway, params: {} };
3487
+ }
3488
+ for (const gateway of this.dynamicGateways) {
3489
+ const match = path.match(gateway.pattern);
3490
+ if (match) {
3491
+ const params = {};
3492
+ for (let i = 0;i < gateway.paramNames.length; i++) {
3493
+ params[gateway.paramNames[i]] = match[i + 1] ?? "";
3494
+ }
3495
+ return { definition: gateway, params };
3496
+ }
3449
3497
  }
3450
- return definition.instance;
3498
+ return;
3499
+ }
3500
+ createGatewayInstance(definition) {
3501
+ return this.container.resolve(definition.gatewayClass);
3451
3502
  }
3452
- invokeHandler(ws, definition, handlerName, ...args) {
3503
+ async invokeHandler(ws, definition, handlerName, ...args) {
3453
3504
  if (!handlerName) {
3454
3505
  return;
3455
3506
  }
3456
- const instance = this.getGatewayInstance(definition);
3507
+ const instance = this.createGatewayInstance(definition);
3457
3508
  const handler = instance[handlerName];
3458
- if (typeof handler === "function") {
3509
+ if (typeof handler !== "function") {
3510
+ return;
3511
+ }
3512
+ const prototype = definition.gatewayClass.prototype;
3513
+ const paramMetadata = getParamMetadata(prototype, handlerName);
3514
+ if (paramMetadata.length > 0) {
3515
+ let context = ws.data?.context;
3516
+ if (!context) {
3517
+ const url = new URL(ws.data.path || "/", "http://localhost");
3518
+ if (ws.data.query) {
3519
+ ws.data.query.forEach((value, key) => {
3520
+ url.searchParams.set(key, value);
3521
+ });
3522
+ }
3523
+ const request = new Request(url.toString(), {
3524
+ method: "GET",
3525
+ headers: new Headers
3526
+ });
3527
+ context = new Context(request);
3528
+ if (ws.data.params) {
3529
+ context.params = ws.data.params;
3530
+ }
3531
+ ws.data.context = context;
3532
+ } else {
3533
+ if (ws.data.params) {
3534
+ context.params = ws.data.params;
3535
+ }
3536
+ }
3537
+ const boundParams = await ParamBinder.bind(prototype, handlerName, context, this.container);
3538
+ const finalArgs = [];
3539
+ const maxParamIndex = paramMetadata.length > 0 ? Math.max(...paramMetadata.map((m) => m.index)) : -1;
3540
+ const totalParamCount = Math.max(maxParamIndex + 1, args.length + 1);
3541
+ for (let i = 0;i < totalParamCount; i++) {
3542
+ const meta = paramMetadata.find((m) => m.index === i);
3543
+ if (meta) {
3544
+ finalArgs[i] = boundParams[i];
3545
+ } else if (i === 0) {
3546
+ finalArgs[i] = ws;
3547
+ } else {
3548
+ const argIndex = i - 1;
3549
+ if (argIndex < args.length) {
3550
+ finalArgs[i] = args[argIndex];
3551
+ } else {
3552
+ finalArgs[i] = undefined;
3553
+ }
3554
+ }
3555
+ }
3556
+ if (args.length > maxParamIndex) {
3557
+ for (let i = maxParamIndex + 1;i < args.length; i++) {
3558
+ finalArgs.push(args[i]);
3559
+ }
3560
+ }
3561
+ handler.apply(instance, finalArgs);
3562
+ } else {
3459
3563
  handler.apply(instance, [ws, ...args]);
3460
3564
  }
3461
3565
  }
3462
- handleOpen(ws) {
3566
+ async handleOpen(ws) {
3463
3567
  const path = ws.data?.path;
3464
- const definition = path ? this.getGateway(path) : undefined;
3465
- if (!definition) {
3568
+ const match = path ? this.getGateway(path) : undefined;
3569
+ if (!match) {
3466
3570
  ws.close(1008, "Gateway not found");
3467
3571
  return;
3468
3572
  }
3469
- this.invokeHandler(ws, definition, definition.handlers.open);
3573
+ if (match.params && Object.keys(match.params).length > 0) {
3574
+ ws.data.params = match.params;
3575
+ }
3576
+ await this.invokeHandler(ws, match.definition, match.definition.handlers.open);
3470
3577
  }
3471
- handleMessage(ws, message) {
3578
+ async handleMessage(ws, message) {
3472
3579
  const path = ws.data?.path;
3473
- const definition = path ? this.getGateway(path) : undefined;
3474
- if (!definition) {
3580
+ const match = path ? this.getGateway(path) : undefined;
3581
+ if (!match) {
3475
3582
  ws.close(1008, "Gateway not found");
3476
3583
  return;
3477
3584
  }
3478
- this.invokeHandler(ws, definition, definition.handlers.message, message);
3585
+ if (match.params && Object.keys(match.params).length > 0 && !ws.data.params) {
3586
+ ws.data.params = match.params;
3587
+ }
3588
+ await this.invokeHandler(ws, match.definition, match.definition.handlers.message, message);
3479
3589
  }
3480
- handleClose(ws, code, reason) {
3590
+ async handleClose(ws, code, reason) {
3481
3591
  const path = ws.data?.path;
3482
- const definition = path ? this.getGateway(path) : undefined;
3483
- if (!definition) {
3592
+ const match = path ? this.getGateway(path) : undefined;
3593
+ if (!match) {
3484
3594
  return;
3485
3595
  }
3486
- this.invokeHandler(ws, definition, definition.handlers.close, code, reason);
3596
+ await this.invokeHandler(ws, match.definition, match.definition.handlers.close, code, reason);
3487
3597
  }
3488
3598
  }
3489
3599
 
@@ -1,22 +1,56 @@
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;
8
+ params?: Record<string, string>;
5
9
  }
6
10
  export declare class WebSocketGatewayRegistry {
7
11
  private static instance;
8
12
  private readonly container;
9
13
  private readonly gateways;
14
+ private readonly staticGateways;
15
+ private readonly dynamicGateways;
10
16
  private constructor();
11
17
  static getInstance(): WebSocketGatewayRegistry;
18
+ /**
19
+ * 解析路径,生成匹配模式和参数名列表
20
+ * @param path - 路由路径
21
+ * @returns 匹配模式和参数名列表
22
+ */
23
+ private parsePath;
12
24
  register(gatewayClass: Constructor<unknown>): void;
25
+ /**
26
+ * 检查是否有匹配的网关(支持动态路径匹配)
27
+ * @param path - 请求路径
28
+ * @returns 是否有匹配的网关
29
+ */
13
30
  hasGateway(path: string): boolean;
14
31
  clear(): void;
32
+ /**
33
+ * 获取匹配的网关(支持动态路径匹配)
34
+ * @param path - 请求路径
35
+ * @returns 匹配的网关定义和路径参数
36
+ */
15
37
  private getGateway;
16
- private getGatewayInstance;
38
+ /**
39
+ * 动态创建网关实例(每次连接创建新实例)
40
+ * @param definition - 网关定义
41
+ * @returns 网关实例
42
+ */
43
+ private createGatewayInstance;
44
+ /**
45
+ * 调用处理器,支持参数绑定
46
+ * @param ws - WebSocket 连接
47
+ * @param definition - 网关定义
48
+ * @param handlerName - 处理器方法名
49
+ * @param args - 原始参数(message, code, reason 等,不包括 ws)
50
+ */
17
51
  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;
52
+ handleOpen(ws: ServerWebSocket<WebSocketConnectionData>): Promise<void>;
53
+ handleMessage(ws: ServerWebSocket<WebSocketConnectionData>, message: string | ArrayBuffer | ArrayBufferView): Promise<void>;
54
+ handleClose(ws: ServerWebSocket<WebSocketConnectionData>, code: number, reason: string): Promise<void>;
21
55
  }
22
56
  //# 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;AAehD,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,eAAe,CAAC;IACxB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC;AAED,qBAAa,wBAAwB;IACnC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAA2B;IAClD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAY;IACtC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAwC;IACjE,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAwC;IACvE,OAAO,CAAC,QAAQ,CAAC,eAAe,CAA2B;IAE3D,OAAO;WAIO,WAAW,IAAI,wBAAwB;IAOrD;;;;OAIG;IACH,OAAO,CAAC,SAAS;IAaV,QAAQ,CAAC,YAAY,EAAE,WAAW,CAAC,OAAO,CAAC,GAAG,IAAI;IAsCzD;;;;OAIG;IACI,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAgBjC,KAAK,IAAI,IAAI;IAMpB;;;;OAIG;IACH,OAAO,CAAC,UAAU;IAuBlB;;;;OAIG;IACH,OAAO,CAAC,qBAAqB;IAI7B;;;;;;OAMG;YACW,aAAa;IA0Gd,UAAU,CAAC,EAAE,EAAE,eAAe,CAAC,uBAAuB,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAcvE,aAAa,CACxB,EAAE,EAAE,eAAe,CAAC,uBAAuB,CAAC,EAC5C,OAAO,EAAE,MAAM,GAAG,WAAW,GAAG,eAAe,GAC9C,OAAO,CAAC,IAAI,CAAC;IAcH,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.7.0",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -86,11 +86,20 @@ export class BunServer {
86
86
  upgradeHeader.toLowerCase() === "websocket"
87
87
  ) {
88
88
  const url = new URL(request.url);
89
+ // 检查是否有匹配的网关(支持动态路径匹配)
89
90
  if (!this.options.websocketRegistry.hasGateway(url.pathname)) {
90
91
  return new Response("WebSocket gateway not found", { status: 404 });
91
92
  }
93
+ // 创建 Context 以便在 WebSocket 处理器中使用
94
+ const context = new Context(request);
95
+ // 创建 Bun 兼容的 URLSearchParams(需要 toJSON 方法)
96
+ const queryParams = new URLSearchParams(url.searchParams);
92
97
  const upgraded = server.upgrade(request, {
93
- data: { path: url.pathname },
98
+ data: {
99
+ path: url.pathname,
100
+ query: queryParams,
101
+ context,
102
+ },
94
103
  });
95
104
  if (upgraded) {
96
105
  return undefined;
@@ -128,14 +137,14 @@ export class BunServer {
128
137
  return responsePromise;
129
138
  },
130
139
  websocket: {
131
- open: (ws) => {
132
- this.options.websocketRegistry?.handleOpen(ws);
140
+ open: async (ws) => {
141
+ await this.options.websocketRegistry?.handleOpen(ws);
133
142
  },
134
- message: (ws, message) => {
135
- this.options.websocketRegistry?.handleMessage(ws, message);
143
+ message: async (ws, message) => {
144
+ await this.options.websocketRegistry?.handleMessage(ws, message);
136
145
  },
137
- close: (ws, code, reason) => {
138
- this.options.websocketRegistry?.handleClose(ws, code, reason);
146
+ close: async (ws, code, reason) => {
147
+ await this.options.websocketRegistry?.handleClose(ws, code, reason);
139
148
  },
140
149
  },
141
150
  });