@dangao/bun-server 1.0.0 → 1.0.3

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 (200) hide show
  1. package/package.json +4 -2
  2. package/readme.md +163 -2
  3. package/src/auth/controller.ts +148 -0
  4. package/src/auth/decorators.ts +81 -0
  5. package/src/auth/index.ts +12 -0
  6. package/src/auth/jwt.ts +169 -0
  7. package/src/auth/oauth2.ts +244 -0
  8. package/src/auth/types.ts +248 -0
  9. package/src/cache/cache-module.ts +67 -0
  10. package/src/cache/decorators.ts +202 -0
  11. package/src/cache/index.ts +27 -0
  12. package/src/cache/service.ts +151 -0
  13. package/src/cache/types.ts +420 -0
  14. package/src/config/config-module.ts +76 -0
  15. package/src/config/index.ts +8 -0
  16. package/src/config/service.ts +93 -0
  17. package/src/config/types.ts +27 -0
  18. package/src/controller/controller.ts +251 -0
  19. package/src/controller/decorators.ts +84 -0
  20. package/src/controller/index.ts +7 -0
  21. package/src/controller/metadata.ts +27 -0
  22. package/src/controller/param-binder.ts +157 -0
  23. package/src/core/application.ts +233 -0
  24. package/src/core/context.ts +228 -0
  25. package/src/core/index.ts +4 -0
  26. package/src/core/server.ts +128 -0
  27. package/src/core/types.ts +2 -0
  28. package/src/database/connection-manager.ts +239 -0
  29. package/src/database/connection-pool.ts +322 -0
  30. package/src/database/database-extension.ts +62 -0
  31. package/src/database/database-module.ts +115 -0
  32. package/src/database/health-indicator.ts +51 -0
  33. package/src/database/index.ts +47 -0
  34. package/src/database/orm/decorators.ts +155 -0
  35. package/src/database/orm/drizzle-repository.ts +39 -0
  36. package/src/database/orm/index.ts +23 -0
  37. package/src/database/orm/repository-decorator.ts +39 -0
  38. package/src/database/orm/repository.ts +103 -0
  39. package/src/database/orm/service.ts +49 -0
  40. package/src/database/orm/transaction-decorator.ts +45 -0
  41. package/src/database/orm/transaction-interceptor.ts +243 -0
  42. package/src/database/orm/transaction-manager.ts +276 -0
  43. package/src/database/orm/transaction-types.ts +140 -0
  44. package/src/database/orm/types.ts +99 -0
  45. package/src/database/service.ts +221 -0
  46. package/src/database/types.ts +171 -0
  47. package/src/di/container.ts +398 -0
  48. package/src/di/decorators.ts +228 -0
  49. package/src/di/index.ts +4 -0
  50. package/src/di/module-registry.ts +188 -0
  51. package/src/di/module.ts +65 -0
  52. package/src/di/types.ts +67 -0
  53. package/src/error/error-codes.ts +222 -0
  54. package/src/error/filter.ts +43 -0
  55. package/src/error/handler.ts +66 -0
  56. package/src/error/http-exception.ts +115 -0
  57. package/src/error/i18n.ts +217 -0
  58. package/src/error/index.ts +16 -0
  59. package/src/extensions/index.ts +5 -0
  60. package/src/extensions/logger-extension.ts +31 -0
  61. package/src/extensions/logger-module.ts +69 -0
  62. package/src/extensions/types.ts +14 -0
  63. package/src/files/index.ts +5 -0
  64. package/src/files/static-middleware.ts +53 -0
  65. package/src/files/storage.ts +67 -0
  66. package/src/files/types.ts +33 -0
  67. package/src/files/upload-middleware.ts +45 -0
  68. package/src/health/controller.ts +76 -0
  69. package/src/health/health-module.ts +51 -0
  70. package/src/health/index.ts +12 -0
  71. package/src/health/types.ts +28 -0
  72. package/src/index.ts +270 -0
  73. package/src/metrics/collector.ts +209 -0
  74. package/src/metrics/controller.ts +40 -0
  75. package/src/metrics/index.ts +15 -0
  76. package/src/metrics/metrics-module.ts +58 -0
  77. package/src/metrics/middleware.ts +46 -0
  78. package/src/metrics/prometheus.ts +79 -0
  79. package/src/metrics/types.ts +103 -0
  80. package/src/middleware/builtin/cors.ts +60 -0
  81. package/src/middleware/builtin/error-handler.ts +90 -0
  82. package/src/middleware/builtin/file-upload.ts +42 -0
  83. package/src/middleware/builtin/index.ts +14 -0
  84. package/src/middleware/builtin/logger.ts +91 -0
  85. package/src/middleware/builtin/rate-limit.ts +252 -0
  86. package/src/middleware/builtin/static-file.ts +88 -0
  87. package/src/middleware/decorators.ts +91 -0
  88. package/src/middleware/index.ts +11 -0
  89. package/src/middleware/middleware.ts +13 -0
  90. package/src/middleware/pipeline.ts +93 -0
  91. package/src/queue/decorators.ts +110 -0
  92. package/src/queue/index.ts +26 -0
  93. package/src/queue/queue-module.ts +64 -0
  94. package/src/queue/service.ts +302 -0
  95. package/src/queue/types.ts +341 -0
  96. package/src/request/body-parser.ts +133 -0
  97. package/src/request/file-handler.ts +46 -0
  98. package/src/request/index.ts +5 -0
  99. package/src/request/request.ts +107 -0
  100. package/src/request/response.ts +150 -0
  101. package/src/router/decorators.ts +122 -0
  102. package/src/router/index.ts +6 -0
  103. package/src/router/registry.ts +98 -0
  104. package/src/router/route.ts +140 -0
  105. package/src/router/router.ts +241 -0
  106. package/src/router/types.ts +27 -0
  107. package/src/security/access-decision-manager.ts +34 -0
  108. package/src/security/authentication-manager.ts +47 -0
  109. package/src/security/context.ts +92 -0
  110. package/src/security/filter.ts +162 -0
  111. package/src/security/index.ts +8 -0
  112. package/src/security/providers/index.ts +3 -0
  113. package/src/security/providers/jwt-provider.ts +60 -0
  114. package/src/security/providers/oauth2-provider.ts +70 -0
  115. package/src/security/security-module.ts +145 -0
  116. package/src/security/types.ts +165 -0
  117. package/src/session/decorators.ts +45 -0
  118. package/src/session/index.ts +19 -0
  119. package/src/session/middleware.ts +143 -0
  120. package/src/session/service.ts +218 -0
  121. package/src/session/session-module.ts +69 -0
  122. package/src/session/types.ts +373 -0
  123. package/src/swagger/decorators.ts +133 -0
  124. package/src/swagger/generator.ts +234 -0
  125. package/src/swagger/index.ts +7 -0
  126. package/src/swagger/swagger-extension.ts +41 -0
  127. package/src/swagger/swagger-module.ts +83 -0
  128. package/src/swagger/types.ts +188 -0
  129. package/src/swagger/ui.ts +98 -0
  130. package/src/testing/harness.ts +96 -0
  131. package/src/validation/decorators.ts +95 -0
  132. package/src/validation/errors.ts +28 -0
  133. package/src/validation/index.ts +14 -0
  134. package/src/validation/types.ts +35 -0
  135. package/src/validation/validator.ts +63 -0
  136. package/src/websocket/decorators.ts +51 -0
  137. package/src/websocket/index.ts +12 -0
  138. package/src/websocket/registry.ts +133 -0
  139. package/tests/cache/cache-module.test.ts +212 -0
  140. package/tests/config/config-module.test.ts +151 -0
  141. package/tests/controller/controller.test.ts +189 -0
  142. package/tests/core/application.test.ts +57 -0
  143. package/tests/core/context-body.test.ts +44 -0
  144. package/tests/core/context.test.ts +86 -0
  145. package/tests/core/edge-cases.test.ts +432 -0
  146. package/tests/database/database-module.test.ts +385 -0
  147. package/tests/database/orm.test.ts +164 -0
  148. package/tests/database/postgres-mysql-integration.test.ts +395 -0
  149. package/tests/database/transaction.test.ts +238 -0
  150. package/tests/di/container.test.ts +264 -0
  151. package/tests/di/module.test.ts +128 -0
  152. package/tests/error/error-codes.test.ts +121 -0
  153. package/tests/error/error-handler.test.ts +68 -0
  154. package/tests/error/error-handling.test.ts +254 -0
  155. package/tests/error/http-exception.test.ts +37 -0
  156. package/tests/error/i18n-integration.test.ts +175 -0
  157. package/tests/extensions/logger-extension.test.ts +40 -0
  158. package/tests/files/static-middleware.test.ts +67 -0
  159. package/tests/files/upload-middleware.test.ts +43 -0
  160. package/tests/health/health-module.test.ts +116 -0
  161. package/tests/integration/application-router.test.ts +85 -0
  162. package/tests/integration/body-parsing.test.ts +88 -0
  163. package/tests/integration/cache-e2e.test.ts +114 -0
  164. package/tests/integration/oauth2-e2e.test.ts +615 -0
  165. package/tests/integration/session-e2e.test.ts +207 -0
  166. package/tests/metrics/metrics-module.test.ts +178 -0
  167. package/tests/middleware/builtin.test.ts +206 -0
  168. package/tests/middleware/file-upload.test.ts +41 -0
  169. package/tests/middleware/middleware.test.ts +120 -0
  170. package/tests/middleware/pipeline.test.ts +72 -0
  171. package/tests/middleware/rate-limit.test.ts +314 -0
  172. package/tests/middleware/static-file.test.ts +62 -0
  173. package/tests/perf/harness.test.ts +48 -0
  174. package/tests/perf/optimization.test.ts +183 -0
  175. package/tests/perf/regression.test.ts +120 -0
  176. package/tests/queue/queue-module.test.ts +217 -0
  177. package/tests/request/body-parser.test.ts +96 -0
  178. package/tests/request/response.test.ts +99 -0
  179. package/tests/router/decorators.test.ts +48 -0
  180. package/tests/router/registry.test.ts +51 -0
  181. package/tests/router/route.test.ts +71 -0
  182. package/tests/router/router-normalization.test.ts +106 -0
  183. package/tests/router/router.test.ts +133 -0
  184. package/tests/security/access-decision-manager.test.ts +84 -0
  185. package/tests/security/authentication-manager.test.ts +81 -0
  186. package/tests/security/context.test.ts +302 -0
  187. package/tests/security/filter.test.ts +225 -0
  188. package/tests/security/jwt-provider.test.ts +106 -0
  189. package/tests/security/oauth2-provider.test.ts +269 -0
  190. package/tests/security/security-module.test.ts +143 -0
  191. package/tests/session/session-module.test.ts +307 -0
  192. package/tests/stress/di-stress.test.ts +30 -0
  193. package/tests/swagger/decorators.test.ts +153 -0
  194. package/tests/swagger/generator.test.ts +202 -0
  195. package/tests/swagger/swagger-extension.test.ts +72 -0
  196. package/tests/swagger/swagger-module.test.ts +79 -0
  197. package/tests/utils/test-port.ts +10 -0
  198. package/tests/validation/controller-validation.test.ts +64 -0
  199. package/tests/validation/validation.test.ts +42 -0
  200. package/tests/websocket/gateway.test.ts +68 -0
