@dangao/bun-server 1.7.0 → 1.8.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.
Files changed (144) hide show
  1. package/README.md +196 -19
  2. package/dist/cache/cache-module.d.ts +18 -0
  3. package/dist/cache/cache-module.d.ts.map +1 -1
  4. package/dist/cache/index.d.ts +3 -1
  5. package/dist/cache/index.d.ts.map +1 -1
  6. package/dist/cache/interceptors.d.ts +41 -0
  7. package/dist/cache/interceptors.d.ts.map +1 -0
  8. package/dist/cache/service-proxy.d.ts +62 -0
  9. package/dist/cache/service-proxy.d.ts.map +1 -0
  10. package/dist/controller/controller.d.ts +8 -0
  11. package/dist/controller/controller.d.ts.map +1 -1
  12. package/dist/core/application.d.ts +5 -0
  13. package/dist/core/application.d.ts.map +1 -1
  14. package/dist/di/container.d.ts +18 -1
  15. package/dist/di/container.d.ts.map +1 -1
  16. package/dist/di/decorators.d.ts +37 -0
  17. package/dist/di/decorators.d.ts.map +1 -1
  18. package/dist/di/index.d.ts +2 -2
  19. package/dist/di/index.d.ts.map +1 -1
  20. package/dist/di/module-registry.d.ts +17 -0
  21. package/dist/di/module-registry.d.ts.map +1 -1
  22. package/dist/di/types.d.ts +22 -0
  23. package/dist/di/types.d.ts.map +1 -1
  24. package/dist/events/decorators.d.ts +52 -0
  25. package/dist/events/decorators.d.ts.map +1 -0
  26. package/dist/events/event-module.d.ts +97 -0
  27. package/dist/events/event-module.d.ts.map +1 -0
  28. package/dist/events/index.d.ts +5 -0
  29. package/dist/events/index.d.ts.map +1 -0
  30. package/dist/events/service.d.ts +76 -0
  31. package/dist/events/service.d.ts.map +1 -0
  32. package/dist/events/types.d.ts +184 -0
  33. package/dist/events/types.d.ts.map +1 -0
  34. package/dist/index.d.ts +6 -4
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +4641 -2840
  37. package/dist/security/filter.d.ts +23 -0
  38. package/dist/security/filter.d.ts.map +1 -1
  39. package/dist/security/guards/builtin/auth-guard.d.ts +44 -0
  40. package/dist/security/guards/builtin/auth-guard.d.ts.map +1 -0
  41. package/dist/security/guards/builtin/index.d.ts +3 -0
  42. package/dist/security/guards/builtin/index.d.ts.map +1 -0
  43. package/dist/security/guards/builtin/roles-guard.d.ts +66 -0
  44. package/dist/security/guards/builtin/roles-guard.d.ts.map +1 -0
  45. package/dist/security/guards/decorators.d.ts +50 -0
  46. package/dist/security/guards/decorators.d.ts.map +1 -0
  47. package/dist/security/guards/execution-context.d.ts +56 -0
  48. package/dist/security/guards/execution-context.d.ts.map +1 -0
  49. package/dist/security/guards/guard-registry.d.ts +67 -0
  50. package/dist/security/guards/guard-registry.d.ts.map +1 -0
  51. package/dist/security/guards/index.d.ts +7 -0
  52. package/dist/security/guards/index.d.ts.map +1 -0
  53. package/dist/security/guards/reflector.d.ts +57 -0
  54. package/dist/security/guards/reflector.d.ts.map +1 -0
  55. package/dist/security/guards/types.d.ts +126 -0
  56. package/dist/security/guards/types.d.ts.map +1 -0
  57. package/dist/security/index.d.ts +1 -0
  58. package/dist/security/index.d.ts.map +1 -1
  59. package/dist/security/security-module.d.ts +20 -0
  60. package/dist/security/security-module.d.ts.map +1 -1
  61. package/dist/validation/class-validator.d.ts +108 -0
  62. package/dist/validation/class-validator.d.ts.map +1 -0
  63. package/dist/validation/custom-validator.d.ts +130 -0
  64. package/dist/validation/custom-validator.d.ts.map +1 -0
  65. package/dist/validation/errors.d.ts +22 -2
  66. package/dist/validation/errors.d.ts.map +1 -1
  67. package/dist/validation/index.d.ts +7 -1
  68. package/dist/validation/index.d.ts.map +1 -1
  69. package/dist/validation/rules/array.d.ts +33 -0
  70. package/dist/validation/rules/array.d.ts.map +1 -0
  71. package/dist/validation/rules/common.d.ts +90 -0
  72. package/dist/validation/rules/common.d.ts.map +1 -0
  73. package/dist/validation/rules/conditional.d.ts +30 -0
  74. package/dist/validation/rules/conditional.d.ts.map +1 -0
  75. package/dist/validation/rules/index.d.ts +5 -0
  76. package/dist/validation/rules/index.d.ts.map +1 -0
  77. package/dist/validation/rules/object.d.ts +30 -0
  78. package/dist/validation/rules/object.d.ts.map +1 -0
  79. package/dist/validation/types.d.ts +52 -1
  80. package/dist/validation/types.d.ts.map +1 -1
  81. package/docs/events.md +494 -0
  82. package/docs/guards.md +376 -0
  83. package/docs/guide.md +309 -1
  84. package/docs/request-lifecycle.md +444 -0
  85. package/docs/symbol-interface-pattern.md +431 -0
  86. package/docs/validation.md +407 -0
  87. package/docs/zh/events.md +494 -0
  88. package/docs/zh/guards.md +376 -0
  89. package/docs/zh/guide.md +309 -1
  90. package/docs/zh/request-lifecycle.md +444 -0
  91. package/docs/zh/symbol-interface-pattern.md +431 -0
  92. package/docs/zh/validation.md +407 -0
  93. package/package.json +1 -1
  94. package/src/cache/cache-module.ts +37 -0
  95. package/src/cache/index.ts +16 -1
  96. package/src/cache/interceptors.ts +295 -0
  97. package/src/cache/service-proxy.ts +219 -0
  98. package/src/controller/controller.ts +30 -6
  99. package/src/core/application.ts +25 -1
  100. package/src/di/container.ts +57 -7
  101. package/src/di/decorators.ts +46 -0
  102. package/src/di/index.ts +17 -2
  103. package/src/di/module-registry.ts +39 -0
  104. package/src/di/types.ts +29 -0
  105. package/src/events/decorators.ts +103 -0
  106. package/src/events/event-module.ts +272 -0
  107. package/src/events/index.ts +32 -0
  108. package/src/events/service.ts +352 -0
  109. package/src/events/types.ts +223 -0
  110. package/src/index.ts +140 -1
  111. package/src/security/filter.ts +88 -8
  112. package/src/security/guards/builtin/auth-guard.ts +68 -0
  113. package/src/security/guards/builtin/index.ts +3 -0
  114. package/src/security/guards/builtin/roles-guard.ts +165 -0
  115. package/src/security/guards/decorators.ts +124 -0
  116. package/src/security/guards/execution-context.ts +152 -0
  117. package/src/security/guards/guard-registry.ts +164 -0
  118. package/src/security/guards/index.ts +7 -0
  119. package/src/security/guards/reflector.ts +99 -0
  120. package/src/security/guards/types.ts +144 -0
  121. package/src/security/index.ts +1 -0
  122. package/src/security/security-module.ts +72 -2
  123. package/src/validation/class-validator.ts +322 -0
  124. package/src/validation/custom-validator.ts +289 -0
  125. package/src/validation/errors.ts +50 -2
  126. package/src/validation/index.ts +103 -1
  127. package/src/validation/rules/array.ts +118 -0
  128. package/src/validation/rules/common.ts +286 -0
  129. package/src/validation/rules/conditional.ts +52 -0
  130. package/src/validation/rules/index.ts +51 -0
  131. package/src/validation/rules/object.ts +86 -0
  132. package/src/validation/types.ts +61 -1
  133. package/tests/cache/cache-decorators.test.ts +284 -0
  134. package/tests/controller/path-combination.test.ts +353 -0
  135. package/tests/di/global-module.test.ts +487 -0
  136. package/tests/events/event-decorators.test.ts +173 -0
  137. package/tests/events/event-emitter.test.ts +373 -0
  138. package/tests/events/event-module.test.ts +373 -0
  139. package/tests/security/guards/guards-integration.test.ts +371 -0
  140. package/tests/security/guards/guards.test.ts +775 -0
  141. package/tests/security/security-module.test.ts +2 -2
  142. package/tests/validation/class-validator.test.ts +349 -0
  143. package/tests/validation/custom-validator.test.ts +335 -0
  144. package/tests/validation/rules.test.ts +543 -0
