@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,133 @@
1
+ /**
2
+ * 请求体解析器
3
+ * 支持 JSON、FormData、URLEncoded 等格式
4
+ */
5
+ export class BodyParser {
6
+ /**
7
+ * 解析请求体
8
+ * @param request - 请求对象
9
+ * @returns 解析后的请求体
10
+ */
11
+ public static async parse(request: Request): Promise<unknown> {
12
+ const contentType = request.headers.get('content-type') ?? '';
13
+
14
+ // 检查是否有请求体(GET 和 HEAD 请求通常没有 body)
15
+ if (request.method === 'GET' || request.method === 'HEAD') {
16
+ return undefined;
17
+ }
18
+
19
+ // 检查 Content-Length
20
+ const contentLength = request.headers.get('content-length');
21
+ if (contentLength === '0') {
22
+ return undefined;
23
+ }
24
+
25
+ // 尝试解析请求体
26
+ try {
27
+ // JSON 格式
28
+ if (contentType.includes('application/json')) {
29
+ const result = await this.parseJSON(request);
30
+ return result;
31
+ }
32
+
33
+ // FormData 格式
34
+ if (contentType.includes('multipart/form-data')) {
35
+ return await this.parseFormData(request);
36
+ }
37
+
38
+ // URLEncoded 格式
39
+ if (contentType.includes('application/x-www-form-urlencoded')) {
40
+ const result = await this.parseURLEncoded(request);
41
+ return result;
42
+ }
43
+
44
+ // 文本格式
45
+ if (contentType.includes('text/')) {
46
+ return await this.parseText(request);
47
+ }
48
+
49
+ // 如果没有 Content-Type 或 Content-Length,尝试读取 body
50
+ // 如果读取成功,尝试解析为 JSON,否则返回文本
51
+ if (!contentType && !contentLength) {
52
+ try {
53
+ const text = await request.text();
54
+ if (!text || text.length === 0) {
55
+ return undefined;
56
+ }
57
+ // 尝试解析为 JSON
58
+ try {
59
+ return JSON.parse(text);
60
+ } catch {
61
+ return text;
62
+ }
63
+ } catch {
64
+ return undefined;
65
+ }
66
+ }
67
+
68
+ // 默认尝试 JSON,失败后返回原始文本
69
+ const fallbackText = await request.text();
70
+ if (!fallbackText) {
71
+ return undefined;
72
+ }
73
+ try {
74
+ return JSON.parse(fallbackText);
75
+ } catch {
76
+ return fallbackText;
77
+ }
78
+ } catch (error) {
79
+ // 如果读取失败,返回 undefined
80
+ return undefined;
81
+ }
82
+ }
83
+
84
+ /**
85
+ * 解析 JSON 格式
86
+ * @param request - 请求对象
87
+ * @returns 解析后的对象
88
+ */
89
+ private static async parseJSON(request: Request): Promise<unknown> {
90
+ const text = await request.text();
91
+ if (!text) {
92
+ return undefined;
93
+ }
94
+ return JSON.parse(text);
95
+ }
96
+
97
+ /**
98
+ * 解析 FormData 格式
99
+ * @param request - 请求对象
100
+ * @returns 解析后的 FormData 对象
101
+ */
102
+ private static async parseFormData(request: Request): Promise<FormData> {
103
+ return await request.formData() as FormData;
104
+ }
105
+
106
+ /**
107
+ * 解析 URLEncoded 格式
108
+ * @param request - 请求对象
109
+ * @returns 解析后的对象
110
+ */
111
+ private static async parseURLEncoded(request: Request): Promise<Record<string, string> | undefined> {
112
+ const text = await request.text();
113
+ if (!text || text.length === 0) {
114
+ return undefined;
115
+ }
116
+
117
+ const params = new URLSearchParams(text);
118
+ const result: Record<string, string> = {};
119
+ params.forEach((value, key) => {
120
+ result[key] = value;
121
+ });
122
+ return result;
123
+ }
124
+
125
+ /**
126
+ * 解析文本格式
127
+ * @param request - 请求对象
128
+ * @returns 文本内容
129
+ */
130
+ private static async parseText(request: Request): Promise<string> {
131
+ return await request.text();
132
+ }
133
+ }
@@ -0,0 +1,46 @@
1
+ import type { Context } from '../core/context';
2
+
3
+ export interface UploadedFile {
4
+ name: string;
5
+ type: string;
6
+ size: number;
7
+ data: ArrayBuffer;
8
+ }
9
+
10
+ export class FileHandler {
11
+ private static readonly MAX_SIZE = 10 * 1024 * 1024; // 10MB
12
+
13
+ public static async parseFormData(context: Context): Promise<FormData> {
14
+ const contentType = context.getHeader('Content-Type');
15
+ if (!contentType || !contentType.includes('multipart/form-data')) {
16
+ throw new Error('Content-Type must be multipart/form-data');
17
+ }
18
+ const request = context.request;
19
+ return await request.formData() as FormData;
20
+ }
21
+
22
+ public static async getFiles(formData: FormData): Promise<Record<string, UploadedFile[]>> {
23
+ const files: Record<string, UploadedFile[]> = {};
24
+ for (const [key, value] of formData.entries()) {
25
+ // @ts-ignore
26
+ if (value instanceof File) {
27
+ const buffer = await value.arrayBuffer();
28
+ if (buffer.byteLength > this.MAX_SIZE) {
29
+ throw new Error(`File ${value.name} exceeds maximum size`);
30
+ }
31
+ if (!files[key]) {
32
+ files[key] = [];
33
+ }
34
+ files[key].push({
35
+ name: value.name,
36
+ type: value.type,
37
+ size: value.size,
38
+ data: buffer,
39
+ });
40
+ }
41
+ }
42
+ return files;
43
+ }
44
+ }
45
+
46
+
@@ -0,0 +1,5 @@
1
+ export { BodyParser } from './body-parser';
2
+ export { RequestWrapper } from './request';
3
+ export { ResponseBuilder } from './response';
4
+ export { FileHandler } from './file-handler';
5
+
@@ -0,0 +1,107 @@
1
+ import { BodyParser } from './body-parser';
2
+
3
+ /**
4
+ * 请求封装类
5
+ * 扩展原生 Request,提供便捷方法
6
+ */
7
+ export class RequestWrapper {
8
+ /**
9
+ * 原始请求对象
10
+ */
11
+ public readonly request: Request;
12
+
13
+ /**
14
+ * 请求 URL
15
+ */
16
+ public readonly url: URL;
17
+
18
+ /**
19
+ * 请求方法
20
+ */
21
+ public readonly method: string;
22
+
23
+ /**
24
+ * 请求路径
25
+ */
26
+ public readonly path: string;
27
+
28
+ /**
29
+ * 查询参数
30
+ */
31
+ public readonly query: URLSearchParams;
32
+
33
+ /**
34
+ * 请求头
35
+ */
36
+ public readonly headers: Headers;
37
+
38
+ /**
39
+ * 请求体(解析后的)
40
+ */
41
+ private _body?: unknown;
42
+
43
+ /**
44
+ * 是否已解析请求体
45
+ */
46
+ private _bodyParsed: boolean = false;
47
+
48
+ public constructor(request: Request) {
49
+ this.request = request;
50
+ this.url = new URL(request.url);
51
+ this.method = request.method;
52
+ this.path = this.url.pathname;
53
+ this.query = this.url.searchParams;
54
+ this.headers = request.headers;
55
+ }
56
+
57
+ /**
58
+ * 获取请求体(自动解析)
59
+ * @returns 解析后的请求体
60
+ */
61
+ public async body(): Promise<unknown> {
62
+ if (!this._bodyParsed) {
63
+ this._body = await BodyParser.parse(this.request);
64
+ this._bodyParsed = true;
65
+ }
66
+ return this._body;
67
+ }
68
+
69
+ /**
70
+ * 获取请求体(已解析的,如果未解析则返回 undefined)
71
+ * @returns 请求体或 undefined
72
+ */
73
+ public getBody(): unknown {
74
+ return this._body;
75
+ }
76
+
77
+ /**
78
+ * 获取查询参数
79
+ * @param key - 参数名
80
+ * @returns 参数值
81
+ */
82
+ public getQuery(key: string): string | null {
83
+ return this.query.get(key);
84
+ }
85
+
86
+ /**
87
+ * 获取所有查询参数
88
+ * @returns 查询参数对象
89
+ */
90
+ public getQueryAll(): Record<string, string> {
91
+ const result: Record<string, string> = {};
92
+ this.query.forEach((value, key) => {
93
+ result[key] = value;
94
+ });
95
+ return result;
96
+ }
97
+
98
+ /**
99
+ * 获取请求头
100
+ * @param key - 头名称
101
+ * @returns 头值
102
+ */
103
+ public getHeader(key: string): string | null {
104
+ return this.headers.get(key);
105
+ }
106
+ }
107
+
@@ -0,0 +1,150 @@
1
+ import type { BodyInit, HeadersInit } from 'bun';
2
+
3
+ /**
4
+ * 响应封装类
5
+ * 提供便捷的响应创建方法
6
+ */
7
+ export class ResponseBuilder {
8
+ /**
9
+ * 创建 JSON 响应
10
+ * @param data - 响应数据
11
+ * @param status - HTTP 状态码
12
+ * @param headers - 响应头
13
+ * @returns Response 对象
14
+ */
15
+ public static json(
16
+ data: unknown,
17
+ status: number = 200,
18
+ headers?: HeadersInit,
19
+ ): Response {
20
+ const responseHeaders = new Headers(headers);
21
+ responseHeaders.set('Content-Type', 'application/json');
22
+
23
+ return new Response(JSON.stringify(data), {
24
+ status,
25
+ headers: responseHeaders,
26
+ });
27
+ }
28
+
29
+ /**
30
+ * 创建文本响应
31
+ * @param text - 响应文本
32
+ * @param status - HTTP 状态码
33
+ * @param headers - 响应头
34
+ * @returns Response 对象
35
+ */
36
+ public static text(
37
+ text: string,
38
+ status: number = 200,
39
+ headers?: HeadersInit,
40
+ ): Response {
41
+ const responseHeaders = new Headers(headers);
42
+ responseHeaders.set('Content-Type', 'text/plain');
43
+
44
+ return new Response(text, {
45
+ status,
46
+ headers: responseHeaders,
47
+ });
48
+ }
49
+
50
+ /**
51
+ * 创建 HTML 响应
52
+ * @param html - HTML 内容
53
+ * @param status - HTTP 状态码
54
+ * @param headers - 响应头
55
+ * @returns Response 对象
56
+ */
57
+ public static html(
58
+ html: string,
59
+ status: number = 200,
60
+ headers?: HeadersInit,
61
+ ): Response {
62
+ const responseHeaders = new Headers(headers);
63
+ responseHeaders.set('Content-Type', 'text/html');
64
+
65
+ return new Response(html, {
66
+ status,
67
+ headers: responseHeaders,
68
+ });
69
+ }
70
+
71
+ /**
72
+ * 创建空响应
73
+ * @param status - HTTP 状态码
74
+ * @param headers - 响应头
75
+ * @returns Response 对象
76
+ */
77
+ public static empty(status: number = 204, headers?: HeadersInit): Response {
78
+ const responseHeaders = headers ? new Headers(headers) : undefined;
79
+ return new Response(null, {
80
+ status,
81
+ headers: responseHeaders,
82
+ });
83
+ }
84
+
85
+ /**
86
+ * 创建重定向响应
87
+ * @param url - 重定向 URL
88
+ * @param status - HTTP 状态码(默认 302)
89
+ * @returns Response 对象
90
+ */
91
+ public static redirect(url: string, status: number = 302): Response {
92
+ return new Response(null, {
93
+ status,
94
+ headers: {
95
+ Location: url,
96
+ },
97
+ });
98
+ }
99
+
100
+ /**
101
+ * 创建错误响应
102
+ * @param message - 错误消息
103
+ * @param status - HTTP 状态码
104
+ * @returns Response 对象
105
+ */
106
+ public static error(message: string, status: number = 500): Response {
107
+ return this.json({ error: message }, status);
108
+ }
109
+
110
+ /**
111
+ * 创建文件/下载响应
112
+ * @param source - 文件路径或二进制数据
113
+ * @param options - 响应配置
114
+ * @returns Response 对象
115
+ */
116
+ public static file(
117
+ source: string | Blob | ArrayBuffer | ArrayBufferView,
118
+ options: {
119
+ fileName?: string;
120
+ contentType?: string;
121
+ status?: number;
122
+ headers?: HeadersInit;
123
+ } = {},
124
+ ): Response {
125
+ const headers = new Headers(options.headers);
126
+ if (options.fileName) {
127
+ headers.set('Content-Disposition', `attachment; filename="${options.fileName}"`);
128
+ }
129
+ if (options.contentType) {
130
+ headers.set('Content-Type', options.contentType);
131
+ }
132
+
133
+ if (typeof source === 'string') {
134
+ const file = Bun.file(source);
135
+ if (!headers.has('Content-Type') && file.type) {
136
+ headers.set('Content-Type', file.type);
137
+ }
138
+ return new Response(file, {
139
+ status: options.status ?? 200,
140
+ headers,
141
+ });
142
+ }
143
+
144
+ return new Response(source as BodyInit, {
145
+ status: options.status ?? 200,
146
+ headers,
147
+ });
148
+ }
149
+ }
150
+
@@ -0,0 +1,122 @@
1
+ import 'reflect-metadata';
2
+ import type { RouteHandler } from './types';
3
+ import { RouteRegistry } from './registry';
4
+ import type { HttpMethod } from './types';
5
+ import { CONTROLLER_METADATA_KEY } from '../controller/controller';
6
+
7
+ /**
8
+ * 路由元数据键
9
+ */
10
+ export const ROUTE_METADATA_KEY = Symbol('route');
11
+
12
+ /**
13
+ * 路由元数据
14
+ */
15
+ export interface RouteMetadata {
16
+ method: HttpMethod;
17
+ path: string;
18
+ handler: RouteHandler;
19
+ /**
20
+ * 属性键(用于控制器方法)
21
+ */
22
+ propertyKey?: string;
23
+ }
24
+
25
+ /**
26
+ * 路由装饰器工厂
27
+ * @param method - HTTP 方法
28
+ * @param path - 路由路径
29
+ * @returns 方法装饰器
30
+ */
31
+ function createRouteDecorator(method: HttpMethod, path: string) {
32
+ return function (target: any, propertyKey: string | symbol, descriptor: PropertyDescriptor) {
33
+ // 检查是否是控制器方法(有 Controller 装饰器)
34
+ const controllerMetadata = Reflect.getMetadata(CONTROLLER_METADATA_KEY, target.constructor);
35
+
36
+ if (controllerMetadata) {
37
+ // 控制器方法:只保存元数据,不直接注册路由
38
+ // 路由注册由 ControllerRegistry 处理
39
+ const existingRoutes: RouteMetadata[] = Reflect.getMetadata(ROUTE_METADATA_KEY, target) || [];
40
+
41
+ // 获取方法名:优先使用 propertyKey,如果不可用则从函数名获取
42
+ let propertyKeyStr: string;
43
+ if (propertyKey && propertyKey !== '') {
44
+ propertyKeyStr = typeof propertyKey === 'string' ? propertyKey : String(propertyKey);
45
+ } else {
46
+ // 从函数名获取(可能不可靠,但作为后备方案)
47
+ propertyKeyStr = descriptor.value.name || '';
48
+ }
49
+
50
+ // 如果仍然没有方法名,尝试从原型中查找
51
+ if (!propertyKeyStr) {
52
+ const propertyNames = Object.getOwnPropertyNames(target);
53
+ for (const key of propertyNames) {
54
+ const targetDescriptor = Object.getOwnPropertyDescriptor(target, key);
55
+ if (targetDescriptor?.value === descriptor.value) {
56
+ propertyKeyStr = key;
57
+ break;
58
+ }
59
+ }
60
+ }
61
+
62
+ existingRoutes.push({
63
+ method,
64
+ path,
65
+ handler: descriptor.value as RouteHandler,
66
+ propertyKey: propertyKeyStr || undefined,
67
+ });
68
+ Reflect.defineMetadata(ROUTE_METADATA_KEY, existingRoutes, target);
69
+ } else {
70
+ // 普通函数:直接注册路由
71
+ const handler = descriptor.value as RouteHandler;
72
+ const registry = RouteRegistry.getInstance();
73
+ registry.register(method, path, handler);
74
+
75
+ // 保存元数据(用于兼容性)
76
+ const existingRoutes: RouteMetadata[] = Reflect.getMetadata(ROUTE_METADATA_KEY, target) || [];
77
+ existingRoutes.push({ method, path, handler });
78
+ Reflect.defineMetadata(ROUTE_METADATA_KEY, existingRoutes, target);
79
+ }
80
+ };
81
+ }
82
+
83
+ /**
84
+ * GET 路由装饰器
85
+ * @param path - 路由路径
86
+ */
87
+ export function GET(path: string) {
88
+ return createRouteDecorator('GET', path);
89
+ }
90
+
91
+ /**
92
+ * POST 路由装饰器
93
+ * @param path - 路由路径
94
+ */
95
+ export function POST(path: string) {
96
+ return createRouteDecorator('POST', path);
97
+ }
98
+
99
+ /**
100
+ * PUT 路由装饰器
101
+ * @param path - 路由路径
102
+ */
103
+ export function PUT(path: string) {
104
+ return createRouteDecorator('PUT', path);
105
+ }
106
+
107
+ /**
108
+ * DELETE 路由装饰器
109
+ * @param path - 路由路径
110
+ */
111
+ export function DELETE(path: string) {
112
+ return createRouteDecorator('DELETE', path);
113
+ }
114
+
115
+ /**
116
+ * PATCH 路由装饰器
117
+ * @param path - 路由路径
118
+ */
119
+ export function PATCH(path: string) {
120
+ return createRouteDecorator('PATCH', path);
121
+ }
122
+
@@ -0,0 +1,6 @@
1
+ export { Route } from './route';
2
+ export { Router } from './router';
3
+ export { RouteRegistry } from './registry';
4
+ export { GET, POST, PUT, DELETE, PATCH } from './decorators';
5
+ export type { HttpMethod, RouteHandler, RouteMatch } from './types';
6
+
@@ -0,0 +1,98 @@
1
+ import { Router } from './router';
2
+ import type { HttpMethod, RouteHandler } from './types';
3
+ import type { Middleware } from '../middleware';
4
+
5
+ /**
6
+ * 路由注册表
7
+ * 全局路由注册表,管理所有路由
8
+ */
9
+ export class RouteRegistry {
10
+ private static instance: RouteRegistry;
11
+ private readonly router: Router;
12
+
13
+ private constructor() {
14
+ this.router = new Router();
15
+ }
16
+
17
+ /**
18
+ * 获取单例实例
19
+ * @returns 路由注册表实例
20
+ */
21
+ public static getInstance(): RouteRegistry {
22
+ if (!RouteRegistry.instance) {
23
+ RouteRegistry.instance = new RouteRegistry();
24
+ }
25
+ return RouteRegistry.instance;
26
+ }
27
+
28
+ /**
29
+ * 注册路由
30
+ * @param method - HTTP 方法
31
+ * @param path - 路由路径
32
+ * @param handler - 路由处理器
33
+ * @param middlewares - 中间件列表
34
+ * @param controllerClass - 控制器类(可选)
35
+ * @param methodName - 方法名(可选)
36
+ */
37
+ public register(
38
+ method: HttpMethod,
39
+ path: string,
40
+ handler: RouteHandler,
41
+ middlewares: Middleware[] = [],
42
+ controllerClass?: new (...args: unknown[]) => unknown,
43
+ methodName?: string,
44
+ ): void {
45
+ this.router.register(method, path, handler, middlewares, controllerClass, methodName);
46
+ }
47
+
48
+ /**
49
+ * 注册 GET 路由
50
+ */
51
+ public get(path: string, handler: RouteHandler, middlewares: Middleware[] = []): void {
52
+ this.router.get(path, handler, middlewares);
53
+ }
54
+
55
+ /**
56
+ * 注册 POST 路由
57
+ */
58
+ public post(path: string, handler: RouteHandler, middlewares: Middleware[] = []): void {
59
+ this.router.post(path, handler, middlewares);
60
+ }
61
+
62
+ /**
63
+ * 注册 PUT 路由
64
+ */
65
+ public put(path: string, handler: RouteHandler, middlewares: Middleware[] = []): void {
66
+ this.router.put(path, handler, middlewares);
67
+ }
68
+
69
+ /**
70
+ * 注册 DELETE 路由
71
+ */
72
+ public delete(path: string, handler: RouteHandler, middlewares: Middleware[] = []): void {
73
+ this.router.delete(path, handler, middlewares);
74
+ }
75
+
76
+ /**
77
+ * 注册 PATCH 路由
78
+ */
79
+ public patch(path: string, handler: RouteHandler, middlewares: Middleware[] = []): void {
80
+ this.router.patch(path, handler, middlewares);
81
+ }
82
+
83
+ /**
84
+ * 获取路由器实例
85
+ * @returns 路由器实例
86
+ */
87
+ public getRouter(): Router {
88
+ return this.router;
89
+ }
90
+
91
+ /**
92
+ * 清除所有路由(主要用于测试)
93
+ */
94
+ public clear(): void {
95
+ this.router.clear();
96
+ }
97
+ }
98
+