@dangao/bun-server 1.0.1 → 1.1.2

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 (262) hide show
  1. package/dist/controller/controller.d.ts +1 -1
  2. package/dist/controller/controller.d.ts.map +1 -1
  3. package/dist/core/application.d.ts.map +1 -1
  4. package/dist/database/database-extension.d.ts.map +1 -1
  5. package/dist/database/database-module.d.ts.map +1 -1
  6. package/dist/database/orm/transaction-decorator.d.ts +1 -0
  7. package/dist/database/orm/transaction-decorator.d.ts.map +1 -1
  8. package/dist/database/orm/transaction-interceptor.d.ts +12 -3
  9. package/dist/database/orm/transaction-interceptor.d.ts.map +1 -1
  10. package/dist/index.d.ts +1 -0
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +678 -310
  13. package/dist/interceptor/base-interceptor.d.ts +94 -0
  14. package/dist/interceptor/base-interceptor.d.ts.map +1 -0
  15. package/dist/interceptor/builtin/cache-interceptor.d.ts +69 -0
  16. package/dist/interceptor/builtin/cache-interceptor.d.ts.map +1 -0
  17. package/dist/interceptor/builtin/index.d.ts +4 -0
  18. package/dist/interceptor/builtin/index.d.ts.map +1 -0
  19. package/dist/interceptor/builtin/log-interceptor.d.ts +56 -0
  20. package/dist/interceptor/builtin/log-interceptor.d.ts.map +1 -0
  21. package/dist/interceptor/builtin/permission-interceptor.d.ts +70 -0
  22. package/dist/interceptor/builtin/permission-interceptor.d.ts.map +1 -0
  23. package/dist/interceptor/index.d.ts +7 -0
  24. package/dist/interceptor/index.d.ts.map +1 -0
  25. package/dist/interceptor/interceptor-chain.d.ts +22 -0
  26. package/dist/interceptor/interceptor-chain.d.ts.map +1 -0
  27. package/dist/interceptor/interceptor-registry.d.ts +59 -0
  28. package/dist/interceptor/interceptor-registry.d.ts.map +1 -0
  29. package/dist/interceptor/metadata.d.ts +12 -0
  30. package/dist/interceptor/metadata.d.ts.map +1 -0
  31. package/dist/interceptor/types.d.ts +42 -0
  32. package/dist/interceptor/types.d.ts.map +1 -0
  33. package/dist/middleware/decorators.d.ts +2 -1
  34. package/dist/middleware/decorators.d.ts.map +1 -1
  35. package/dist/router/decorators.d.ts.map +1 -1
  36. package/dist/router/registry.d.ts +2 -1
  37. package/dist/router/registry.d.ts.map +1 -1
  38. package/dist/router/route.d.ts +3 -2
  39. package/dist/router/route.d.ts.map +1 -1
  40. package/dist/router/router.d.ts +2 -1
  41. package/dist/router/router.d.ts.map +1 -1
  42. package/dist/websocket/decorators.d.ts +2 -1
  43. package/dist/websocket/decorators.d.ts.map +1 -1
  44. package/package.json +5 -3
  45. package/readme.md +163 -2
  46. package/src/auth/controller.ts +148 -0
  47. package/src/auth/decorators.ts +81 -0
  48. package/src/auth/index.ts +12 -0
  49. package/src/auth/jwt.ts +169 -0
  50. package/src/auth/oauth2.ts +244 -0
  51. package/src/auth/types.ts +248 -0
  52. package/src/cache/cache-module.ts +67 -0
  53. package/src/cache/decorators.ts +202 -0
  54. package/src/cache/index.ts +27 -0
  55. package/src/cache/service.ts +151 -0
  56. package/src/cache/types.ts +420 -0
  57. package/src/config/config-module.ts +76 -0
  58. package/src/config/index.ts +8 -0
  59. package/src/config/service.ts +93 -0
  60. package/src/config/types.ts +27 -0
  61. package/src/controller/controller.ts +278 -0
  62. package/src/controller/decorators.ts +84 -0
  63. package/src/controller/index.ts +7 -0
  64. package/src/controller/metadata.ts +27 -0
  65. package/src/controller/param-binder.ts +157 -0
  66. package/src/core/application.ts +239 -0
  67. package/src/core/context.ts +228 -0
  68. package/src/core/index.ts +4 -0
  69. package/src/core/server.ts +128 -0
  70. package/src/core/types.ts +2 -0
  71. package/src/database/connection-manager.ts +239 -0
  72. package/src/database/connection-pool.ts +322 -0
  73. package/src/database/database-extension.ts +83 -0
  74. package/src/database/database-module.ts +121 -0
  75. package/src/database/health-indicator.ts +51 -0
  76. package/src/database/index.ts +47 -0
  77. package/src/database/orm/decorators.ts +155 -0
  78. package/src/database/orm/drizzle-repository.ts +39 -0
  79. package/src/database/orm/index.ts +23 -0
  80. package/src/database/orm/repository-decorator.ts +39 -0
  81. package/src/database/orm/repository.ts +103 -0
  82. package/src/database/orm/service.ts +49 -0
  83. package/src/database/orm/transaction-decorator.ts +76 -0
  84. package/src/database/orm/transaction-interceptor.ts +263 -0
  85. package/src/database/orm/transaction-manager.ts +276 -0
  86. package/src/database/orm/transaction-types.ts +140 -0
  87. package/src/database/orm/types.ts +99 -0
  88. package/src/database/service.ts +221 -0
  89. package/src/database/types.ts +171 -0
  90. package/src/di/container.ts +398 -0
  91. package/src/di/decorators.ts +228 -0
  92. package/src/di/index.ts +4 -0
  93. package/src/di/module-registry.ts +188 -0
  94. package/src/di/module.ts +65 -0
  95. package/src/di/types.ts +67 -0
  96. package/src/error/error-codes.ts +222 -0
  97. package/src/error/filter.ts +43 -0
  98. package/src/error/handler.ts +66 -0
  99. package/src/error/http-exception.ts +115 -0
  100. package/src/error/i18n.ts +217 -0
  101. package/src/error/index.ts +16 -0
  102. package/src/extensions/index.ts +5 -0
  103. package/src/extensions/logger-extension.ts +31 -0
  104. package/src/extensions/logger-module.ts +69 -0
  105. package/src/extensions/types.ts +14 -0
  106. package/src/files/index.ts +5 -0
  107. package/src/files/static-middleware.ts +53 -0
  108. package/src/files/storage.ts +67 -0
  109. package/src/files/types.ts +33 -0
  110. package/src/files/upload-middleware.ts +45 -0
  111. package/src/health/controller.ts +76 -0
  112. package/src/health/health-module.ts +51 -0
  113. package/src/health/index.ts +12 -0
  114. package/src/health/types.ts +28 -0
  115. package/src/index.ts +292 -0
  116. package/src/interceptor/base-interceptor.ts +203 -0
  117. package/src/interceptor/builtin/cache-interceptor.ts +169 -0
  118. package/src/interceptor/builtin/index.ts +28 -0
  119. package/src/interceptor/builtin/log-interceptor.ts +178 -0
  120. package/src/interceptor/builtin/permission-interceptor.ts +173 -0
  121. package/src/interceptor/index.ts +26 -0
  122. package/src/interceptor/interceptor-chain.ts +79 -0
  123. package/src/interceptor/interceptor-registry.ts +132 -0
  124. package/src/interceptor/metadata.ts +40 -0
  125. package/src/interceptor/types.ts +52 -0
  126. package/src/metrics/collector.ts +209 -0
  127. package/src/metrics/controller.ts +40 -0
  128. package/src/metrics/index.ts +15 -0
  129. package/src/metrics/metrics-module.ts +58 -0
  130. package/src/metrics/middleware.ts +46 -0
  131. package/src/metrics/prometheus.ts +79 -0
  132. package/src/metrics/types.ts +103 -0
  133. package/src/middleware/builtin/cors.ts +60 -0
  134. package/src/middleware/builtin/error-handler.ts +90 -0
  135. package/src/middleware/builtin/file-upload.ts +42 -0
  136. package/src/middleware/builtin/index.ts +14 -0
  137. package/src/middleware/builtin/logger.ts +91 -0
  138. package/src/middleware/builtin/rate-limit.ts +252 -0
  139. package/src/middleware/builtin/static-file.ts +88 -0
  140. package/src/middleware/decorators.ts +92 -0
  141. package/src/middleware/index.ts +11 -0
  142. package/src/middleware/middleware.ts +13 -0
  143. package/src/middleware/pipeline.ts +93 -0
  144. package/src/queue/decorators.ts +110 -0
  145. package/src/queue/index.ts +26 -0
  146. package/src/queue/queue-module.ts +64 -0
  147. package/src/queue/service.ts +302 -0
  148. package/src/queue/types.ts +341 -0
  149. package/src/request/body-parser.ts +133 -0
  150. package/src/request/file-handler.ts +46 -0
  151. package/src/request/index.ts +5 -0
  152. package/src/request/request.ts +107 -0
  153. package/src/request/response.ts +150 -0
  154. package/src/router/decorators.ts +123 -0
  155. package/src/router/index.ts +6 -0
  156. package/src/router/registry.ts +99 -0
  157. package/src/router/route.ts +141 -0
  158. package/src/router/router.ts +242 -0
  159. package/src/router/types.ts +27 -0
  160. package/src/security/access-decision-manager.ts +34 -0
  161. package/src/security/authentication-manager.ts +47 -0
  162. package/src/security/context.ts +92 -0
  163. package/src/security/filter.ts +162 -0
  164. package/src/security/index.ts +8 -0
  165. package/src/security/providers/index.ts +3 -0
  166. package/src/security/providers/jwt-provider.ts +60 -0
  167. package/src/security/providers/oauth2-provider.ts +70 -0
  168. package/src/security/security-module.ts +145 -0
  169. package/src/security/types.ts +165 -0
  170. package/src/session/decorators.ts +45 -0
  171. package/src/session/index.ts +19 -0
  172. package/src/session/middleware.ts +143 -0
  173. package/src/session/service.ts +218 -0
  174. package/src/session/session-module.ts +69 -0
  175. package/src/session/types.ts +373 -0
  176. package/src/swagger/decorators.ts +133 -0
  177. package/src/swagger/generator.ts +234 -0
  178. package/src/swagger/index.ts +7 -0
  179. package/src/swagger/swagger-extension.ts +41 -0
  180. package/src/swagger/swagger-module.ts +83 -0
  181. package/src/swagger/types.ts +188 -0
  182. package/src/swagger/ui.ts +98 -0
  183. package/src/testing/harness.ts +96 -0
  184. package/src/validation/decorators.ts +95 -0
  185. package/src/validation/errors.ts +28 -0
  186. package/src/validation/index.ts +14 -0
  187. package/src/validation/types.ts +35 -0
  188. package/src/validation/validator.ts +63 -0
  189. package/src/websocket/decorators.ts +53 -0
  190. package/src/websocket/index.ts +12 -0
  191. package/src/websocket/registry.ts +133 -0
  192. package/tests/cache/cache-module.test.ts +212 -0
  193. package/tests/config/config-module.test.ts +151 -0
  194. package/tests/controller/controller.test.ts +189 -0
  195. package/tests/controller/path-combination.test.ts +207 -0
  196. package/tests/core/application.test.ts +57 -0
  197. package/tests/core/context-body.test.ts +44 -0
  198. package/tests/core/context.test.ts +86 -0
  199. package/tests/core/edge-cases.test.ts +432 -0
  200. package/tests/database/database-module.test.ts +385 -0
  201. package/tests/database/orm.test.ts +164 -0
  202. package/tests/database/postgres-mysql-integration.test.ts +395 -0
  203. package/tests/database/transaction.test.ts +238 -0
  204. package/tests/di/container.test.ts +264 -0
  205. package/tests/di/module.test.ts +128 -0
  206. package/tests/error/error-codes.test.ts +121 -0
  207. package/tests/error/error-handler.test.ts +68 -0
  208. package/tests/error/error-handling.test.ts +254 -0
  209. package/tests/error/http-exception.test.ts +37 -0
  210. package/tests/error/i18n-integration.test.ts +175 -0
  211. package/tests/extensions/logger-extension.test.ts +40 -0
  212. package/tests/files/static-middleware.test.ts +67 -0
  213. package/tests/files/upload-middleware.test.ts +43 -0
  214. package/tests/health/health-module.test.ts +116 -0
  215. package/tests/integration/application-router.test.ts +85 -0
  216. package/tests/integration/body-parsing.test.ts +88 -0
  217. package/tests/integration/cache-e2e.test.ts +114 -0
  218. package/tests/integration/oauth2-e2e.test.ts +615 -0
  219. package/tests/integration/session-e2e.test.ts +207 -0
  220. package/tests/interceptor/builtin/cache-interceptor.test.ts +137 -0
  221. package/tests/interceptor/builtin/permission-interceptor.test.ts +182 -0
  222. package/tests/interceptor/interceptor-advanced-integration.test.ts +592 -0
  223. package/tests/interceptor/interceptor-arg-modification.test.ts +76 -0
  224. package/tests/interceptor/interceptor-chain.test.ts +199 -0
  225. package/tests/interceptor/interceptor-integration.test.ts +230 -0
  226. package/tests/interceptor/interceptor-registry.test.ts +200 -0
  227. package/tests/interceptor/perf/interceptor-performance.test.ts +341 -0
  228. package/tests/metrics/metrics-module.test.ts +178 -0
  229. package/tests/middleware/builtin.test.ts +206 -0
  230. package/tests/middleware/file-upload.test.ts +41 -0
  231. package/tests/middleware/middleware.test.ts +120 -0
  232. package/tests/middleware/pipeline.test.ts +72 -0
  233. package/tests/middleware/rate-limit.test.ts +314 -0
  234. package/tests/middleware/static-file.test.ts +62 -0
  235. package/tests/perf/harness.test.ts +48 -0
  236. package/tests/perf/optimization.test.ts +183 -0
  237. package/tests/perf/regression.test.ts +120 -0
  238. package/tests/queue/queue-module.test.ts +217 -0
  239. package/tests/request/body-parser.test.ts +96 -0
  240. package/tests/request/response.test.ts +99 -0
  241. package/tests/router/decorators.test.ts +46 -0
  242. package/tests/router/registry.test.ts +51 -0
  243. package/tests/router/route.test.ts +71 -0
  244. package/tests/router/router-normalization.test.ts +106 -0
  245. package/tests/router/router.test.ts +133 -0
  246. package/tests/security/access-decision-manager.test.ts +84 -0
  247. package/tests/security/authentication-manager.test.ts +81 -0
  248. package/tests/security/context.test.ts +302 -0
  249. package/tests/security/filter.test.ts +225 -0
  250. package/tests/security/jwt-provider.test.ts +106 -0
  251. package/tests/security/oauth2-provider.test.ts +269 -0
  252. package/tests/security/security-module.test.ts +143 -0
  253. package/tests/session/session-module.test.ts +307 -0
  254. package/tests/stress/di-stress.test.ts +30 -0
  255. package/tests/swagger/decorators.test.ts +153 -0
  256. package/tests/swagger/generator.test.ts +202 -0
  257. package/tests/swagger/swagger-extension.test.ts +72 -0
  258. package/tests/swagger/swagger-module.test.ts +79 -0
  259. package/tests/utils/test-port.ts +10 -0
  260. package/tests/validation/controller-validation.test.ts +64 -0
  261. package/tests/validation/validation.test.ts +42 -0
  262. package/tests/websocket/gateway.test.ts +68 -0