@@ -11,13 +11,41 @@ export interface ValidationRuleDefinition {
11
11
 
12
12
  /**
13
13
  * 校验函数
14
+ * @param value - 要验证的值
15
+ * @param obj - 完整对象(用于条件验证)
14
16
  */
15
- validate: (value: unknown) => boolean;
17
+ validate: (value: unknown, obj?: unknown) => boolean;
16
18
 
17
19
  /**
18
20
  * 是否为可选字段
19
21
  */
20
22
  optional?: boolean;
23
+
24
+ /**
25
+ * 条件验证函数
26
+ * 返回 true 时执行验证,返回 false 时跳过
27
+ */
28
+ condition?: (value: unknown, obj?: unknown) => boolean;
29
+
30
+ /**
31
+ * 转换函数(在验证前执行)
32
+ */
33
+ transform?: (value: unknown) => unknown;
34
+
35
+ /**
36
+ * 是否为嵌套验证
37
+ */
38
+ nested?: boolean;
39
+
40
+ /**
41
+ * 嵌套验证的类型
42
+ */
43
+ nestedType?: new () => unknown;
44
+
45
+ /**
46
+ * 是否对数组每个元素执行嵌套验证
47
+ */
48
+ each?: boolean;
21
49
  }
22
50
 
23
51
  export interface ValidationMetadata {
@@ -32,4 +60,36 @@ export interface ValidationMetadata {
32
60
  rules: ValidationRuleDefinition[];
33
61
  }
34
62
 
63
+ /**
64
+ * 类级别验证元数据
65
+ */
66
+ export interface ClassValidationMetadata {
67
+ /**
68
+ * 属性名
69
+ */
70
+ property: string;
71
+
72
+ /**
73
+ * 规则列表
74
+ */
75
+ rules: ValidationRuleDefinition[];
76
+ }
77
+
78
+ /**
79
+ * 验证选项
80
+ */
81
+ export interface ValidationOptions {
82
+ /**
83
+ * 是否在第一个错误时停止验证
84
+ * @default false
85
+ */
86
+ stopAtFirstError?: boolean;
87
+
88
+ /**
89
+ * 是否跳过缺失的属性
90
+ * @default false
91
+ */
92
+ skipMissingProperties?: boolean;
93
+ }
94
+
35
95
 
@@ -0,0 +1,284 @@
1
+ import 'reflect-metadata';
2
+ import { describe, expect, test, beforeEach, afterEach } from 'bun:test';
3
+ import { Application } from '../../src/core/application';
4
+ import { Controller } from '../../src/controller/controller';
5
+ import { GET } from '../../src/router/decorators';
6
+ import { Param } from '../../src/controller/decorators';
7
+ import { Module } from '../../src/di/module';
8
+ import { Injectable, Inject } from '../../src/di/decorators';
9
+ import {
10
+ CacheModule,
11
+ CacheService,
12
+ Cacheable,
13
+ CacheEvict,
14
+ CachePut,
15
+ EnableCacheProxy,
16
+ CACHE_SERVICE_TOKEN,
17
+ } from '../../src/cache';
18
+ import { RouteRegistry } from '../../src/router/registry';
19
+ import { ControllerRegistry } from '../../src/controller/controller';
20
+ import { ModuleRegistry } from '../../src/di/module-registry';
21
+ import { getTestPort } from '../utils/test-port';
22
+
23
+ describe('Cache Decorators', () => {
24
+ let app: Application;
25
+ let port: number;
26
+ let callCount: number;
27
+
28
+ beforeEach(() => {
29
+ port = getTestPort();
30
+ callCount = 0;
31
+ CacheModule.reset();
32
+ });
33
+
34
+ afterEach(async () => {
35
+ if (app) {
36
+ await app.stop();
37
+ }
38
+ RouteRegistry.getInstance().clear();
39
+ ControllerRegistry.getInstance().clear();
40
+ ModuleRegistry.getInstance().clear();
41
+ });
42
+
43
+ test('@Cacheable should cache method results in service layer', async () => {
44
+ // 定义服务
45
+ @Injectable()
46
+ @EnableCacheProxy()
47
+ class UserService {
48
+ @Cacheable({ key: 'user:{0}', ttl: 5000 })
49
+ public async findById(id: string): Promise<{ id: string; name: string }> {
50
+ callCount++;
51
+ return { id, name: `User ${id}` };
52
+ }
53
+ }
54
+
55
+ // 定义控制器
56
+ @Controller('/api/users')
57
+ class UserController {
58
+ public constructor(private readonly userService: UserService) {}
59
+
60
+ @GET('/:id')
61
+ public async getUser(@Param('id') id: string): Promise<{ id: string; name: string }> {
62
+ return await this.userService.findById(id);
63
+ }
64
+ }
65
+
66
+ // 定义模块
67
+ @Module({
68
+ imports: [CacheModule.forRoot({ defaultTtl: 5000 })],
69
+ controllers: [UserController],
70
+ providers: [UserService],
71
+ })
72
+ class AppModule {}
73
+
74
+ // 创建应用
75
+ app = new Application({ port });
76
+ app.registerModule(AppModule);
77
+ await app.listen();
78
+
79
+ // 第一次调用 - 应该执行方法
80
+ const response1 = await fetch(`http://localhost:${port}/api/users/1`);
81
+ expect(response1.status).toBe(200);
82
+ const data1 = await response1.json();
83
+ expect(data1.id).toBe('1');
84
+ expect(data1.name).toBe('User 1');
85
+ expect(callCount).toBe(1);
86
+
87
+ // 第二次调用 - 应该使用缓存
88
+ const response2 = await fetch(`http://localhost:${port}/api/users/1`);
89
+ expect(response2.status).toBe(200);
90
+ const data2 = await response2.json();
91
+ expect(data2.id).toBe('1');
92
+ expect(data2.name).toBe('User 1');
93
+ expect(callCount).toBe(1); // 方法没有被再次调用
94
+
95
+ // 调用不同的 ID - 应该执行方法
96
+ const response3 = await fetch(`http://localhost:${port}/api/users/2`);
97
+ expect(response3.status).toBe(200);
98
+ const data3 = await response3.json();
99
+ expect(data3.id).toBe('2');
100
+ expect(data3.name).toBe('User 2');
101
+ expect(callCount).toBe(2);
102
+ });
103
+
104
+ test('@CacheEvict should clear cache', async () => {
105
+ // 定义服务
106
+ @Injectable()
107
+ @EnableCacheProxy()
108
+ class ProductService {
109
+ @Cacheable({ key: 'product:{0}', ttl: 5000 })
110
+ public async findById(id: string): Promise<{ id: string; name: string }> {
111
+ callCount++;
112
+ return { id, name: `Product ${id} v${callCount}` };
113
+ }
114
+
115
+ @CacheEvict({ key: 'product:{0}' })
116
+ public async deleteById(id: string): Promise<void> {
117
+ // 删除产品
118
+ }
119
+ }
120
+
121
+ // 定义控制器
122
+ @Controller('/api/products')
123
+ class ProductController {
124
+ public constructor(private readonly productService: ProductService) {}
125
+
126
+ @GET('/:id')
127
+ public async getProduct(@Param('id') id: string): Promise<{ id: string; name: string }> {
128
+ return await this.productService.findById(id);
129
+ }
130
+
131
+ @GET('/:id/delete')
132
+ public async deleteProduct(@Param('id') id: string): Promise<{ success: boolean }> {
133
+ await this.productService.deleteById(id);
134
+ return { success: true };
135
+ }
136
+ }
137
+
138
+ // 定义模块
139
+ @Module({
140
+ imports: [CacheModule.forRoot({ defaultTtl: 5000 })],
141
+ controllers: [ProductController],
142
+ providers: [ProductService],
143
+ })
144
+ class AppModule {}
145
+
146
+ // 创建应用
147
+ app = new Application({ port });
148
+ app.registerModule(AppModule);
149
+ await app.listen();
150
+
151
+ // 第一次调用 - 缓存产品
152
+ const response1 = await fetch(`http://localhost:${port}/api/products/1`);
153
+ expect(response1.status).toBe(200);
154
+ const data1 = await response1.json();
155
+ expect(data1.name).toBe('Product 1 v1');
156
+ expect(callCount).toBe(1);
157
+
158
+ // 第二次调用 - 使用缓存
159
+ const response2 = await fetch(`http://localhost:${port}/api/products/1`);
160
+ const data2 = await response2.json();
161
+ expect(data2.name).toBe('Product 1 v1');
162
+ expect(callCount).toBe(1);
163
+
164
+ // 删除产品 - 清除缓存
165
+ await fetch(`http://localhost:${port}/api/products/1/delete`);
166
+
167
+ // 再次获取 - 缓存已清除,应重新执行方法
168
+ const response3 = await fetch(`http://localhost:${port}/api/products/1`);
169
+ const data3 = await response3.json();
170
+ expect(data3.name).toBe('Product 1 v2');
171
+ expect(callCount).toBe(2);
172
+ });
173
+
174
+ test('@CachePut should always execute method and update cache', async () => {
175
+ // 定义服务
176
+ @Injectable()
177
+ @EnableCacheProxy()
178
+ class OrderService {
179
+ @Cacheable({ key: 'order:{0}', ttl: 5000 })
180
+ public async findById(id: string): Promise<{ id: string; status: string }> {
181
+ callCount++;
182
+ return { id, status: 'pending' };
183
+ }
184
+
185
+ @CachePut({ key: 'order:{0}', ttl: 5000 })
186
+ public async updateStatus(id: string, status: string): Promise<{ id: string; status: string }> {
187
+ return { id, status };
188
+ }
189
+ }
190
+
191
+ // 定义控制器
192
+ @Controller('/api/orders')
193
+ class OrderController {
194
+ public constructor(private readonly orderService: OrderService) {}
195
+
196
+ @GET('/:id')
197
+ public async getOrder(@Param('id') id: string): Promise<{ id: string; status: string }> {
198
+ return await this.orderService.findById(id);
199
+ }
200
+
201
+ @GET('/:id/update/:status')
202
+ public async updateOrder(
203
+ @Param('id') id: string,
204
+ @Param('status') status: string,
205
+ ): Promise<{ id: string; status: string }> {
206
+ return await this.orderService.updateStatus(id, status);
207
+ }
208
+ }
209
+
210
+ // 定义模块
211
+ @Module({
212
+ imports: [CacheModule.forRoot({ defaultTtl: 5000 })],
213
+ controllers: [OrderController],
214
+ providers: [OrderService],
215
+ })
216
+ class AppModule {}
217
+
218
+ // 创建应用
219
+ app = new Application({ port });
220
+ app.registerModule(AppModule);
221
+ await app.listen();
222
+
223
+ // 第一次调用 - 缓存订单
224
+ const response1 = await fetch(`http://localhost:${port}/api/orders/1`);
225
+ expect(response1.status).toBe(200);
226
+ const data1 = await response1.json();
227
+ expect(data1.status).toBe('pending');
228
+ expect(callCount).toBe(1);
229
+
230
+ // 更新订单状态 - @CachePut 会更新缓存
231
+ const response2 = await fetch(`http://localhost:${port}/api/orders/1/update/completed`);
232
+ const data2 = await response2.json();
233
+ expect(data2.status).toBe('completed');
234
+
235
+ // 再次获取 - 应该返回更新后的缓存
236
+ const response3 = await fetch(`http://localhost:${port}/api/orders/1`);
237
+ const data3 = await response3.json();
238
+ expect(data3.status).toBe('completed');
239
+ expect(callCount).toBe(1); // findById 没有被再次调用
240
+ });
241
+
242
+ test('Service without @EnableCacheProxy should not use cache', async () => {
243
+ // 定义服务(没有 @EnableCacheProxy)
244
+ @Injectable()
245
+ class NoCacheService {
246
+ @Cacheable({ key: 'nocache:{0}', ttl: 5000 })
247
+ public async findById(id: string): Promise<{ id: string }> {
248
+ callCount++;
249
+ return { id };
250
+ }
251
+ }
252
+
253
+ // 定义控制器
254
+ @Controller('/api/nocache')
255
+ class NoCacheController {
256
+ public constructor(private readonly noCacheService: NoCacheService) {}
257
+
258
+ @GET('/:id')
259
+ public async get(@Param('id') id: string): Promise<{ id: string }> {
260
+ return await this.noCacheService.findById(id);
261
+ }
262
+ }
263
+
264
+ // 定义模块
265
+ @Module({
266
+ imports: [CacheModule.forRoot({ defaultTtl: 5000 })],
267
+ controllers: [NoCacheController],
268
+ providers: [NoCacheService],
269
+ })
270
+ class AppModule {}
271
+
272
+ // 创建应用
273
+ app = new Application({ port });
274
+ app.registerModule(AppModule);
275
+ await app.listen();
276
+
277
+ // 两次调用都应该执行方法(没有缓存)
278
+ await fetch(`http://localhost:${port}/api/nocache/1`);
279
+ expect(callCount).toBe(1);
280
+
281
+ await fetch(`http://localhost:${port}/api/nocache/1`);
282
+ expect(callCount).toBe(2); // 方法被再次调用
283
+ });
284
+ });