@@ -0,0 +1,72 @@
1
+ import { describe, expect, test } from 'bun:test';
2
+ import { SwaggerExtension } from '../../src/swagger/swagger-extension';
3
+ import { Container } from '../../src/di/container';
4
+
5
+ describe('SwaggerExtension', () => {
6
+ test('should create extension with options', () => {
7
+ const extension = new SwaggerExtension({
8
+ info: {
9
+ title: 'Test API',
10
+ version: '1.0.0',
11
+ },
12
+ });
13
+
14
+ expect(extension).toBeDefined();
15
+ });
16
+
17
+ test('should register extension', () => {
18
+ const container = new Container();
19
+ const extension = new SwaggerExtension({
20
+ info: {
21
+ title: 'Test API',
22
+ version: '1.0.0',
23
+ },
24
+ });
25
+
26
+ expect(() => extension.register(container)).not.toThrow();
27
+ });
28
+
29
+ test('should get generator', () => {
30
+ const extension = new SwaggerExtension({
31
+ info: {
32
+ title: 'Test API',
33
+ version: '1.0.0',
34
+ },
35
+ });
36
+
37
+ const generator = extension.getGenerator();
38
+ expect(generator).toBeDefined();
39
+ });
40
+
41
+ test('should generate JSON', () => {
42
+ const extension = new SwaggerExtension({
43
+ info: {
44
+ title: 'Test API',
45
+ version: '1.0.0',
46
+ },
47
+ });
48
+
49
+ const json = extension.generateJSON();
50
+ expect(json).toBeDefined();
51
+ expect(() => JSON.parse(json)).not.toThrow();
52
+
53
+ const document = JSON.parse(json);
54
+ expect(document.openapi).toBe('3.0.0');
55
+ expect(document.info.title).toBe('Test API');
56
+ });
57
+
58
+ test('should create generator on first access', () => {
59
+ const extension = new SwaggerExtension({
60
+ info: {
61
+ title: 'Test API',
62
+ version: '1.0.0',
63
+ },
64
+ });
65
+
66
+ // 访问两次应该返回同一个实例
67
+ const generator1 = extension.getGenerator();
68
+ const generator2 = extension.getGenerator();
69
+ expect(generator1).toBe(generator2);
70
+ });
71
+ });
72
+
@@ -0,0 +1,79 @@
1
+ import { describe, expect, test, beforeEach } from 'bun:test';
2
+ import { SwaggerModule } from '../../src/swagger/swagger-module';
3
+ import { MODULE_METADATA_KEY } from '../../src/di/module';
4
+ import 'reflect-metadata';
5
+
6
+ describe('SwaggerModule', () => {
7
+ beforeEach(() => {
8
+ // 清除模块元数据
9
+ Reflect.deleteMetadata(MODULE_METADATA_KEY, SwaggerModule);
10
+ });
11
+
12
+ test('should create module with forRoot', () => {
13
+ const module = SwaggerModule.forRoot({
14
+ info: {
15
+ title: 'Test API',
16
+ version: '1.0.0',
17
+ },
18
+ });
19
+
20
+ expect(module).toBe(SwaggerModule);
21
+ });
22
+
23
+ test('should register extensions and middlewares', () => {
24
+ SwaggerModule.forRoot({
25
+ info: {
26
+ title: 'Test API',
27
+ version: '1.0.0',
28
+ },
29
+ enableUI: true,
30
+ });
31
+
32
+ const metadata = Reflect.getMetadata(MODULE_METADATA_KEY, SwaggerModule);
33
+ expect(metadata).toBeDefined();
34
+ expect(metadata.extensions).toBeDefined();
35
+ expect(metadata.extensions.length).toBeGreaterThan(0);
36
+ expect(metadata.middlewares).toBeDefined();
37
+ expect(metadata.middlewares.length).toBeGreaterThan(0);
38
+ });
39
+
40
+ test('should not register UI middleware when disabled', () => {
41
+ SwaggerModule.forRoot({
42
+ info: {
43
+ title: 'Test API',
44
+ version: '1.0.0',
45
+ },
46
+ enableUI: false,
47
+ });
48
+
49
+ const metadata = Reflect.getMetadata(MODULE_METADATA_KEY, SwaggerModule);
50
+ expect(metadata.middlewares.length).toBe(0);
51
+ });
52
+
53
+ test('should use custom UI path', () => {
54
+ SwaggerModule.forRoot({
55
+ info: {
56
+ title: 'Test API',
57
+ version: '1.0.0',
58
+ },
59
+ uiPath: '/api-docs',
60
+ jsonPath: '/api-docs.json',
61
+ });
62
+
63
+ const metadata = Reflect.getMetadata(MODULE_METADATA_KEY, SwaggerModule);
64
+ expect(metadata).toBeDefined();
65
+ });
66
+
67
+ test('should use default paths when not specified', () => {
68
+ SwaggerModule.forRoot({
69
+ info: {
70
+ title: 'Test API',
71
+ version: '1.0.0',
72
+ },
73
+ });
74
+
75
+ const metadata = Reflect.getMetadata(MODULE_METADATA_KEY, SwaggerModule);
76
+ expect(metadata).toBeDefined();
77
+ });
78
+ });
79
+
@@ -0,0 +1,10 @@
1
+ /**
2
+ * 获取测试专用端口
3
+ * 使用随机高位端口,降低并行测试的端口冲突概率
4
+ * @returns 不同测试之间不冲突的端口号
5
+ */
6
+ export function getTestPort(): number {
7
+ // 选择 30000-59999 之间的随机端口,避免常用端口冲突
8
+ return 30000 + Math.floor(Math.random() * 30000);
9
+ }
10
+
@@ -0,0 +1,64 @@
1
+ import 'reflect-metadata';
2
+ import { afterEach, beforeEach, describe, expect, test } from 'bun:test';
3
+
4
+ import { Controller, ControllerRegistry } from '../../src/controller/controller';
5
+ import { GET } from '../../src/router/decorators';
6
+ import { Query } from '../../src/controller/decorators';
7
+ import { Validate, IsEmail, IsString, MinLength } from '../../src/validation';
8
+ import { RouteRegistry } from '../../src/router/registry';
9
+ import { Context } from '../../src/core/context';
10
+
11
+ describe('Controller Validation Integration', () => {
12
+ beforeEach(() => {
13
+ RouteRegistry.getInstance().clear();
14
+ ControllerRegistry.getInstance().clear();
15
+ });
16
+
17
+ afterEach(() => {
18
+ RouteRegistry.getInstance().clear();
19
+ ControllerRegistry.getInstance().clear();
20
+ });
21
+
22
+ test('should accept valid input', async () => {
23
+ @Controller('/api/validation')
24
+ class ValidationController {
25
+ @GET('/email')
26
+ public getUser(@Query('email') @Validate(IsEmail()) email: string) {
27
+ return { email };
28
+ }
29
+ }
30
+
31
+ ControllerRegistry.getInstance().register(ValidationController);
32
+
33
+ const router = RouteRegistry.getInstance().getRouter();
34
+ const context = new Context(
35
+ new Request('http://localhost/api/validation/email?email=test@example.com'),
36
+ );
37
+ const response = await router.handle(context);
38
+ expect(response?.status).toBe(200);
39
+ const data = await response?.json();
40
+ expect(data).toEqual({ email: 'test@example.com' });
41
+ });
42
+
43
+ test('should reject invalid input with 400', async () => {
44
+ @Controller('/api/validation')
45
+ class ValidationController {
46
+ @GET('/name')
47
+ public getUser(@Query('name') @Validate(IsString(), MinLength(1)) name: string) {
48
+ return { name };
49
+ }
50
+ }
51
+
52
+ ControllerRegistry.getInstance().register(ValidationController);
53
+
54
+ const router = RouteRegistry.getInstance().getRouter();
55
+ const context = new Context(new Request('http://localhost/api/validation/name?name='));
56
+ const response = await router.handle(context);
57
+ expect(response?.status).toBe(400);
58
+ const data = await response?.json();
59
+ expect(data.error).toBe('Validation failed');
60
+ expect(data.issues.length).toBeGreaterThan(0);
61
+ });
62
+ });
63
+
64
+
@@ -0,0 +1,42 @@
1
+ import { describe, expect, test } from 'bun:test';
2
+
3
+ import {
4
+ Validate,
5
+ IsString,
6
+ IsEmail,
7
+ IsOptional,
8
+ getValidationMetadata,
9
+ validateParameters,
10
+ ValidationError,
11
+ } from '../../src/validation';
12
+
13
+ class TestClass {
14
+ public method(@Validate(IsString()) value: unknown): void {
15
+ void value;
16
+ }
17
+ }
18
+
19
+ describe('Validation Decorators', () => {
20
+ test('should store validation metadata', () => {
21
+ const metadata = getValidationMetadata(TestClass.prototype, 'method');
22
+ expect(metadata.length).toBe(1);
23
+ expect(metadata[0].index).toBe(0);
24
+ expect(metadata[0].rules[0].name).toBe('isString');
25
+ });
26
+
27
+ test('validateParameters should throw ValidationError on failure', () => {
28
+ const metadata = [
29
+ { index: 0, rules: [IsEmail()] },
30
+ { index: 1, rules: [IsOptional(), IsString()] },
31
+ ];
32
+
33
+ expect(() => validateParameters(['not-email', undefined], metadata)).toThrow(ValidationError);
34
+ });
35
+
36
+ test('validateParameters should allow optional values', () => {
37
+ const metadata = [{ index: 0, rules: [IsOptional(), IsString()] }];
38
+ expect(() => validateParameters([undefined], metadata)).not.toThrow();
39
+ });
40
+ });
41
+
42
+
@@ -0,0 +1,68 @@
1
+ import { describe, expect, test, beforeEach, afterEach } from 'bun:test';
2
+ import type { ServerWebSocket } from 'bun';
3
+
4
+ import { WebSocketGateway, OnOpen, OnMessage, OnClose } from '../../src/websocket/decorators';
5
+ import { WebSocketGatewayRegistry } from '../../src/websocket/registry';
6
+ import { ControllerRegistry } from '../../src/controller/controller';
7
+ import type { WebSocketConnectionData } from '../../src/websocket/registry';
8
+
9
+ function createFakeSocket(path: string): ServerWebSocket<WebSocketConnectionData> {
10
+ return {
11
+ data: { path },
12
+ send() {},
13
+ close() {},
14
+ } as unknown as ServerWebSocket<WebSocketConnectionData>;
15
+ }
16
+
17
+ describe('WebSocketGatewayRegistry', () => {
18
+ const registry = WebSocketGatewayRegistry.getInstance();
19
+
20
+ beforeEach(() => {
21
+ registry.clear();
22
+ ControllerRegistry.getInstance().clear();
23
+ TestGateway.events = [];
24
+ });
25
+
26
+ afterEach(() => {
27
+ registry.clear();
28
+ ControllerRegistry.getInstance().clear();
29
+ TestGateway.events = [];
30
+ });
31
+
32
+ @WebSocketGateway('/ws/test')
33
+ class TestGateway {
34
+ public static events: string[] = [];
35
+
36
+ @OnOpen
37
+ public handleOpen(): void {
38
+ TestGateway.events.push('open');
39
+ }
40
+
41
+ @OnMessage
42
+ public handleMessage(
43
+ _ws: ServerWebSocket<WebSocketConnectionData>,
44
+ message: unknown,
45
+ ): void {
46
+ TestGateway.events.push(`message:${message}`);
47
+ }
48
+
49
+ @OnClose
50
+ public handleClose(_ws: ServerWebSocket<WebSocketConnectionData>): void {
51
+ TestGateway.events.push('close');
52
+ }
53
+ }
54
+
55
+ test('should register gateway and handle events', () => {
56
+ registry.register(TestGateway);
57
+
58
+ const ws = createFakeSocket('/ws/test');
59
+ registry.handleOpen(ws);
60
+ registry.handleMessage(ws, 'hello');
61
+ registry.handleClose(ws, 1000, 'normal');
62
+
63
+ expect(registry.hasGateway('/ws/test')).toBe(true);
64
+ expect(TestGateway.events).toEqual(['open', 'message:hello', 'close']);
65
+ });
66
+ });
67
+
68
+