@@ -0,0 +1,76 @@
1
+ import 'reflect-metadata';
2
+ import { Propagation } from './transaction-types';
3
+ import type { TransactionOptions } from './transaction-types';
4
+
5
+ /**
6
+ * Transaction 元数据键
7
+ */
8
+ export const TRANSACTION_METADATA_KEY = Symbol('@dangao/bun-server:orm:transaction');
9
+
10
+ /**
11
+ * Transactional 装饰器
12
+ * 标记方法需要在事务中执行
13
+ * @param options - 事务配置选项
14
+ */
15
+ export function Transactional(options?: TransactionOptions): MethodDecorator {
16
+ return (target, propertyKey, descriptor) => {
17
+ if (!descriptor || typeof descriptor.value !== 'function') {
18
+ throw new Error('@Transactional() can only be applied to methods');
19
+ }
20
+
21
+ const metadata = {
22
+ propagation: options?.propagation ?? Propagation.REQUIRED,
23
+ isolationLevel: options?.isolationLevel,
24
+ timeout: options?.timeout,
25
+ readOnly: options?.readOnly ?? false,
26
+ rollbackFor: options?.rollbackFor ?? [],
27
+ noRollbackFor: options?.noRollbackFor ?? [],
28
+ };
29
+
30
+ Reflect.defineMetadata(TRANSACTION_METADATA_KEY, metadata, target, propertyKey);
31
+ };
32
+ }
33
+
34
+ /**
35
+ * 获取 Transaction 元数据
36
+ * 支持从实例或原型上获取元数据(元数据通常存储在原型上)
37
+ */
38
+ export function getTransactionMetadata(
39
+ target: unknown,
40
+ propertyKey: string | symbol,
41
+ ): TransactionOptions | undefined {
42
+ if (typeof target !== 'object' || target === null) {
43
+ return undefined;
44
+ }
45
+
46
+ // 首先尝试直接从 target 获取(如果 target 是原型)
47
+ let metadata = Reflect.getMetadata(TRANSACTION_METADATA_KEY, target, propertyKey) as TransactionOptions | undefined;
48
+ if (metadata !== undefined) {
49
+ return metadata;
50
+ }
51
+
52
+ // 如果 target 是实例,尝试从原型获取
53
+ // 装饰器元数据通常存储在原型上,而不是实例上
54
+ const prototype = Object.getPrototypeOf(target);
55
+ if (prototype && prototype !== Object.prototype) {
56
+ metadata = Reflect.getMetadata(TRANSACTION_METADATA_KEY, prototype, propertyKey) as TransactionOptions | undefined;
57
+ if (metadata !== undefined) {
58
+ return metadata;
59
+ }
60
+ }
61
+
62
+ // 如果仍然找不到,尝试从构造函数原型获取
63
+ // 这处理了 target 是实例但原型链查找失败的情况
64
+ const constructor = (target as any).constructor;
65
+ if (constructor && typeof constructor === 'function') {
66
+ // 如果 target 本身不是构造函数原型,尝试从构造函数原型获取
67
+ if (target !== constructor.prototype) {
68
+ metadata = Reflect.getMetadata(TRANSACTION_METADATA_KEY, constructor.prototype, propertyKey) as TransactionOptions | undefined;
69
+ if (metadata !== undefined) {
70
+ return metadata;
71
+ }
72
+ }
73
+ }
74
+
75
+ return undefined;
76
+ }
@@ -0,0 +1,263 @@
1
+ import type { Container } from '../../di/container';
2
+ import type { Context } from '../../core/context';
3
+ import type { Interceptor } from '../../interceptor';
4
+ import { TRANSACTION_SERVICE_TOKEN } from './transaction-types';
5
+ import { TransactionManager } from './transaction-manager';
6
+ import { getTransactionMetadata, TRANSACTION_METADATA_KEY } from './transaction-decorator';
7
+ import { Propagation, TransactionStatus } from './transaction-types';
8
+
9
+ /**
10
+ * 事务拦截器
11
+ * 在方法调用时检查 @Transactional() 装饰器并执行事务逻辑
12
+ * 实现 Interceptor 接口,通过拦截器机制注册和执行
13
+ */
14
+ export class TransactionInterceptor implements Interceptor {
15
+ /**
16
+ * 执行拦截器逻辑
17
+ * 实现 Interceptor 接口
18
+ */
19
+ public async execute<T>(
20
+ target: unknown,
21
+ propertyKey: string | symbol,
22
+ originalMethod: (...args: unknown[]) => T | Promise<T>,
23
+ args: unknown[],
24
+ container: Container,
25
+ context?: Context,
26
+ ): Promise<T> {
27
+ const transactionMetadata = getTransactionMetadata(target, propertyKey);
28
+
29
+ // 如果没有事务元数据,直接执行原方法
30
+ if (!transactionMetadata) {
31
+ return await Promise.resolve(originalMethod.apply(target, args));
32
+ }
33
+
34
+ // 获取事务管理器
35
+ let transactionManager: TransactionManager;
36
+ try {
37
+ transactionManager = container.resolve<TransactionManager>(
38
+ TRANSACTION_SERVICE_TOKEN,
39
+ );
40
+ } catch (error) {
41
+ // 如果没有注册事务管理器,直接执行原方法
42
+ console.warn('TransactionManager not found, executing without transaction');
43
+ return await Promise.resolve(originalMethod.apply(target, args));
44
+ }
45
+
46
+ const propagation = transactionMetadata.propagation ?? Propagation.REQUIRED;
47
+ const currentTransaction = transactionManager.getCurrentTransaction();
48
+
49
+ // 根据传播行为决定事务策略
50
+ switch (propagation) {
51
+ case Propagation.REQUIRED:
52
+ if (currentTransaction) {
53
+ // 加入现有事务
54
+ return await TransactionInterceptor.executeInExistingTransaction(
55
+ originalMethod,
56
+ target,
57
+ args,
58
+ currentTransaction.id,
59
+ transactionManager,
60
+ );
61
+ } else {
62
+ // 创建新事务
63
+ return await TransactionInterceptor.executeInNewTransaction(
64
+ originalMethod,
65
+ target,
66
+ args,
67
+ transactionMetadata,
68
+ transactionManager,
69
+ );
70
+ }
71
+
72
+ case Propagation.REQUIRES_NEW:
73
+ // 总是创建新事务
74
+ return await TransactionInterceptor.executeInNewTransaction(
75
+ originalMethod,
76
+ target,
77
+ args,
78
+ transactionMetadata,
79
+ transactionManager,
80
+ );
81
+
82
+ case Propagation.SUPPORTS:
83
+ if (currentTransaction) {
84
+ // 加入现有事务
85
+ return await TransactionInterceptor.executeInExistingTransaction(
86
+ originalMethod,
87
+ target,
88
+ args,
89
+ currentTransaction.id,
90
+ transactionManager,
91
+ );
92
+ } else {
93
+ // 非事务执行
94
+ return await Promise.resolve(originalMethod.apply(target, args));
95
+ }
96
+
97
+ case Propagation.NOT_SUPPORTED:
98
+ // 非事务执行
99
+ return await Promise.resolve(originalMethod.apply(target, args));
100
+
101
+ case Propagation.NEVER:
102
+ if (currentTransaction) {
103
+ throw new Error(
104
+ 'Transaction propagation NEVER requires no existing transaction',
105
+ );
106
+ }
107
+ return await Promise.resolve(originalMethod.apply(target, args));
108
+
109
+ case Propagation.NESTED:
110
+ if (currentTransaction) {
111
+ // 创建嵌套事务(保存点)
112
+ return await TransactionInterceptor.executeInNestedTransaction(
113
+ originalMethod,
114
+ target,
115
+ args,
116
+ currentTransaction.id,
117
+ transactionMetadata,
118
+ transactionManager,
119
+ );
120
+ } else {
121
+ // 创建新事务
122
+ return await TransactionInterceptor.executeInNewTransaction(
123
+ originalMethod,
124
+ target,
125
+ args,
126
+ transactionMetadata,
127
+ transactionManager,
128
+ );
129
+ }
130
+
131
+ default:
132
+ return await Promise.resolve(originalMethod.apply(target, args));
133
+ }
134
+ }
135
+
136
+ /**
137
+ * 执行带事务的方法(静态方法,保持向后兼容)
138
+ * @deprecated 使用拦截器机制,此方法保留用于向后兼容
139
+ */
140
+ public static async executeWithTransaction<T>(
141
+ target: unknown,
142
+ propertyKey: string | symbol,
143
+ originalMethod: (...args: unknown[]) => T | Promise<T>,
144
+ args: unknown[],
145
+ container: Container,
146
+ ): Promise<T> {
147
+ const interceptor = new TransactionInterceptor();
148
+ return await interceptor.execute(target, propertyKey, originalMethod, args, container);
149
+ }
150
+
151
+ /**
152
+ * 在新事务中执行方法
153
+ */
154
+ private static async executeInNewTransaction<T>(
155
+ method: (...args: unknown[]) => T | Promise<T>,
156
+ target: unknown,
157
+ args: unknown[],
158
+ options: {
159
+ timeout?: number;
160
+ rollbackFor?: Array<new () => Error>;
161
+ noRollbackFor?: Array<new () => Error>;
162
+ },
163
+ transactionManager: TransactionManager,
164
+ ): Promise<T> {
165
+ const context = await transactionManager.beginTransaction({
166
+ timeout: options.timeout,
167
+ });
168
+
169
+ try {
170
+ const result = await Promise.resolve(method.apply(target, args));
171
+ await transactionManager.commitTransaction(context.id);
172
+ return result;
173
+ } catch (error) {
174
+ // 检查是否需要回滚
175
+ if (this.shouldRollback(error, options.rollbackFor, options.noRollbackFor)) {
176
+ await transactionManager.rollbackTransaction(context.id);
177
+ } else {
178
+ await transactionManager.commitTransaction(context.id);
179
+ }
180
+ throw error;
181
+ }
182
+ }
183
+
184
+ /**
185
+ * 在现有事务中执行方法
186
+ */
187
+ private static async executeInExistingTransaction<T>(
188
+ method: (...args: unknown[]) => T | Promise<T>,
189
+ target: unknown,
190
+ args: unknown[],
191
+ transactionId: string,
192
+ transactionManager: TransactionManager,
193
+ ): Promise<T> {
194
+ // 直接执行,不提交或回滚(由外层事务管理)
195
+ return await Promise.resolve(method.apply(target, args));
196
+ }
197
+
198
+ /**
199
+ * 在嵌套事务中执行方法
200
+ */
201
+ private static async executeInNestedTransaction<T>(
202
+ method: (...args: unknown[]) => T | Promise<T>,
203
+ target: unknown,
204
+ args: unknown[],
205
+ parentTransactionId: string,
206
+ options: {
207
+ timeout?: number;
208
+ rollbackFor?: Array<new () => Error>;
209
+ noRollbackFor?: Array<new () => Error>;
210
+ },
211
+ transactionManager: TransactionManager,
212
+ ): Promise<T> {
213
+ const savepointName = await transactionManager.createSavepoint(parentTransactionId);
214
+
215
+ try {
216
+ const result = await Promise.resolve(method.apply(target, args));
217
+ // 嵌套事务成功,不执行任何操作(保存点保留)
218
+ return result;
219
+ } catch (error) {
220
+ // 检查是否需要回滚到保存点
221
+ if (this.shouldRollback(error, options.rollbackFor, options.noRollbackFor)) {
222
+ await transactionManager.rollbackToSavepoint(parentTransactionId, savepointName);
223
+ }
224
+ throw error;
225
+ }
226
+ }
227
+
228
+ /**
229
+ * 判断是否应该回滚
230
+ */
231
+ private static shouldRollback(
232
+ error: unknown,
233
+ rollbackFor?: Array<new () => Error>,
234
+ noRollbackFor?: Array<new () => Error>,
235
+ ): boolean {
236
+ if (!error) {
237
+ return false;
238
+ }
239
+
240
+ // 如果指定了 noRollbackFor,且错误匹配,则不回滚
241
+ if (noRollbackFor && noRollbackFor.length > 0) {
242
+ for (const ErrorClass of noRollbackFor) {
243
+ if (error instanceof ErrorClass) {
244
+ return false;
245
+ }
246
+ }
247
+ }
248
+
249
+ // 如果指定了 rollbackFor,且错误匹配,则回滚
250
+ if (rollbackFor && rollbackFor.length > 0) {
251
+ for (const ErrorClass of rollbackFor) {
252
+ if (error instanceof ErrorClass) {
253
+ return true;
254
+ }
255
+ }
256
+ // 如果指定了 rollbackFor 但错误不匹配,则不回滚
257
+ return false;
258
+ }
259
+
260
+ // 默认情况下,所有错误都回滚
261
+ return true;
262
+ }
263
+ }
@@ -0,0 +1,276 @@
1
+ import { Injectable, Inject } from '../../di/decorators';
2
+ import { DATABASE_SERVICE_TOKEN } from '../types';
3
+ import type { DatabaseService } from '../service';
4
+ import {
5
+ Propagation,
6
+ IsolationLevel,
7
+ TransactionStatus,
8
+ type TransactionOptions,
9
+ type TransactionContext,
10
+ } from './transaction-types';
11
+
12
+ /**
13
+ * 事务管理器
14
+ * 管理数据库事务的生命周期
15
+ */
16
+ @Injectable()
17
+ export class TransactionManager {
18
+ private readonly transactions = new Map<string, TransactionContext>();
19
+ private readonly connectionTransactions = new Map<unknown, string>(); // connection -> transactionId
20
+
21
+ public constructor(
22
+ @Inject(DATABASE_SERVICE_TOKEN)
23
+ private readonly databaseService: DatabaseService,
24
+ ) {}
25
+
26
+ /**
27
+ * 开始事务
28
+ */
29
+ public async beginTransaction(
30
+ options: TransactionOptions = {},
31
+ ): Promise<TransactionContext> {
32
+ const transactionId = this.generateTransactionId();
33
+ const context: TransactionContext = {
34
+ id: transactionId,
35
+ status: TransactionStatus.ACTIVE,
36
+ startTime: Date.now(),
37
+ level: 0,
38
+ savepoints: [],
39
+ };
40
+
41
+ // 获取数据库连接
42
+ const connection = await this.databaseService.getConnection();
43
+ this.connectionTransactions.set(connection, transactionId);
44
+ this.transactions.set(transactionId, context);
45
+
46
+ // 开始事务(根据数据库类型)
47
+ await this.executeBegin(connection, options);
48
+
49
+ return context;
50
+ }
51
+
52
+ /**
53
+ * 提交事务
54
+ */
55
+ public async commitTransaction(transactionId: string): Promise<void> {
56
+ const context = this.transactions.get(transactionId);
57
+ if (!context) {
58
+ throw new Error(`Transaction ${transactionId} not found`);
59
+ }
60
+
61
+ if (context.status !== TransactionStatus.ACTIVE) {
62
+ throw new Error(`Transaction ${transactionId} is not active`);
63
+ }
64
+
65
+ // 查找连接
66
+ const connection = this.findConnectionByTransactionId(transactionId);
67
+ if (!connection) {
68
+ throw new Error(`Connection not found for transaction ${transactionId}`);
69
+ }
70
+
71
+ // 提交事务
72
+ await this.executeCommit(connection);
73
+
74
+ context.status = TransactionStatus.COMMITTED;
75
+ this.cleanupTransaction(transactionId);
76
+ }
77
+
78
+ /**
79
+ * 回滚事务
80
+ */
81
+ public async rollbackTransaction(transactionId: string): Promise<void> {
82
+ const context = this.transactions.get(transactionId);
83
+ if (!context) {
84
+ throw new Error(`Transaction ${transactionId} not found`);
85
+ }
86
+
87
+ // 查找连接
88
+ const connection = this.findConnectionByTransactionId(transactionId);
89
+ if (!connection) {
90
+ throw new Error(`Connection not found for transaction ${transactionId}`);
91
+ }
92
+
93
+ // 回滚事务
94
+ await this.executeRollback(connection);
95
+
96
+ context.status = TransactionStatus.ROLLED_BACK;
97
+ this.cleanupTransaction(transactionId);
98
+ }
99
+
100
+ /**
101
+ * 创建保存点(用于嵌套事务)
102
+ */
103
+ public async createSavepoint(transactionId: string): Promise<string> {
104
+ const context = this.transactions.get(transactionId);
105
+ if (!context) {
106
+ throw new Error(`Transaction ${transactionId} not found`);
107
+ }
108
+
109
+ const savepointName = `sp_${context.level}_${Date.now()}`;
110
+ context.savepoints = context.savepoints || [];
111
+ context.savepoints.push(savepointName);
112
+ context.level += 1;
113
+
114
+ const connection = this.findConnectionByTransactionId(transactionId);
115
+ if (connection) {
116
+ await this.executeSavepoint(connection, savepointName);
117
+ }
118
+
119
+ return savepointName;
120
+ }
121
+
122
+ /**
123
+ * 回滚到保存点
124
+ */
125
+ public async rollbackToSavepoint(
126
+ transactionId: string,
127
+ savepointName: string,
128
+ ): Promise<void> {
129
+ const context = this.transactions.get(transactionId);
130
+ if (!context) {
131
+ throw new Error(`Transaction ${transactionId} not found`);
132
+ }
133
+
134
+ const connection = this.findConnectionByTransactionId(transactionId);
135
+ if (connection) {
136
+ await this.executeRollbackToSavepoint(connection, savepointName);
137
+ }
138
+
139
+ // 移除该保存点之后的所有保存点
140
+ const index = context.savepoints?.indexOf(savepointName) ?? -1;
141
+ if (index >= 0 && context.savepoints) {
142
+ context.savepoints = context.savepoints.slice(0, index);
143
+ context.level = index;
144
+ }
145
+ }
146
+
147
+ /**
148
+ * 获取当前事务上下文
149
+ */
150
+ public getCurrentTransaction(): TransactionContext | null {
151
+ // 简化实现:返回最后一个活动事务
152
+ // 实际实现中应该使用 ThreadLocal 或类似机制
153
+ for (const context of this.transactions.values()) {
154
+ if (context.status === TransactionStatus.ACTIVE) {
155
+ return context;
156
+ }
157
+ }
158
+ return null;
159
+ }
160
+
161
+ /**
162
+ * 检查是否有活动事务
163
+ */
164
+ public hasActiveTransaction(): boolean {
165
+ return this.getCurrentTransaction() !== null;
166
+ }
167
+
168
+ /**
169
+ * 生成事务 ID
170
+ */
171
+ private generateTransactionId(): string {
172
+ return `tx_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
173
+ }
174
+
175
+ /**
176
+ * 查找连接对应的事务 ID
177
+ */
178
+ private findConnectionByTransactionId(transactionId: string): unknown {
179
+ for (const [connection, txId] of this.connectionTransactions.entries()) {
180
+ if (txId === transactionId) {
181
+ return connection;
182
+ }
183
+ }
184
+ return null;
185
+ }
186
+
187
+ /**
188
+ * 清理事务
189
+ */
190
+ private cleanupTransaction(transactionId: string): void {
191
+ this.transactions.delete(transactionId);
192
+ // 清理连接映射
193
+ for (const [connection, txId] of this.connectionTransactions.entries()) {
194
+ if (txId === transactionId) {
195
+ this.connectionTransactions.delete(connection);
196
+ break;
197
+ }
198
+ }
199
+ }
200
+
201
+ /**
202
+ * 执行 BEGIN 语句
203
+ */
204
+ private async executeBegin(connection: unknown, options: TransactionOptions): Promise<void> {
205
+ const dbType = this.databaseService['config'].database.type;
206
+
207
+ if (dbType === 'sqlite') {
208
+ // SQLite 默认自动提交,需要显式开始事务
209
+ await this.databaseService.query('BEGIN TRANSACTION');
210
+ } else if (dbType === 'postgres' || dbType === 'mysql') {
211
+ // PostgreSQL 和 MySQL 使用 Bun.SQL,需要设置隔离级别
212
+ let sql = 'START TRANSACTION';
213
+ if (options.isolationLevel) {
214
+ const isolation = this.getIsolationLevelSQL(options.isolationLevel);
215
+ sql += ` ${isolation}`;
216
+ }
217
+ if (options.readOnly) {
218
+ sql += ' READ ONLY';
219
+ }
220
+ await this.databaseService.query(sql);
221
+ }
222
+ }
223
+
224
+ /**
225
+ * 执行 COMMIT 语句
226
+ */
227
+ private async executeCommit(connection: unknown): Promise<void> {
228
+ await this.databaseService.query('COMMIT');
229
+ }
230
+
231
+ /**
232
+ * 执行 ROLLBACK 语句
233
+ */
234
+ private async executeRollback(connection: unknown): Promise<void> {
235
+ await this.databaseService.query('ROLLBACK');
236
+ }
237
+
238
+ /**
239
+ * 执行 SAVEPOINT 语句
240
+ */
241
+ private async executeSavepoint(connection: unknown, savepointName: string): Promise<void> {
242
+ await this.databaseService.query(`SAVEPOINT ${savepointName}`);
243
+ }
244
+
245
+ /**
246
+ * 执行 ROLLBACK TO SAVEPOINT 语句
247
+ */
248
+ private async executeRollbackToSavepoint(
249
+ connection: unknown,
250
+ savepointName: string,
251
+ ): Promise<void> {
252
+ await this.databaseService.query(`ROLLBACK TO SAVEPOINT ${savepointName}`);
253
+ }
254
+
255
+ /**
256
+ * 获取隔离级别的 SQL
257
+ */
258
+ private getIsolationLevelSQL(level: IsolationLevel): string {
259
+ const dbType = this.databaseService['config'].database.type;
260
+ const levelMap: Record<IsolationLevel, string> = {
261
+ [IsolationLevel.READ_UNCOMMITTED]: 'READ UNCOMMITTED',
262
+ [IsolationLevel.READ_COMMITTED]: 'READ COMMITTED',
263
+ [IsolationLevel.REPEATABLE_READ]: 'REPEATABLE READ',
264
+ [IsolationLevel.SERIALIZABLE]: 'SERIALIZABLE',
265
+ };
266
+
267
+ if (dbType === 'postgres') {
268
+ return `SET TRANSACTION ISOLATION LEVEL ${levelMap[level]}`;
269
+ } else if (dbType === 'mysql') {
270
+ return `SET TRANSACTION ISOLATION LEVEL ${levelMap[level]}`;
271
+ }
272
+
273
+ // SQLite 不支持隔离级别设置
274
+ return '';
275
+ }
276
+ }