@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,239 @@
1
+ import type {
2
+ ConnectionInfo,
3
+ ConnectionPoolOptions,
4
+ ConnectionStatus,
5
+ DatabaseConfig,
6
+ DatabaseType,
7
+ } from './types';
8
+
9
+ import { ConnectionPool } from './connection-pool';
10
+
11
+ /**
12
+ * 数据库连接管理器
13
+ * 负责管理数据库连接池和连接健康检查
14
+ */
15
+ export class DatabaseConnectionManager {
16
+ private config: DatabaseConfig;
17
+ private poolOptions: ConnectionPoolOptions;
18
+ private pool: ConnectionPool;
19
+ private currentConnection: unknown | null = null;
20
+ private status: ConnectionStatus = 'disconnected';
21
+ private error: string | undefined;
22
+
23
+ public constructor(
24
+ config: DatabaseConfig,
25
+ poolOptions: ConnectionPoolOptions = {},
26
+ ) {
27
+ this.config = config;
28
+ this.poolOptions = {
29
+ maxConnections: poolOptions.maxConnections ?? 10,
30
+ connectionTimeout: poolOptions.connectionTimeout ?? 30000,
31
+ retryCount: poolOptions.retryCount ?? 3,
32
+ retryDelay: poolOptions.retryDelay ?? 1000,
33
+ };
34
+ this.pool = new ConnectionPool(config, this.poolOptions);
35
+ }
36
+
37
+ /**
38
+ * 连接数据库(从连接池获取连接)
39
+ */
40
+ public async connect(): Promise<void> {
41
+ if (this.status === 'connected') {
42
+ return;
43
+ }
44
+
45
+ this.status = 'connecting';
46
+
47
+ try {
48
+ // 从连接池获取连接
49
+ this.currentConnection = await this.pool.acquire();
50
+ this.status = 'connected';
51
+ this.error = undefined;
52
+ } catch (error) {
53
+ this.status = 'error';
54
+ this.error = error instanceof Error ? error.message : String(error);
55
+ throw error;
56
+ }
57
+ }
58
+
59
+ /**
60
+ * 断开数据库连接(释放连接回池中)
61
+ */
62
+ public async disconnect(): Promise<void> {
63
+ if (this.status === 'disconnected' || !this.currentConnection) {
64
+ return;
65
+ }
66
+
67
+ try {
68
+ // 释放连接回池中
69
+ this.pool.release(this.currentConnection);
70
+ this.currentConnection = null;
71
+ this.status = 'disconnected';
72
+ this.error = undefined;
73
+ } catch (error) {
74
+ this.status = 'error';
75
+ this.error = error instanceof Error ? error.message : String(error);
76
+ throw error;
77
+ }
78
+ }
79
+
80
+ /**
81
+ * 关闭连接池(关闭所有连接)
82
+ */
83
+ public async closePool(): Promise<void> {
84
+ await this.pool.close();
85
+ this.currentConnection = null;
86
+ this.status = 'disconnected';
87
+ this.error = undefined;
88
+ }
89
+
90
+ /**
91
+ * 检查数据库连接健康状态
92
+ */
93
+ public async healthCheck(): Promise<boolean> {
94
+ if (this.status !== 'connected' || !this.currentConnection) {
95
+ return false;
96
+ }
97
+
98
+ try {
99
+ if (this.config.type === 'sqlite') {
100
+ return await this.healthCheckSqlite(this.currentConnection);
101
+ } else if (this.config.type === 'postgres') {
102
+ return await this.healthCheckPostgres(this.currentConnection);
103
+ } else if (this.config.type === 'mysql') {
104
+ return await this.healthCheckMysql(this.currentConnection);
105
+ }
106
+ return false;
107
+ } catch {
108
+ return false;
109
+ }
110
+ }
111
+
112
+ /**
113
+ * 获取连接池统计信息
114
+ */
115
+ public getPoolStats() {
116
+ return this.pool.getPoolStats();
117
+ }
118
+
119
+ /**
120
+ * 获取连接信息
121
+ */
122
+ public getConnectionInfo(): ConnectionInfo {
123
+ return {
124
+ status: this.status,
125
+ type: this.config.type,
126
+ error: this.error,
127
+ };
128
+ }
129
+
130
+ /**
131
+ * 获取原始连接对象
132
+ */
133
+ public getConnection(): unknown {
134
+ return this.currentConnection;
135
+ }
136
+
137
+ /**
138
+ * 从连接池获取新连接(用于需要独立连接的场景)
139
+ */
140
+ public async acquireConnection(): Promise<unknown> {
141
+ return await this.pool.acquire();
142
+ }
143
+
144
+ /**
145
+ * 释放连接回池中
146
+ */
147
+ public releaseConnection(connection: unknown): void {
148
+ this.pool.release(connection);
149
+ }
150
+
151
+ /**
152
+ * 获取数据库类型
153
+ */
154
+ public getDatabaseType(): DatabaseType {
155
+ return this.config.type;
156
+ }
157
+
158
+ /**
159
+ * SQLite 健康检查
160
+ */
161
+ private async healthCheckSqlite(connection: unknown): Promise<boolean> {
162
+ try {
163
+ if (
164
+ connection &&
165
+ typeof connection === 'object' &&
166
+ 'query' in connection &&
167
+ typeof connection.query === 'function'
168
+ ) {
169
+ // 执行简单的查询来检查连接
170
+ const db = connection as {
171
+ query: (sql: string) => {
172
+ all: () => unknown[];
173
+ };
174
+ };
175
+ db.query('SELECT 1').all();
176
+ return true;
177
+ }
178
+ return false;
179
+ } catch {
180
+ return false;
181
+ }
182
+ }
183
+
184
+ /**
185
+ * PostgreSQL 健康检查(使用 Bun.SQL)
186
+ */
187
+ private async healthCheckPostgres(connection: unknown): Promise<boolean> {
188
+ try {
189
+ // Bun.SQL 对象可以作为函数调用(模板字符串)
190
+ if (connection && typeof connection === 'function') {
191
+ const result = await (connection as (template: TemplateStringsArray, ...values: unknown[]) => Promise<unknown[]>)`SELECT 1`;
192
+ return Array.isArray(result) && result.length > 0;
193
+ }
194
+ // 或者使用 query 方法(如果存在)
195
+ if (
196
+ connection &&
197
+ typeof connection === 'object' &&
198
+ 'query' in connection &&
199
+ typeof connection.query === 'function'
200
+ ) {
201
+ await (connection as { query: (sql: string) => Promise<unknown> }).query(
202
+ 'SELECT 1',
203
+ );
204
+ return true;
205
+ }
206
+ return false;
207
+ } catch {
208
+ return false;
209
+ }
210
+ }
211
+
212
+ /**
213
+ * MySQL 健康检查(使用 Bun.SQL)
214
+ */
215
+ private async healthCheckMysql(connection: unknown): Promise<boolean> {
216
+ try {
217
+ // Bun.SQL 对象可以作为函数调用(模板字符串)
218
+ if (connection && typeof connection === 'function') {
219
+ const result = await (connection as (template: TemplateStringsArray, ...values: unknown[]) => Promise<unknown[]>)`SELECT 1`;
220
+ return Array.isArray(result) && result.length > 0;
221
+ }
222
+ // 或者使用 query 方法(如果存在)
223
+ if (
224
+ connection &&
225
+ typeof connection === 'object' &&
226
+ 'query' in connection &&
227
+ typeof connection.query === 'function'
228
+ ) {
229
+ await (connection as { query: (sql: string) => Promise<unknown> }).query(
230
+ 'SELECT 1',
231
+ );
232
+ return true;
233
+ }
234
+ return false;
235
+ } catch {
236
+ return false;
237
+ }
238
+ }
239
+ }
@@ -0,0 +1,322 @@
1
+ import type {
2
+ ConnectionPoolOptions,
3
+ DatabaseConfig,
4
+ DatabaseType,
5
+ } from './types';
6
+
7
+ /**
8
+ * 连接池中的连接项
9
+ */
10
+ interface PoolConnection {
11
+ /**
12
+ * 连接对象
13
+ */
14
+ connection: unknown;
15
+ /**
16
+ * 是否正在使用
17
+ */
18
+ inUse: boolean;
19
+ /**
20
+ * 创建时间
21
+ */
22
+ createdAt: number;
23
+ /**
24
+ * 最后使用时间
25
+ */
26
+ lastUsedAt: number;
27
+ }
28
+
29
+ /**
30
+ * 数据库连接池
31
+ * 管理数据库连接的创建、复用和销毁
32
+ */
33
+ export class ConnectionPool {
34
+ private config: DatabaseConfig;
35
+ private options: Required<ConnectionPoolOptions>;
36
+ private connections: PoolConnection[] = [];
37
+ private pendingConnections: Set<Promise<unknown>> = new Set();
38
+ private isClosing = false;
39
+
40
+ public constructor(
41
+ config: DatabaseConfig,
42
+ options: ConnectionPoolOptions = {},
43
+ ) {
44
+ this.config = config;
45
+ this.options = {
46
+ maxConnections: options.maxConnections ?? 10,
47
+ connectionTimeout: options.connectionTimeout ?? 30000,
48
+ retryCount: options.retryCount ?? 3,
49
+ retryDelay: options.retryDelay ?? 1000,
50
+ };
51
+ }
52
+
53
+ /**
54
+ * 获取连接(从池中获取或创建新连接)
55
+ */
56
+ public async acquire(): Promise<unknown> {
57
+ if (this.isClosing) {
58
+ throw new Error('Connection pool is closing');
59
+ }
60
+
61
+ // 查找空闲连接
62
+ const idleConnection = this.connections.find((conn) => !conn.inUse);
63
+ if (idleConnection) {
64
+ idleConnection.inUse = true;
65
+ idleConnection.lastUsedAt = Date.now();
66
+ return idleConnection.connection;
67
+ }
68
+
69
+ // 如果未达到最大连接数,创建新连接
70
+ if (this.connections.length < this.options.maxConnections) {
71
+ const connection = await this.createConnection();
72
+ const poolConnection: PoolConnection = {
73
+ connection,
74
+ inUse: true,
75
+ createdAt: Date.now(),
76
+ lastUsedAt: Date.now(),
77
+ };
78
+ this.connections.push(poolConnection);
79
+ return connection;
80
+ }
81
+
82
+ // 等待连接可用(简单的轮询等待)
83
+ return await this.waitForConnection();
84
+ }
85
+
86
+ /**
87
+ * 释放连接回池中
88
+ */
89
+ public release(connection: unknown): void {
90
+ const poolConnection = this.connections.find(
91
+ (conn) => conn.connection === connection,
92
+ );
93
+ if (poolConnection) {
94
+ poolConnection.inUse = false;
95
+ poolConnection.lastUsedAt = Date.now();
96
+ }
97
+ }
98
+
99
+ /**
100
+ * 关闭连接池
101
+ */
102
+ public async close(): Promise<void> {
103
+ this.isClosing = true;
104
+
105
+ // 等待所有待处理的连接完成
106
+ await Promise.all(Array.from(this.pendingConnections));
107
+
108
+ // 关闭所有连接
109
+ const closePromises = this.connections.map((poolConnection) =>
110
+ this.closeConnection(poolConnection.connection),
111
+ );
112
+ await Promise.all(closePromises);
113
+
114
+ this.connections = [];
115
+ this.isClosing = false;
116
+ }
117
+
118
+ /**
119
+ * 获取池状态信息
120
+ */
121
+ public getPoolStats(): {
122
+ total: number;
123
+ inUse: number;
124
+ idle: number;
125
+ maxConnections: number;
126
+ } {
127
+ const inUse = this.connections.filter((conn) => conn.inUse).length;
128
+ return {
129
+ total: this.connections.length,
130
+ inUse,
131
+ idle: this.connections.length - inUse,
132
+ maxConnections: this.options.maxConnections,
133
+ };
134
+ }
135
+
136
+ /**
137
+ * 创建新连接
138
+ */
139
+ private async createConnection(): Promise<unknown> {
140
+ const createPromise = this.createConnectionWithRetry();
141
+ this.pendingConnections.add(createPromise);
142
+
143
+ try {
144
+ const connection = await createPromise;
145
+ return connection;
146
+ } finally {
147
+ this.pendingConnections.delete(createPromise);
148
+ }
149
+ }
150
+
151
+ /**
152
+ * 带重试的连接创建
153
+ */
154
+ private async createConnectionWithRetry(): Promise<unknown> {
155
+ let lastError: Error | undefined;
156
+
157
+ for (let i = 0; i <= this.options.retryCount; i++) {
158
+ try {
159
+ if (this.config.type === 'sqlite') {
160
+ return await this.createSqliteConnection(this.config.config);
161
+ } else if (this.config.type === 'postgres') {
162
+ return await this.createPostgresConnection(this.config.config);
163
+ } else if (this.config.type === 'mysql') {
164
+ return await this.createMysqlConnection(this.config.config);
165
+ }
166
+ } catch (error) {
167
+ lastError = error instanceof Error ? error : new Error(String(error));
168
+ if (i < this.options.retryCount) {
169
+ await this.sleep(this.options.retryDelay);
170
+ }
171
+ }
172
+ }
173
+
174
+ throw lastError ?? new Error('Failed to create database connection');
175
+ }
176
+
177
+ /**
178
+ * 创建 SQLite 连接
179
+ */
180
+ private async createSqliteConnection(
181
+ config: { path: string },
182
+ ): Promise<unknown> {
183
+ // 使用 Bun 原生 SQLite API
184
+ const { Database } = await import('bun:sqlite');
185
+ const db = new Database(config.path);
186
+ return db;
187
+ }
188
+
189
+ /**
190
+ * 创建 PostgreSQL 连接(使用 Bun.SQL)
191
+ */
192
+ private async createPostgresConnection(
193
+ config: {
194
+ host: string;
195
+ port: number;
196
+ database: string;
197
+ user: string;
198
+ password: string;
199
+ ssl?: boolean;
200
+ },
201
+ ): Promise<unknown> {
202
+ // 使用 Bun.SQL API
203
+ // Bun.SQL 支持 postgres:// URL
204
+ const url = `postgres://${config.user}:${config.password}@${config.host}:${config.port}/${config.database}`;
205
+ const { SQL } = await import('bun');
206
+ return new SQL(url, {
207
+ max: 1, // 单个连接(连接池会在外部管理)
208
+ tls: config.ssl ?? false,
209
+ });
210
+ }
211
+
212
+ /**
213
+ * 创建 MySQL 连接(使用 Bun.SQL)
214
+ */
215
+ private async createMysqlConnection(
216
+ config: {
217
+ host: string;
218
+ port: number;
219
+ database: string;
220
+ user: string;
221
+ password: string;
222
+ },
223
+ ): Promise<unknown> {
224
+ // 使用 Bun.SQL API
225
+ // Bun.SQL 支持 mysql:// URL
226
+ const url = `mysql://${config.user}:${config.password}@${config.host}:${config.port}/${config.database}`;
227
+ const { SQL } = await import('bun');
228
+ return new SQL(url, {
229
+ max: 1, // 单个连接(连接池会在外部管理)
230
+ });
231
+ }
232
+
233
+ /**
234
+ * 关闭连接
235
+ */
236
+ private async closeConnection(connection: unknown): Promise<void> {
237
+ const dbType = this.config.type;
238
+ if (dbType === 'sqlite') {
239
+ await this.closeSqliteConnection(connection);
240
+ } else if (dbType === 'postgres') {
241
+ await this.closePostgresConnection(connection);
242
+ } else if (dbType === 'mysql') {
243
+ await this.closeMysqlConnection(connection);
244
+ }
245
+ }
246
+
247
+ /**
248
+ * 关闭 SQLite 连接
249
+ */
250
+ private async closeSqliteConnection(connection: unknown): Promise<void> {
251
+ if (
252
+ connection &&
253
+ typeof connection === 'object' &&
254
+ 'close' in connection &&
255
+ typeof connection.close === 'function'
256
+ ) {
257
+ (connection as { close: () => void }).close();
258
+ }
259
+ }
260
+
261
+ /**
262
+ * 关闭 PostgreSQL 连接(Bun.SQL 会自动管理连接)
263
+ */
264
+ private async closePostgresConnection(_connection: unknown): Promise<void> {
265
+ // Bun.SQL 会自动管理连接池,这里不需要手动关闭
266
+ // 如果需要强制关闭,可以调用 connection.close()
267
+ if (
268
+ _connection &&
269
+ typeof _connection === 'object' &&
270
+ 'close' in _connection &&
271
+ typeof (_connection as { close: () => void }).close === 'function'
272
+ ) {
273
+ (_connection as { close: () => void }).close();
274
+ }
275
+ }
276
+
277
+ /**
278
+ * 关闭 MySQL 连接(Bun.SQL 会自动管理连接)
279
+ */
280
+ private async closeMysqlConnection(_connection: unknown): Promise<void> {
281
+ // Bun.SQL 会自动管理连接池,这里不需要手动关闭
282
+ // 如果需要强制关闭,可以调用 connection.close()
283
+ if (
284
+ _connection &&
285
+ typeof _connection === 'object' &&
286
+ 'close' in _connection &&
287
+ typeof (_connection as { close: () => void }).close === 'function'
288
+ ) {
289
+ (_connection as { close: () => void }).close();
290
+ }
291
+ }
292
+
293
+ /**
294
+ * 等待连接可用
295
+ */
296
+ private async waitForConnection(): Promise<unknown> {
297
+ const startTime = Date.now();
298
+ const timeout = this.options.connectionTimeout;
299
+
300
+ while (Date.now() - startTime < timeout) {
301
+ const idleConnection = this.connections.find((conn) => !conn.inUse);
302
+ if (idleConnection) {
303
+ idleConnection.inUse = true;
304
+ idleConnection.lastUsedAt = Date.now();
305
+ return idleConnection.connection;
306
+ }
307
+
308
+ await this.sleep(100); // 等待 100ms 后重试
309
+ }
310
+
311
+ throw new Error(
312
+ `Connection timeout: No available connection within ${timeout}ms`,
313
+ );
314
+ }
315
+
316
+ /**
317
+ * 睡眠函数
318
+ */
319
+ private sleep(ms: number): Promise<void> {
320
+ return new Promise((resolve) => setTimeout(resolve, ms));
321
+ }
322
+ }
@@ -0,0 +1,62 @@
1
+ import type { Container } from '../di/container';
2
+ import type { ApplicationExtension } from '../extensions/types';
3
+
4
+ import { DATABASE_SERVICE_TOKEN } from './types';
5
+ import type { DatabaseService } from './service';
6
+
7
+ /**
8
+ * 数据库扩展
9
+ * 在应用启动时自动初始化数据库连接
10
+ */
11
+ export class DatabaseExtension implements ApplicationExtension {
12
+ public register(container: Container): void {
13
+ // 扩展注册时不初始化,等待应用启动时初始化
14
+ // 这里只是标记需要初始化
15
+ }
16
+
17
+ /**
18
+ * 初始化数据库连接
19
+ * 应该在应用启动时调用
20
+ */
21
+ public async initialize(container: Container): Promise<void> {
22
+ try {
23
+ const databaseService = container.resolve<DatabaseService>(
24
+ DATABASE_SERVICE_TOKEN,
25
+ );
26
+ await databaseService.initialize();
27
+ } catch (error) {
28
+ // 如果 DatabaseService 未注册,忽略错误
29
+ // 这意味着用户可能没有使用 DatabaseModule
30
+ if (
31
+ error instanceof Error &&
32
+ error.message.includes('Provider not found')
33
+ ) {
34
+ return;
35
+ }
36
+ throw error;
37
+ }
38
+ }
39
+
40
+ /**
41
+ * 关闭数据库连接
42
+ * 应该在应用停止时调用
43
+ */
44
+ public async close(container: Container): Promise<void> {
45
+ try {
46
+ const databaseService = container.resolve<DatabaseService>(
47
+ DATABASE_SERVICE_TOKEN,
48
+ );
49
+ // 关闭连接池(关闭所有连接)
50
+ await databaseService.closePool();
51
+ } catch (error) {
52
+ // 如果 DatabaseService 未注册,忽略错误
53
+ if (
54
+ error instanceof Error &&
55
+ error.message.includes('Provider not found')
56
+ ) {
57
+ return;
58
+ }
59
+ throw error;
60
+ }
61
+ }
62
+ }