@dangao/bun-server 3.1.0 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ai/types.d.ts +4 -0
- package/dist/ai/types.d.ts.map +1 -1
- package/dist/auth/types.d.ts +4 -0
- package/dist/auth/types.d.ts.map +1 -1
- package/dist/cache/interceptors.d.ts +3 -3
- package/dist/cache/interceptors.d.ts.map +1 -1
- package/dist/cache/service.d.ts +6 -6
- package/dist/cache/service.d.ts.map +1 -1
- package/dist/cache/types.d.ts +12 -12
- package/dist/cache/types.d.ts.map +1 -1
- package/dist/config/service.d.ts +6 -4
- package/dist/config/service.d.ts.map +1 -1
- package/dist/core/application.d.ts +4 -0
- package/dist/core/application.d.ts.map +1 -1
- package/dist/core/context.d.ts +4 -2
- package/dist/core/context.d.ts.map +1 -1
- package/dist/database/connection-manager.d.ts +2 -2
- package/dist/database/connection-manager.d.ts.map +1 -1
- package/dist/database/connection-pool.d.ts +4 -4
- package/dist/database/connection-pool.d.ts.map +1 -1
- package/dist/database/database-module.d.ts.map +1 -1
- package/dist/database/db-proxy.d.ts.map +1 -1
- package/dist/database/driver.d.ts +83 -0
- package/dist/database/driver.d.ts.map +1 -0
- package/dist/database/index.d.ts +2 -1
- package/dist/database/index.d.ts.map +1 -1
- package/dist/database/service.d.ts +0 -10
- package/dist/database/service.d.ts.map +1 -1
- package/dist/database/sql-manager.d.ts.map +1 -1
- package/dist/database/sqlite-adapter.d.ts +4 -2
- package/dist/database/sqlite-adapter.d.ts.map +1 -1
- package/dist/database/types.d.ts +26 -0
- package/dist/database/types.d.ts.map +1 -1
- package/dist/di/container.d.ts +2 -0
- package/dist/di/container.d.ts.map +1 -1
- package/dist/di/module-registry.d.ts.map +1 -1
- package/dist/di/module.d.ts +11 -1
- package/dist/di/module.d.ts.map +1 -1
- package/dist/di/types.d.ts +1 -1
- package/dist/di/types.d.ts.map +1 -1
- package/dist/error/handler.d.ts.map +1 -1
- package/dist/error/http-exception.d.ts +8 -8
- package/dist/error/http-exception.d.ts.map +1 -1
- package/dist/error/index.d.ts +1 -0
- package/dist/error/index.d.ts.map +1 -1
- package/dist/events/service.d.ts +2 -2
- package/dist/events/service.d.ts.map +1 -1
- package/dist/events/types.d.ts +12 -3
- package/dist/events/types.d.ts.map +1 -1
- package/dist/index.js +5951 -5820
- package/dist/index.node.mjs +310 -139
- package/dist/interceptor/base-interceptor.d.ts +3 -3
- package/dist/interceptor/base-interceptor.d.ts.map +1 -1
- package/dist/interceptor/builtin/log-interceptor.d.ts +1 -1
- package/dist/interceptor/builtin/log-interceptor.d.ts.map +1 -1
- package/dist/interceptor/builtin/permission-interceptor.d.ts +1 -1
- package/dist/interceptor/builtin/permission-interceptor.d.ts.map +1 -1
- package/dist/interceptor/interceptor-chain.d.ts +1 -1
- package/dist/interceptor/interceptor-chain.d.ts.map +1 -1
- package/dist/interceptor/interceptor-registry.d.ts +3 -1
- package/dist/interceptor/interceptor-registry.d.ts.map +1 -1
- package/dist/interceptor/types.d.ts +6 -1
- package/dist/interceptor/types.d.ts.map +1 -1
- package/dist/microservice/service-client/types.d.ts +1 -0
- package/dist/microservice/service-client/types.d.ts.map +1 -1
- package/dist/microservice/tracing/tracer.d.ts +1 -0
- package/dist/microservice/tracing/tracer.d.ts.map +1 -1
- package/dist/middleware/builtin/file-upload.d.ts +2 -0
- package/dist/middleware/builtin/file-upload.d.ts.map +1 -1
- package/dist/middleware/builtin/rate-limit.d.ts +9 -1
- package/dist/middleware/builtin/rate-limit.d.ts.map +1 -1
- package/dist/queue/service.d.ts +2 -2
- package/dist/queue/service.d.ts.map +1 -1
- package/dist/queue/types.d.ts +25 -1
- package/dist/queue/types.d.ts.map +1 -1
- package/dist/router/decorators.d.ts +1 -2
- package/dist/router/decorators.d.ts.map +1 -1
- package/dist/security/guards/types.d.ts +1 -0
- package/dist/security/guards/types.d.ts.map +1 -1
- package/dist/security/types.d.ts +1 -1
- package/dist/security/types.d.ts.map +1 -1
- package/dist/session/types.d.ts +8 -0
- package/dist/session/types.d.ts.map +1 -1
- package/dist/swagger/decorators.d.ts +1 -1
- package/dist/swagger/decorators.d.ts.map +1 -1
- package/dist/swagger/types.d.ts +1 -1
- package/dist/swagger/types.d.ts.map +1 -1
- package/dist/testing/harness.d.ts +1 -1
- package/dist/testing/harness.d.ts.map +1 -1
- package/dist/testing/test-client.d.ts +1 -1
- package/dist/testing/test-client.d.ts.map +1 -1
- package/dist/testing/testing-module.d.ts +2 -2
- package/dist/testing/testing-module.d.ts.map +1 -1
- package/dist/validation/errors.d.ts +5 -1
- package/dist/validation/errors.d.ts.map +1 -1
- package/docs/database.md +44 -0
- package/docs/zh/database.md +44 -0
- package/package.json +3 -3
- package/src/ai/types.ts +5 -0
- package/src/auth/types.ts +4 -1
- package/src/cache/interceptors.ts +6 -6
- package/src/cache/service-proxy.ts +2 -2
- package/src/cache/service.ts +17 -8
- package/src/cache/types.ts +12 -12
- package/src/config/service.ts +8 -6
- package/src/core/application.ts +7 -1
- package/src/core/context.ts +6 -3
- package/src/database/connection-manager.ts +5 -46
- package/src/database/connection-pool.ts +26 -49
- package/src/database/database-module.ts +6 -0
- package/src/database/db-proxy.ts +3 -2
- package/src/database/driver.ts +368 -0
- package/src/database/index.ts +8 -0
- package/src/database/service.ts +3 -74
- package/src/database/sql-manager.ts +38 -24
- package/src/database/sqlite-adapter.ts +4 -3
- package/src/database/types.ts +27 -2
- package/src/di/container.ts +13 -0
- package/src/di/module-registry.ts +2 -3
- package/src/di/module.ts +21 -2
- package/src/di/types.ts +1 -2
- package/src/error/handler.ts +3 -4
- package/src/error/http-exception.ts +11 -11
- package/src/error/index.ts +1 -1
- package/src/events/service.ts +4 -2
- package/src/events/types.ts +14 -3
- package/src/interceptor/base-interceptor.ts +5 -6
- package/src/interceptor/builtin/log-interceptor.ts +2 -3
- package/src/interceptor/builtin/permission-interceptor.ts +2 -3
- package/src/interceptor/interceptor-chain.ts +6 -7
- package/src/interceptor/interceptor-registry.ts +5 -3
- package/src/interceptor/types.ts +9 -4
- package/src/microservice/service-client/types.ts +1 -1
- package/src/microservice/tracing/tracer.ts +15 -3
- package/src/middleware/builtin/file-upload.ts +3 -2
- package/src/middleware/builtin/rate-limit.ts +22 -5
- package/src/queue/service.ts +1 -1
- package/src/queue/types.ts +40 -1
- package/src/router/decorators.ts +2 -3
- package/src/security/guards/types.ts +2 -1
- package/src/security/types.ts +1 -2
- package/src/session/service.ts +1 -1
- package/src/session/types.ts +10 -0
- package/src/swagger/decorators.ts +15 -4
- package/src/swagger/generator.ts +2 -3
- package/src/swagger/types.ts +1 -2
- package/src/testing/harness.ts +1 -2
- package/src/testing/test-client.ts +2 -2
- package/src/testing/testing-module.ts +5 -5
- package/src/validation/errors.ts +6 -2
- package/tests/bun-test-shim.d.ts +11 -0
- package/tests/controller/context-decorator.test.ts +1 -2
- package/tests/database/database-module.test.ts +4 -0
- package/tests/database/driver-mysql2.test.ts +95 -0
- package/tests/database/driver.test.ts +234 -0
- package/tests/database/orm.test.ts +4 -0
- package/tests/di/module.test.ts +199 -1
- package/tests/events/event-emitter.test.ts +2 -2
- package/tests/global.d.ts +30 -0
- package/tests/queue/queue-service.test.ts +14 -0
- package/tests/testing/testing-module.test.ts +20 -0
package/docs/database.md
CHANGED
|
@@ -25,6 +25,50 @@ DatabaseModule.forRoot({
|
|
|
25
25
|
});
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
+
## Driver selection (decoupled from the runtime platform)
|
|
29
|
+
|
|
30
|
+
> Added in v3.2.0.
|
|
31
|
+
|
|
32
|
+
By default the underlying driver for Postgres/MySQL is chosen by the runtime platform: the Bun runtime uses the built-in `Bun.SQL`, while Node.js uses the pure-JS `mysql2` / `postgres` drivers.
|
|
33
|
+
|
|
34
|
+
The optional `driver` field lets you **explicitly pick a pure-JS driver even under the Bun runtime**, decoupled from the platform engine (fs/crypto/http server, etc.):
|
|
35
|
+
|
|
36
|
+
| driver | Behavior |
|
|
37
|
+
| --- | --- |
|
|
38
|
+
| `'auto'` (default) | Bun → `Bun.SQL`; Node → `mysql2` (MySQL) / `postgres` (PostgreSQL). Backward compatible. |
|
|
39
|
+
| `'mysql2'` | Always use `mysql2`, regardless of runtime (only for `type: 'mysql'`). |
|
|
40
|
+
| `'postgres'` | Always use `postgres`, regardless of runtime (only for `type: 'postgres'`). |
|
|
41
|
+
| `'bun-sql'` | Force `Bun.SQL` (valid on Bun only; throws a clear error on Node). |
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
DatabaseModule.forRoot({
|
|
45
|
+
database: {
|
|
46
|
+
type: 'mysql',
|
|
47
|
+
driver: 'mysql2', // uses mysql2 even on the Bun runtime
|
|
48
|
+
config: {
|
|
49
|
+
host: '127.0.0.1',
|
|
50
|
+
port: 3306,
|
|
51
|
+
database: 'app',
|
|
52
|
+
user: 'root',
|
|
53
|
+
password: process.env.DB_PASSWORD!,
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
// The V2 single-tenant form is supported too:
|
|
59
|
+
DatabaseModule.forRoot({
|
|
60
|
+
type: 'mysql',
|
|
61
|
+
url: process.env.DB_URL!,
|
|
62
|
+
driver: 'mysql2',
|
|
63
|
+
});
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Why? MySQL under `bun build --compile`
|
|
67
|
+
|
|
68
|
+
A single binary produced by `bun build --compile` freezes the Bun runtime at build time, and the built-in `Bun.SQL` MySQL adapter has several known issues. Setting `driver: 'mysql2'` lets the compiled binary connect using the statically bundled pure-JS `mysql2` driver, without falling back to SQLite and without switching the whole platform via `BUN_SERVER_PLATFORM=node` (which would also move the HTTP server to Node).
|
|
69
|
+
|
|
70
|
+
The `driver` switch is orthogonal to `BUN_SERVER_PLATFORM`: the former only selects the SQL driver, while the latter selects the entire platform engine.
|
|
71
|
+
|
|
28
72
|
## Route strategy
|
|
29
73
|
|
|
30
74
|
```ts
|
package/docs/zh/database.md
CHANGED
|
@@ -25,6 +25,50 @@ DatabaseModule.forRoot({
|
|
|
25
25
|
});
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
+
## 驱动选择(driver,与运行时平台解耦)
|
|
29
|
+
|
|
30
|
+
> v3.2.0 新增。
|
|
31
|
+
|
|
32
|
+
默认情况下,Postgres/MySQL 连接的底层驱动由运行时平台决定:Bun 运行时使用内建 `Bun.SQL`,Node.js 运行时使用 `mysql2` / `postgres` 纯 JS 驱动。
|
|
33
|
+
|
|
34
|
+
通过可选的 `driver` 字段,可以**在 Bun 运行时下也显式选用纯 JS 驱动**,与平台引擎(fs/crypto/http server 等)解耦:
|
|
35
|
+
|
|
36
|
+
| driver | 行为 |
|
|
37
|
+
| --- | --- |
|
|
38
|
+
| `'auto'`(默认) | Bun → `Bun.SQL`;Node → `mysql2`(MySQL)/ `postgres`(PostgreSQL)。向后兼容。 |
|
|
39
|
+
| `'mysql2'` | 无论运行时如何,都使用 `mysql2`(仅 `type: 'mysql'`)。 |
|
|
40
|
+
| `'postgres'` | 无论运行时如何,都使用 `postgres`(仅 `type: 'postgres'`)。 |
|
|
41
|
+
| `'bun-sql'` | 强制使用 `Bun.SQL`(仅 Bun 合法,Node 会抛出清晰错误)。 |
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
DatabaseModule.forRoot({
|
|
45
|
+
database: {
|
|
46
|
+
type: 'mysql',
|
|
47
|
+
driver: 'mysql2', // 即使在 Bun 运行时也走 mysql2
|
|
48
|
+
config: {
|
|
49
|
+
host: '127.0.0.1',
|
|
50
|
+
port: 3306,
|
|
51
|
+
database: 'app',
|
|
52
|
+
user: 'root',
|
|
53
|
+
password: process.env.DB_PASSWORD!,
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
// V2 单租户写法同样支持:
|
|
59
|
+
DatabaseModule.forRoot({
|
|
60
|
+
type: 'mysql',
|
|
61
|
+
url: process.env.DB_URL!,
|
|
62
|
+
driver: 'mysql2',
|
|
63
|
+
});
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### 为什么需要它?`bun build --compile` 下的 MySQL
|
|
67
|
+
|
|
68
|
+
`bun build --compile` 产出的单二进制会把当时的 Bun 运行时焊死,其中内建 `Bun.SQL` 的 MySQL 适配带有若干已知问题。显式设置 `driver: 'mysql2'` 即可让编译产物使用静态打包的纯 JS `mysql2` 驱动连库,无需为此回退到 SQLite,也无需用 `BUN_SERVER_PLATFORM=node` 整体切平台(那会把 HTTP server 也切到 Node)。
|
|
69
|
+
|
|
70
|
+
`driver` 开关与 `BUN_SERVER_PLATFORM` 正交:前者只决定 SQL 驱动,后者决定整个平台引擎。
|
|
71
|
+
|
|
28
72
|
## 路由策略示例
|
|
29
73
|
|
|
30
74
|
```ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dangao/bun-server",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.3.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"test:bun": "bun test tests/platform/bun",
|
|
49
49
|
"test:node": "vitest run tests/platform/node",
|
|
50
50
|
"test:platform": "bun run test:bun && bun run test:node",
|
|
51
|
-
"
|
|
51
|
+
"typecheck": "tsc -p tsconfig.build.json --noEmit && tsc -p tsconfig.test.json --noEmit",
|
|
52
52
|
"clean": "rm -rf dist",
|
|
53
53
|
"bundle": "bun build src/index.ts --target=bun --format=esm --outfile=dist/index.js --external reflect-metadata --external @dangao/logsmith --external @dangao/nacos-client --external @vscode/sqlite3",
|
|
54
54
|
"bundle:node": "bun build src/index.ts --target=node --packages=external --outfile=dist/index.node.mjs",
|
|
@@ -88,4 +88,4 @@
|
|
|
88
88
|
"postgres": "^3.4.9",
|
|
89
89
|
"reflect-metadata": "^0.2.2"
|
|
90
90
|
}
|
|
91
|
-
}
|
|
91
|
+
}
|
package/src/ai/types.ts
CHANGED
|
@@ -144,6 +144,11 @@ export const AI_SERVICE_TOKEN = Symbol('@dangao/bun-server:ai:service');
|
|
|
144
144
|
export const AI_MODULE_OPTIONS_TOKEN = Symbol('@dangao/bun-server:ai:options');
|
|
145
145
|
export const AI_TOOL_REGISTRY_TOKEN = Symbol('@dangao/bun-server:ai:tool-registry');
|
|
146
146
|
|
|
147
|
+
/**
|
|
148
|
+
* Metadata key for AiModule options.
|
|
149
|
+
*/
|
|
150
|
+
export const MODULE_METADATA_KEY = '@dangao/bun-server:ai:module';
|
|
151
|
+
|
|
147
152
|
/**
|
|
148
153
|
* Metadata key for @AiTool decorator
|
|
149
154
|
*/
|
package/src/auth/types.ts
CHANGED
|
@@ -93,6 +93,10 @@ export interface OAuth2Client {
|
|
|
93
93
|
* 允许的授权类型
|
|
94
94
|
*/
|
|
95
95
|
grantTypes: ('authorization_code' | 'refresh_token')[];
|
|
96
|
+
/**
|
|
97
|
+
* 允许的 OAuth2 scope 列表
|
|
98
|
+
*/
|
|
99
|
+
scopes?: string[];
|
|
96
100
|
}
|
|
97
101
|
|
|
98
102
|
/**
|
|
@@ -245,4 +249,3 @@ export interface AuthContext {
|
|
|
245
249
|
*/
|
|
246
250
|
isAuthenticated: boolean;
|
|
247
251
|
}
|
|
248
|
-
|
|
@@ -91,8 +91,8 @@ export class CacheableInterceptor extends BaseInterceptor {
|
|
|
91
91
|
public async execute<T>(
|
|
92
92
|
target: unknown,
|
|
93
93
|
propertyKey: string | symbol,
|
|
94
|
-
originalMethod: (...args:
|
|
95
|
-
args:
|
|
94
|
+
originalMethod: (...args: any[]) => T | Promise<T>,
|
|
95
|
+
args: any[],
|
|
96
96
|
container: Container,
|
|
97
97
|
context?: Context,
|
|
98
98
|
): Promise<T> {
|
|
@@ -164,8 +164,8 @@ export class CacheEvictInterceptor extends BaseInterceptor {
|
|
|
164
164
|
public async execute<T>(
|
|
165
165
|
target: unknown,
|
|
166
166
|
propertyKey: string | symbol,
|
|
167
|
-
originalMethod: (...args:
|
|
168
|
-
args:
|
|
167
|
+
originalMethod: (...args: any[]) => T | Promise<T>,
|
|
168
|
+
args: any[],
|
|
169
169
|
container: Container,
|
|
170
170
|
context?: Context,
|
|
171
171
|
): Promise<T> {
|
|
@@ -238,8 +238,8 @@ export class CachePutInterceptor extends BaseInterceptor {
|
|
|
238
238
|
public async execute<T>(
|
|
239
239
|
target: unknown,
|
|
240
240
|
propertyKey: string | symbol,
|
|
241
|
-
originalMethod: (...args:
|
|
242
|
-
args:
|
|
241
|
+
originalMethod: (...args: any[]) => T | Promise<T>,
|
|
242
|
+
args: any[],
|
|
243
243
|
container: Container,
|
|
244
244
|
context?: Context,
|
|
245
245
|
): Promise<T> {
|
|
@@ -87,9 +87,9 @@ export class CacheServiceProxy {
|
|
|
87
87
|
|
|
88
88
|
// 返回包装后的方法
|
|
89
89
|
// 使用原型方法以确保拦截器能获取到正确的元数据
|
|
90
|
-
const originalMethod = prototypeMethod as (...args:
|
|
90
|
+
const originalMethod = prototypeMethod as (...args: any[]) => unknown;
|
|
91
91
|
|
|
92
|
-
return async function (this: T, ...args:
|
|
92
|
+
return async function (this: T, ...args: any[]): Promise<unknown> {
|
|
93
93
|
// 按优先级执行拦截器:CacheEvict (beforeInvocation) -> Cacheable/CachePut -> CacheEvict (afterInvocation)
|
|
94
94
|
|
|
95
95
|
// 如果有 @CacheEvict 且配置了 beforeInvocation
|
package/src/cache/service.ts
CHANGED
|
@@ -13,11 +13,14 @@ export class CacheService {
|
|
|
13
13
|
private keyPrefix: string;
|
|
14
14
|
|
|
15
15
|
public constructor(
|
|
16
|
-
@Inject(CACHE_OPTIONS_TOKEN) options: CacheModuleOptions,
|
|
16
|
+
@Inject(CACHE_OPTIONS_TOKEN) options: CacheModuleOptions | CacheStore,
|
|
17
17
|
) {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
const resolvedOptions: CacheModuleOptions = isCacheStore(options)
|
|
19
|
+
? { store: options }
|
|
20
|
+
: options;
|
|
21
|
+
this.store = resolvedOptions.store!;
|
|
22
|
+
this.defaultTtl = resolvedOptions.defaultTtl ?? 3600000; // 1 小时
|
|
23
|
+
this.keyPrefix = resolvedOptions.keyPrefix ?? '';
|
|
21
24
|
}
|
|
22
25
|
|
|
23
26
|
/**
|
|
@@ -25,7 +28,7 @@ export class CacheService {
|
|
|
25
28
|
* @param key - 缓存键
|
|
26
29
|
* @returns 缓存值,如果不存在则返回 undefined
|
|
27
30
|
*/
|
|
28
|
-
public async get<T =
|
|
31
|
+
public async get<T = any>(key: string): Promise<T | undefined> {
|
|
29
32
|
return this.store.get<T>(this.getKey(key));
|
|
30
33
|
}
|
|
31
34
|
|
|
@@ -36,7 +39,7 @@ export class CacheService {
|
|
|
36
39
|
* @param ttl - 过期时间(毫秒),0 表示永不过期,undefined 使用默认 TTL
|
|
37
40
|
* @returns 是否设置成功
|
|
38
41
|
*/
|
|
39
|
-
public async set<T =
|
|
42
|
+
public async set<T = any>(
|
|
40
43
|
key: string,
|
|
41
44
|
value: T,
|
|
42
45
|
ttl?: number,
|
|
@@ -76,7 +79,7 @@ export class CacheService {
|
|
|
76
79
|
* @param keys - 缓存键数组
|
|
77
80
|
* @returns 缓存值映射
|
|
78
81
|
*/
|
|
79
|
-
public async getMany<T =
|
|
82
|
+
public async getMany<T = any>(
|
|
80
83
|
keys: string[],
|
|
81
84
|
): Promise<Map<string, T>> {
|
|
82
85
|
const prefixedKeys = keys.map((k) => this.getKey(k));
|
|
@@ -97,7 +100,7 @@ export class CacheService {
|
|
|
97
100
|
* @param ttl - 过期时间(毫秒),0 表示永不过期,undefined 使用默认 TTL
|
|
98
101
|
* @returns 是否设置成功
|
|
99
102
|
*/
|
|
100
|
-
public async setMany<T =
|
|
103
|
+
public async setMany<T = any>(
|
|
101
104
|
entries: Array<{ key: string; value: T }>,
|
|
102
105
|
ttl?: number,
|
|
103
106
|
): Promise<boolean> {
|
|
@@ -149,3 +152,9 @@ export class CacheService {
|
|
|
149
152
|
return this.keyPrefix ? `${this.keyPrefix}${key}` : key;
|
|
150
153
|
}
|
|
151
154
|
}
|
|
155
|
+
|
|
156
|
+
function isCacheStore(value: CacheModuleOptions | CacheStore): value is CacheStore {
|
|
157
|
+
return typeof (value as CacheStore).get === 'function' &&
|
|
158
|
+
typeof (value as CacheStore).set === 'function' &&
|
|
159
|
+
typeof (value as CacheStore).delete === 'function';
|
|
160
|
+
}
|
package/src/cache/types.ts
CHANGED
|
@@ -7,7 +7,7 @@ export interface CacheStore {
|
|
|
7
7
|
* @param key - 缓存键
|
|
8
8
|
* @returns 缓存值,如果不存在则返回 undefined
|
|
9
9
|
*/
|
|
10
|
-
get<T =
|
|
10
|
+
get<T = any>(key: string): Promise<T | undefined>;
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* 设置缓存值
|
|
@@ -16,7 +16,7 @@ export interface CacheStore {
|
|
|
16
16
|
* @param ttl - 过期时间(毫秒),0 表示永不过期
|
|
17
17
|
* @returns 是否设置成功
|
|
18
18
|
*/
|
|
19
|
-
set<T =
|
|
19
|
+
set<T = any>(key: string, value: T, ttl?: number): Promise<boolean>;
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
22
|
* 删除缓存
|
|
@@ -43,7 +43,7 @@ export interface CacheStore {
|
|
|
43
43
|
* @param keys - 缓存键数组
|
|
44
44
|
* @returns 缓存值映射
|
|
45
45
|
*/
|
|
46
|
-
getMany<T =
|
|
46
|
+
getMany<T = any>(keys: string[]): Promise<Map<string, T>>;
|
|
47
47
|
|
|
48
48
|
/**
|
|
49
49
|
* 设置多个缓存值
|
|
@@ -51,7 +51,7 @@ export interface CacheStore {
|
|
|
51
51
|
* @param ttl - 过期时间(毫秒),0 表示永不过期
|
|
52
52
|
* @returns 是否设置成功
|
|
53
53
|
*/
|
|
54
|
-
setMany<T =
|
|
54
|
+
setMany<T = any>(
|
|
55
55
|
entries: Array<{ key: string; value: T }>,
|
|
56
56
|
ttl?: number,
|
|
57
57
|
): Promise<boolean>;
|
|
@@ -84,7 +84,7 @@ export class MemoryCacheStore implements CacheStore {
|
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
public async get<T =
|
|
87
|
+
public async get<T = any>(key: string): Promise<T | undefined> {
|
|
88
88
|
const entry = this.store.get(key);
|
|
89
89
|
if (!entry) {
|
|
90
90
|
return undefined;
|
|
@@ -99,7 +99,7 @@ export class MemoryCacheStore implements CacheStore {
|
|
|
99
99
|
return entry.value as T;
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
-
public async set<T =
|
|
102
|
+
public async set<T = any>(
|
|
103
103
|
key: string,
|
|
104
104
|
value: T,
|
|
105
105
|
ttl?: number,
|
|
@@ -133,7 +133,7 @@ export class MemoryCacheStore implements CacheStore {
|
|
|
133
133
|
return true;
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
-
public async getMany<T =
|
|
136
|
+
public async getMany<T = any>(
|
|
137
137
|
keys: string[],
|
|
138
138
|
): Promise<Map<string, T>> {
|
|
139
139
|
const result = new Map<string, T>();
|
|
@@ -154,7 +154,7 @@ export class MemoryCacheStore implements CacheStore {
|
|
|
154
154
|
return result;
|
|
155
155
|
}
|
|
156
156
|
|
|
157
|
-
public async setMany<T =
|
|
157
|
+
public async setMany<T = any>(
|
|
158
158
|
entries: Array<{ key: string; value: T }>,
|
|
159
159
|
ttl?: number,
|
|
160
160
|
): Promise<boolean> {
|
|
@@ -239,7 +239,7 @@ export class RedisCacheStore implements CacheStore {
|
|
|
239
239
|
return `${this.keyPrefix}${key}`;
|
|
240
240
|
}
|
|
241
241
|
|
|
242
|
-
public async get<T =
|
|
242
|
+
public async get<T = any>(key: string): Promise<T | undefined> {
|
|
243
243
|
const value = await this.client.get(this.getKey(key));
|
|
244
244
|
if (value === null) {
|
|
245
245
|
return undefined;
|
|
@@ -251,7 +251,7 @@ export class RedisCacheStore implements CacheStore {
|
|
|
251
251
|
}
|
|
252
252
|
}
|
|
253
253
|
|
|
254
|
-
public async set<T =
|
|
254
|
+
public async set<T = any>(
|
|
255
255
|
key: string,
|
|
256
256
|
value: T,
|
|
257
257
|
ttl?: number,
|
|
@@ -296,7 +296,7 @@ export class RedisCacheStore implements CacheStore {
|
|
|
296
296
|
}
|
|
297
297
|
}
|
|
298
298
|
|
|
299
|
-
public async getMany<T =
|
|
299
|
+
public async getMany<T = any>(
|
|
300
300
|
keys: string[],
|
|
301
301
|
): Promise<Map<string, T>> {
|
|
302
302
|
const result = new Map<string, T>();
|
|
@@ -325,7 +325,7 @@ export class RedisCacheStore implements CacheStore {
|
|
|
325
325
|
return result;
|
|
326
326
|
}
|
|
327
327
|
|
|
328
|
-
public async setMany<T =
|
|
328
|
+
public async setMany<T = any>(
|
|
329
329
|
entries: Array<{ key: string; value: T }>,
|
|
330
330
|
ttl?: number,
|
|
331
331
|
): Promise<boolean> {
|
package/src/config/service.ts
CHANGED
|
@@ -67,12 +67,12 @@ export class ConfigService<TConfig extends Record<string, unknown> = Record<stri
|
|
|
67
67
|
* 更新配置(用于配置中心动态刷新)
|
|
68
68
|
* @param newConfig - 新配置对象
|
|
69
69
|
*/
|
|
70
|
-
public updateConfig(newConfig: TConfig): void {
|
|
71
|
-
this.config = newConfig;
|
|
70
|
+
public updateConfig(newConfig: TConfig | Record<string, unknown>): void {
|
|
71
|
+
this.config = newConfig as TConfig;
|
|
72
72
|
// 通知所有监听器
|
|
73
73
|
for (const listener of this.configUpdateListeners) {
|
|
74
74
|
try {
|
|
75
|
-
listener(
|
|
75
|
+
listener(this.config);
|
|
76
76
|
} catch (error) {
|
|
77
77
|
console.error('[ConfigService] Error in config update listener:', error);
|
|
78
78
|
}
|
|
@@ -83,7 +83,7 @@ export class ConfigService<TConfig extends Record<string, unknown> = Record<stri
|
|
|
83
83
|
* 合并配置(用于配置中心增量更新)
|
|
84
84
|
* @param partialConfig - 部分配置对象
|
|
85
85
|
*/
|
|
86
|
-
public mergeConfig(partialConfig: Partial<TConfig>): void {
|
|
86
|
+
public mergeConfig(partialConfig: Partial<TConfig> | Record<string, unknown>): void {
|
|
87
87
|
this.config = {
|
|
88
88
|
...this.config,
|
|
89
89
|
...partialConfig,
|
|
@@ -125,6 +125,8 @@ export class ConfigService<TConfig extends Record<string, unknown> = Record<stri
|
|
|
125
125
|
* @param key - 配置键(如 "db.host")
|
|
126
126
|
* @param defaultValue - 默认值(可选)
|
|
127
127
|
*/
|
|
128
|
+
public get(key: string): any;
|
|
129
|
+
public get<T>(key: string, defaultValue?: T): T | undefined;
|
|
128
130
|
public get<T = unknown>(key: string, defaultValue?: T): T | undefined {
|
|
129
131
|
const namespacedKey = this.applyNamespace(key);
|
|
130
132
|
const value = this.getValueByPath(this.config, namespacedKey);
|
|
@@ -138,6 +140,8 @@ export class ConfigService<TConfig extends Record<string, unknown> = Record<stri
|
|
|
138
140
|
* 获取必需的配置值,如果不存在则抛出错误
|
|
139
141
|
* @param key - 配置键
|
|
140
142
|
*/
|
|
143
|
+
public getRequired(key: string): any;
|
|
144
|
+
public getRequired<T>(key: string): T;
|
|
141
145
|
public getRequired<T = unknown>(key: string): T {
|
|
142
146
|
const value = this.get<T>(key);
|
|
143
147
|
if (value === undefined) {
|
|
@@ -191,5 +195,3 @@ export class ConfigService<TConfig extends Record<string, unknown> = Record<stri
|
|
|
191
195
|
return current;
|
|
192
196
|
}
|
|
193
197
|
}
|
|
194
|
-
|
|
195
|
-
|
package/src/core/application.ts
CHANGED
|
@@ -490,6 +490,13 @@ export class Application {
|
|
|
490
490
|
return this.server;
|
|
491
491
|
}
|
|
492
492
|
|
|
493
|
+
/**
|
|
494
|
+
* 获取底层平台服务器实例
|
|
495
|
+
*/
|
|
496
|
+
public getNativeServer(): unknown {
|
|
497
|
+
return this.server?.getNativeServer();
|
|
498
|
+
}
|
|
499
|
+
|
|
493
500
|
/**
|
|
494
501
|
* 自动注册服务到注册中心
|
|
495
502
|
* 扫描所有使用 @ServiceRegistry 装饰器的控制器,自动注册服务
|
|
@@ -639,4 +646,3 @@ export class Application {
|
|
|
639
646
|
this.signalHandlersInstalled = false;
|
|
640
647
|
}
|
|
641
648
|
}
|
|
642
|
-
|
package/src/core/context.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { BodyParser } from '../request/body-parser';
|
|
2
2
|
import type { UploadedFileInfo } from '../files';
|
|
3
|
+
import type { Container } from '../di/container';
|
|
3
4
|
import type { BodyInit } from 'bun'
|
|
4
5
|
import { type URLSearchParams, URL } from 'url';
|
|
5
6
|
|
|
@@ -32,7 +33,7 @@ export class Context {
|
|
|
32
33
|
/**
|
|
33
34
|
* 请求路径
|
|
34
35
|
*/
|
|
35
|
-
public
|
|
36
|
+
public path: string;
|
|
36
37
|
|
|
37
38
|
/**
|
|
38
39
|
* 查询参数
|
|
@@ -80,8 +81,11 @@ export class Context {
|
|
|
80
81
|
*/
|
|
81
82
|
public readonly signal: AbortSignal;
|
|
82
83
|
|
|
83
|
-
public
|
|
84
|
+
public readonly container?: Container;
|
|
85
|
+
|
|
86
|
+
public constructor(request: Request, container?: Container) {
|
|
84
87
|
this.request = request;
|
|
88
|
+
this.container = container;
|
|
85
89
|
this.url = new URL(request.url);
|
|
86
90
|
this.method = request.method;
|
|
87
91
|
this.path = this.url.pathname;
|
|
@@ -304,4 +308,3 @@ export class Context {
|
|
|
304
308
|
return sanitized;
|
|
305
309
|
}
|
|
306
310
|
}
|
|
307
|
-
|
|
@@ -7,6 +7,7 @@ import type {
|
|
|
7
7
|
} from './types';
|
|
8
8
|
|
|
9
9
|
import { ConnectionPool } from './connection-pool';
|
|
10
|
+
import { healthCheckViaDriver } from './driver';
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* 数据库连接管理器
|
|
@@ -189,58 +190,16 @@ export class DatabaseConnectionManager {
|
|
|
189
190
|
}
|
|
190
191
|
|
|
191
192
|
/**
|
|
192
|
-
* PostgreSQL
|
|
193
|
+
* PostgreSQL 健康检查(按连接 driver tag 分流)
|
|
193
194
|
*/
|
|
194
195
|
private async healthCheckPostgres(connection: unknown): Promise<boolean> {
|
|
195
|
-
|
|
196
|
-
// Bun.SQL 对象可以作为函数调用(模板字符串)
|
|
197
|
-
if (connection && typeof connection === 'function') {
|
|
198
|
-
const result = await (connection as (template: TemplateStringsArray, ...values: unknown[]) => Promise<unknown[]>)`SELECT 1`;
|
|
199
|
-
return Array.isArray(result) && result.length > 0;
|
|
200
|
-
}
|
|
201
|
-
// 或者使用 query 方法(如果存在)
|
|
202
|
-
if (
|
|
203
|
-
connection &&
|
|
204
|
-
typeof connection === 'object' &&
|
|
205
|
-
'query' in connection &&
|
|
206
|
-
typeof connection.query === 'function'
|
|
207
|
-
) {
|
|
208
|
-
await (connection as { query: (sql: string) => Promise<unknown> }).query(
|
|
209
|
-
'SELECT 1',
|
|
210
|
-
);
|
|
211
|
-
return true;
|
|
212
|
-
}
|
|
213
|
-
return false;
|
|
214
|
-
} catch (_error) {
|
|
215
|
-
return false;
|
|
216
|
-
}
|
|
196
|
+
return await healthCheckViaDriver(connection);
|
|
217
197
|
}
|
|
218
198
|
|
|
219
199
|
/**
|
|
220
|
-
* MySQL
|
|
200
|
+
* MySQL 健康检查(按连接 driver tag 分流)
|
|
221
201
|
*/
|
|
222
202
|
private async healthCheckMysql(connection: unknown): Promise<boolean> {
|
|
223
|
-
|
|
224
|
-
// Bun.SQL 对象可以作为函数调用(模板字符串)
|
|
225
|
-
if (connection && typeof connection === 'function') {
|
|
226
|
-
const result = await (connection as (template: TemplateStringsArray, ...values: unknown[]) => Promise<unknown[]>)`SELECT 1`;
|
|
227
|
-
return Array.isArray(result) && result.length > 0;
|
|
228
|
-
}
|
|
229
|
-
// 或者使用 query 方法(如果存在)
|
|
230
|
-
if (
|
|
231
|
-
connection &&
|
|
232
|
-
typeof connection === 'object' &&
|
|
233
|
-
'query' in connection &&
|
|
234
|
-
typeof connection.query === 'function'
|
|
235
|
-
) {
|
|
236
|
-
await (connection as { query: (sql: string) => Promise<unknown> }).query(
|
|
237
|
-
'SELECT 1',
|
|
238
|
-
);
|
|
239
|
-
return true;
|
|
240
|
-
}
|
|
241
|
-
return false;
|
|
242
|
-
} catch (_error) {
|
|
243
|
-
return false;
|
|
244
|
-
}
|
|
203
|
+
return await healthCheckViaDriver(connection);
|
|
245
204
|
}
|
|
246
205
|
}
|
|
@@ -4,6 +4,12 @@ import type {
|
|
|
4
4
|
DatabaseType,
|
|
5
5
|
} from './types';
|
|
6
6
|
import { getRuntime } from '../platform/runtime';
|
|
7
|
+
import {
|
|
8
|
+
closeViaDriver,
|
|
9
|
+
createMysqlConnection,
|
|
10
|
+
createPostgresConnection,
|
|
11
|
+
resolveDriver,
|
|
12
|
+
} from './driver';
|
|
7
13
|
|
|
8
14
|
/**
|
|
9
15
|
* 连接池中的连接项
|
|
@@ -215,7 +221,7 @@ export class ConnectionPool {
|
|
|
215
221
|
}
|
|
216
222
|
|
|
217
223
|
/**
|
|
218
|
-
* 创建 PostgreSQL
|
|
224
|
+
* 创建 PostgreSQL 连接(按所选 driver 分流,与运行时平台解耦)
|
|
219
225
|
*/
|
|
220
226
|
private async createPostgresConnection(
|
|
221
227
|
config: {
|
|
@@ -227,18 +233,16 @@ export class ConnectionPool {
|
|
|
227
233
|
ssl?: boolean;
|
|
228
234
|
},
|
|
229
235
|
): Promise<unknown> {
|
|
230
|
-
const
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
const postgres = require('postgres') as typeof import('postgres');
|
|
237
|
-
return postgres(url, { max: 1, ssl: config.ssl ? 'require' : false });
|
|
236
|
+
const driver = resolveDriver(
|
|
237
|
+
'postgres',
|
|
238
|
+
this.config.type === 'postgres' ? this.config.driver : undefined,
|
|
239
|
+
getRuntime().engine,
|
|
240
|
+
);
|
|
241
|
+
return await createPostgresConnection(config, driver);
|
|
238
242
|
}
|
|
239
243
|
|
|
240
244
|
/**
|
|
241
|
-
* 创建 MySQL
|
|
245
|
+
* 创建 MySQL 连接(按所选 driver 分流,与运行时平台解耦)
|
|
242
246
|
*/
|
|
243
247
|
private async createMysqlConnection(
|
|
244
248
|
config: {
|
|
@@ -249,21 +253,12 @@ export class ConnectionPool {
|
|
|
249
253
|
password: string;
|
|
250
254
|
},
|
|
251
255
|
): Promise<unknown> {
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
const mysql2 = require('mysql2/promise') as typeof import('mysql2/promise');
|
|
259
|
-
const conn = await mysql2.createConnection({
|
|
260
|
-
host: config.host,
|
|
261
|
-
port: config.port,
|
|
262
|
-
database: config.database,
|
|
263
|
-
user: config.user,
|
|
264
|
-
password: config.password,
|
|
265
|
-
});
|
|
266
|
-
return conn;
|
|
256
|
+
const driver = resolveDriver(
|
|
257
|
+
'mysql',
|
|
258
|
+
this.config.type === 'mysql' ? this.config.driver : undefined,
|
|
259
|
+
getRuntime().engine,
|
|
260
|
+
);
|
|
261
|
+
return await createMysqlConnection(config, driver);
|
|
267
262
|
}
|
|
268
263
|
|
|
269
264
|
/**
|
|
@@ -295,35 +290,17 @@ export class ConnectionPool {
|
|
|
295
290
|
}
|
|
296
291
|
|
|
297
292
|
/**
|
|
298
|
-
* 关闭 PostgreSQL
|
|
293
|
+
* 关闭 PostgreSQL 连接(按连接 driver tag 分流:bun-sql → close(),postgres → end())
|
|
299
294
|
*/
|
|
300
|
-
private async closePostgresConnection(
|
|
301
|
-
|
|
302
|
-
// 如果需要强制关闭,可以调用 connection.close()
|
|
303
|
-
if (
|
|
304
|
-
_connection &&
|
|
305
|
-
typeof _connection === 'object' &&
|
|
306
|
-
'close' in _connection &&
|
|
307
|
-
typeof (_connection as { close: () => void }).close === 'function'
|
|
308
|
-
) {
|
|
309
|
-
(_connection as { close: () => void }).close();
|
|
310
|
-
}
|
|
295
|
+
private async closePostgresConnection(connection: unknown): Promise<void> {
|
|
296
|
+
await closeViaDriver(connection);
|
|
311
297
|
}
|
|
312
298
|
|
|
313
299
|
/**
|
|
314
|
-
* 关闭 MySQL
|
|
300
|
+
* 关闭 MySQL 连接(按连接 driver tag 分流:bun-sql → close(),mysql2 → end())
|
|
315
301
|
*/
|
|
316
|
-
private async closeMysqlConnection(
|
|
317
|
-
|
|
318
|
-
// 如果需要强制关闭,可以调用 connection.close()
|
|
319
|
-
if (
|
|
320
|
-
_connection &&
|
|
321
|
-
typeof _connection === 'object' &&
|
|
322
|
-
'close' in _connection &&
|
|
323
|
-
typeof (_connection as { close: () => void }).close === 'function'
|
|
324
|
-
) {
|
|
325
|
-
(_connection as { close: () => void }).close();
|
|
326
|
-
}
|
|
302
|
+
private async closeMysqlConnection(connection: unknown): Promise<void> {
|
|
303
|
+
await closeViaDriver(connection);
|
|
327
304
|
}
|
|
328
305
|
|
|
329
306
|
/**
|