@dangao/bun-server 1.9.0 → 1.12.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/README.md +79 -6
- package/dist/cache/cache-module.d.ts +6 -0
- package/dist/cache/cache-module.d.ts.map +1 -1
- package/dist/client/generator.d.ts +16 -0
- package/dist/client/generator.d.ts.map +1 -0
- package/dist/client/index.d.ts +4 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/runtime.d.ts +15 -0
- package/dist/client/runtime.d.ts.map +1 -0
- package/dist/client/types.d.ts +36 -0
- package/dist/client/types.d.ts.map +1 -0
- package/dist/config/config-module.d.ts +7 -0
- package/dist/config/config-module.d.ts.map +1 -1
- package/dist/config/index.d.ts +1 -1
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/service.d.ts +13 -0
- package/dist/config/service.d.ts.map +1 -1
- package/dist/config/types.d.ts +10 -0
- package/dist/config/types.d.ts.map +1 -1
- package/dist/core/application.d.ts +7 -0
- package/dist/core/application.d.ts.map +1 -1
- package/dist/core/apply-decorators.d.ts +6 -0
- package/dist/core/apply-decorators.d.ts.map +1 -0
- package/dist/core/cluster.d.ts +47 -0
- package/dist/core/cluster.d.ts.map +1 -0
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/server.d.ts +8 -0
- package/dist/core/server.d.ts.map +1 -1
- package/dist/dashboard/controller.d.ts +55 -0
- package/dist/dashboard/controller.d.ts.map +1 -0
- package/dist/dashboard/dashboard-extension.d.ts +20 -0
- package/dist/dashboard/dashboard-extension.d.ts.map +1 -0
- package/dist/dashboard/dashboard-module.d.ts +13 -0
- package/dist/dashboard/dashboard-module.d.ts.map +1 -0
- package/dist/dashboard/index.d.ts +4 -0
- package/dist/dashboard/index.d.ts.map +1 -0
- package/dist/dashboard/types.d.ts +16 -0
- package/dist/dashboard/types.d.ts.map +1 -0
- package/dist/dashboard/ui.d.ts +7 -0
- package/dist/dashboard/ui.d.ts.map +1 -0
- package/dist/database/database-module.d.ts +7 -0
- package/dist/database/database-module.d.ts.map +1 -1
- package/dist/debug/debug-module.d.ts +13 -0
- package/dist/debug/debug-module.d.ts.map +1 -0
- package/dist/debug/debug-ui-middleware.d.ts +8 -0
- package/dist/debug/debug-ui-middleware.d.ts.map +1 -0
- package/dist/debug/index.d.ts +5 -0
- package/dist/debug/index.d.ts.map +1 -0
- package/dist/debug/middleware.d.ts +12 -0
- package/dist/debug/middleware.d.ts.map +1 -0
- package/dist/debug/recorder.d.ts +61 -0
- package/dist/debug/recorder.d.ts.map +1 -0
- package/dist/debug/types.d.ts +48 -0
- package/dist/debug/types.d.ts.map +1 -0
- package/dist/debug/ui.d.ts +6 -0
- package/dist/debug/ui.d.ts.map +1 -0
- package/dist/di/async-module.d.ts +49 -0
- package/dist/di/async-module.d.ts.map +1 -0
- package/dist/di/lifecycle.d.ts +49 -0
- package/dist/di/lifecycle.d.ts.map +1 -0
- package/dist/di/module-registry.d.ts +24 -0
- package/dist/di/module-registry.d.ts.map +1 -1
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1887 -35
- package/dist/router/route.d.ts +5 -7
- package/dist/router/route.d.ts.map +1 -1
- package/dist/swagger/generator.d.ts +10 -0
- package/dist/swagger/generator.d.ts.map +1 -1
- package/dist/testing/test-client.d.ts +49 -0
- package/dist/testing/test-client.d.ts.map +1 -0
- package/dist/testing/testing-module.d.ts +90 -0
- package/dist/testing/testing-module.d.ts.map +1 -0
- package/dist/websocket/registry.d.ts +1 -6
- package/dist/websocket/registry.d.ts.map +1 -1
- package/docs/async-module.md +59 -0
- package/docs/client-generation.md +100 -0
- package/docs/cluster.md +81 -0
- package/docs/custom-decorators.md +1 -7
- package/docs/dashboard.md +54 -0
- package/docs/debug.md +58 -0
- package/docs/extensions.md +0 -2
- package/docs/guide.md +0 -1
- package/docs/lifecycle.md +72 -0
- package/docs/testing.md +110 -0
- package/docs/zh/async-module.md +98 -0
- package/docs/zh/client-generation.md +92 -0
- package/docs/zh/cluster.md +74 -0
- package/docs/zh/custom-decorators.md +1 -7
- package/docs/zh/dashboard.md +69 -0
- package/docs/zh/debug.md +81 -0
- package/docs/zh/extensions.md +0 -2
- package/docs/zh/guide.md +0 -1
- package/docs/zh/lifecycle.md +87 -0
- package/docs/zh/migration.md +0 -5
- package/docs/zh/testing.md +119 -0
- package/package.json +4 -4
- package/src/cache/cache-module.ts +25 -0
- package/src/client/generator.ts +36 -0
- package/src/client/index.ts +8 -0
- package/src/client/runtime.ts +101 -0
- package/src/client/types.ts +38 -0
- package/src/config/config-module.ts +44 -4
- package/src/config/index.ts +1 -0
- package/src/config/service.ts +50 -0
- package/src/config/types.ts +12 -0
- package/src/core/application.ts +37 -0
- package/src/core/apply-decorators.ts +31 -0
- package/src/core/cluster.ts +143 -0
- package/src/core/index.ts +1 -0
- package/src/core/server.ts +14 -1
- package/src/dashboard/controller.ts +227 -0
- package/src/dashboard/dashboard-extension.ts +26 -0
- package/src/dashboard/dashboard-module.ts +38 -0
- package/src/dashboard/index.ts +3 -0
- package/src/dashboard/types.ts +16 -0
- package/src/dashboard/ui.ts +219 -0
- package/src/database/database-module.ts +20 -0
- package/src/debug/debug-module.ts +70 -0
- package/src/debug/debug-ui-middleware.ts +110 -0
- package/src/debug/index.ts +9 -0
- package/src/debug/middleware.ts +126 -0
- package/src/debug/recorder.ts +141 -0
- package/src/debug/types.ts +49 -0
- package/src/debug/ui.ts +393 -0
- package/src/di/async-module.ts +141 -0
- package/src/di/lifecycle.ts +117 -0
- package/src/di/module-registry.ts +75 -0
- package/src/index.ts +35 -0
- package/src/router/route.ts +20 -20
- package/src/swagger/generator.ts +100 -0
- package/src/testing/test-client.ts +112 -0
- package/src/testing/testing-module.ts +238 -0
- package/src/websocket/registry.ts +3 -16
- package/tests/auth/auth-decorators.test.ts +0 -1
- package/tests/auth/oauth2-service.test.ts +0 -1
- package/tests/cache/cache-decorators-extended.test.ts +0 -1
- package/tests/cache/cache-decorators.test.ts +0 -1
- package/tests/cache/cache-interceptors.test.ts +0 -1
- package/tests/cache/cache-module.test.ts +0 -1
- package/tests/cache/cache-service-proxy.test.ts +0 -1
- package/tests/client/client-generator.test.ts +142 -0
- package/tests/config/config-center-integration.test.ts +0 -1
- package/tests/config/config-module-extended.test.ts +0 -1
- package/tests/config/config-module.test.ts +0 -1
- package/tests/controller/controller.test.ts +0 -1
- package/tests/controller/param-binder.test.ts +0 -1
- package/tests/controller/path-combination.test.ts +0 -1
- package/tests/core/application.test.ts +34 -0
- package/tests/core/apply-decorators.test.ts +109 -0
- package/tests/core/cluster.test.ts +32 -0
- package/tests/dashboard/dashboard-module.test.ts +85 -0
- package/tests/database/database-module.test.ts +0 -1
- package/tests/database/orm.test.ts +0 -1
- package/tests/database/postgres-mysql-integration.test.ts +0 -1
- package/tests/database/transaction.test.ts +0 -1
- package/tests/debug/debug-module.test.ts +141 -0
- package/tests/di/async-module.test.ts +125 -0
- package/tests/di/container.test.ts +0 -1
- package/tests/di/lifecycle.test.ts +140 -0
- package/tests/error/error-handler.test.ts +0 -1
- package/tests/events/event-decorators.test.ts +0 -1
- package/tests/events/event-listener-scanner.test.ts +0 -1
- package/tests/events/event-module.test.ts +0 -1
- package/tests/extensions/logger-module.test.ts +0 -1
- package/tests/health/health-module.test.ts +0 -1
- package/tests/integration/oauth2-e2e.test.ts +0 -1
- package/tests/integration/session-e2e.test.ts +0 -1
- package/tests/interceptor/base-interceptor.test.ts +0 -1
- package/tests/interceptor/builtin/cache-interceptor.test.ts +0 -1
- package/tests/interceptor/builtin/log-interceptor.test.ts +0 -1
- package/tests/interceptor/builtin/permission-interceptor.test.ts +0 -1
- package/tests/interceptor/interceptor-advanced-integration.test.ts +0 -1
- package/tests/interceptor/interceptor-chain.test.ts +0 -1
- package/tests/interceptor/interceptor-integration.test.ts +0 -1
- package/tests/interceptor/interceptor-metadata.test.ts +0 -1
- package/tests/interceptor/interceptor-registry.test.ts +0 -1
- package/tests/interceptor/perf/interceptor-performance.test.ts +0 -1
- package/tests/metrics/metrics-module.test.ts +0 -1
- package/tests/microservice/config-center.test.ts +0 -1
- package/tests/microservice/service-client-decorators.test.ts +0 -1
- package/tests/microservice/service-registry-decorators.test.ts +0 -1
- package/tests/microservice/service-registry.test.ts +0 -1
- package/tests/middleware/builtin/middleware-builtin-extended.test.ts +0 -1
- package/tests/middleware/builtin/rate-limit.test.ts +0 -1
- package/tests/middleware/middleware-decorators.test.ts +0 -1
- package/tests/middleware/middleware-pipeline.test.ts +0 -1
- package/tests/middleware/middleware.test.ts +0 -1
- package/tests/perf/optimization.test.ts +0 -1
- package/tests/queue/queue-decorators.test.ts +0 -1
- package/tests/queue/queue-module.test.ts +0 -1
- package/tests/queue/queue-service.test.ts +0 -1
- package/tests/router/router-decorators.test.ts +0 -1
- package/tests/router/router-extended.test.ts +0 -1
- package/tests/security/guards/guards-integration.test.ts +0 -1
- package/tests/security/guards/guards.test.ts +0 -1
- package/tests/security/guards/reflector.test.ts +0 -1
- package/tests/security/security-filter.test.ts +0 -1
- package/tests/security/security-module-extended.test.ts +0 -1
- package/tests/security/security-module.test.ts +0 -1
- package/tests/session/session-decorators.test.ts +0 -1
- package/tests/session/session-module.test.ts +0 -1
- package/tests/swagger/decorators.test.ts +0 -1
- package/tests/swagger/swagger-module.test.ts +0 -1
- package/tests/swagger/ui.test.ts +0 -1
- package/tests/testing/testing-module.test.ts +129 -0
- package/tests/validation/class-validator.test.ts +0 -1
- package/tests/validation/controller-validation.test.ts +0 -1
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Lifecycle Hooks
|
|
2
|
+
|
|
3
|
+
Bun Server supports lifecycle hooks that let providers participate in application startup and shutdown. Implement the appropriate interface on your injectable services.
|
|
4
|
+
|
|
5
|
+
## Interfaces
|
|
6
|
+
|
|
7
|
+
| Interface | Method | When Called |
|
|
8
|
+
|-----------|--------|-------------|
|
|
9
|
+
| `OnModuleInit` | `onModuleInit()` | After all module providers are registered |
|
|
10
|
+
| `OnModuleDestroy` | `onModuleDestroy()` | During shutdown (reverse order) |
|
|
11
|
+
| `OnApplicationBootstrap` | `onApplicationBootstrap()` | After all modules init, before server listens |
|
|
12
|
+
| `OnApplicationShutdown` | `onApplicationShutdown(signal?)` | When graceful shutdown begins |
|
|
13
|
+
|
|
14
|
+
## Execution Order
|
|
15
|
+
|
|
16
|
+
**Startup**: `onModuleInit` (all modules) -> `onApplicationBootstrap` (all modules) -> server starts
|
|
17
|
+
|
|
18
|
+
**Shutdown**: `onApplicationShutdown` (reverse order) -> `onModuleDestroy` (reverse order)
|
|
19
|
+
|
|
20
|
+
## Example: DatabaseService with init/destroy
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
import {
|
|
24
|
+
Injectable,
|
|
25
|
+
OnModuleInit,
|
|
26
|
+
OnModuleDestroy,
|
|
27
|
+
} from '@dangao/bun-server';
|
|
28
|
+
|
|
29
|
+
@Injectable()
|
|
30
|
+
class DatabaseService implements OnModuleInit, OnModuleDestroy {
|
|
31
|
+
private connected = false;
|
|
32
|
+
|
|
33
|
+
public async onModuleInit(): Promise<void> {
|
|
34
|
+
console.log('[DatabaseService] Connecting...');
|
|
35
|
+
await this.connect();
|
|
36
|
+
this.connected = true;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
public async onModuleDestroy(): Promise<void> {
|
|
40
|
+
console.log('[DatabaseService] Closing connection...');
|
|
41
|
+
await this.disconnect();
|
|
42
|
+
this.connected = false;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
public isConnected(): boolean {
|
|
46
|
+
return this.connected;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
private async connect(): Promise<void> {
|
|
50
|
+
// Open DB connection
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
private async disconnect(): Promise<void> {
|
|
54
|
+
// Close DB connection
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Example: Application-level hooks
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
@Injectable()
|
|
63
|
+
class AppService implements OnApplicationBootstrap, OnApplicationShutdown {
|
|
64
|
+
public onApplicationBootstrap(): void {
|
|
65
|
+
console.log('Application bootstrapped');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
public onApplicationShutdown(signal?: string): void {
|
|
69
|
+
console.log(`Shutting down (signal: ${signal ?? 'none'})`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
```
|
package/docs/testing.md
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# Testing Module
|
|
2
|
+
|
|
3
|
+
The Testing Module provides utilities for unit and integration testing of Bun Server applications. Use `Test.createTestingModule()` to build an isolated test environment with provider overrides.
|
|
4
|
+
|
|
5
|
+
## API Overview
|
|
6
|
+
|
|
7
|
+
### Test.createTestingModule()
|
|
8
|
+
|
|
9
|
+
Creates a test module builder with the given module metadata (controllers, providers, imports).
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
const builder = Test.createTestingModule({
|
|
13
|
+
controllers: [UserController],
|
|
14
|
+
providers: [UserService],
|
|
15
|
+
});
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
### overrideProvider().useValue() / .useClass() / .useFactory()
|
|
19
|
+
|
|
20
|
+
Override a provider with a mock or alternative implementation:
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
const module = await Test.createTestingModule({
|
|
24
|
+
controllers: [GreetController],
|
|
25
|
+
providers: [{ provide: GREETER_TOKEN, useClass: RealGreeter }],
|
|
26
|
+
})
|
|
27
|
+
.overrideProvider(GREETER_TOKEN)
|
|
28
|
+
.useValue(mockGreeter) // or .useClass(MockGreeter) or .useFactory(() => mock)
|
|
29
|
+
.compile();
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
- **useValue**: Replace with a fixed value (e.g. mock object)
|
|
33
|
+
- **useClass**: Replace with an alternative class
|
|
34
|
+
- **useFactory**: Replace with a factory function
|
|
35
|
+
|
|
36
|
+
### TestingModule.get()
|
|
37
|
+
|
|
38
|
+
Resolve a provider from the DI container:
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
const service = module.get(UserService);
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### TestingModule.createApplication()
|
|
45
|
+
|
|
46
|
+
Create an `Application` instance with all providers and controllers registered. Does not start the server.
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
const app = module.createApplication({ port: 0 });
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### TestingModule.createHttpClient()
|
|
53
|
+
|
|
54
|
+
Creates an `Application`, starts it on a random port, and returns a `TestHttpClient` for HTTP requests.
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
const client = await module.createHttpClient();
|
|
58
|
+
const res = await client.get('/api/users');
|
|
59
|
+
await client.close();
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### TestHttpClient Methods
|
|
63
|
+
|
|
64
|
+
| Method | Description |
|
|
65
|
+
|--------|-------------|
|
|
66
|
+
| `get(path, options?)` | GET request |
|
|
67
|
+
| `post(path, options?)` | POST request |
|
|
68
|
+
| `put(path, options?)` | PUT request |
|
|
69
|
+
| `delete(path, options?)` | DELETE request |
|
|
70
|
+
| `patch(path, options?)` | PATCH request |
|
|
71
|
+
| `close()` | Stop the test server |
|
|
72
|
+
|
|
73
|
+
Options support `headers`, `body`, and `query`.
|
|
74
|
+
|
|
75
|
+
## Example with bun:test
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
import { describe, expect, test } from 'bun:test';
|
|
79
|
+
import { Test, Controller, GET, Inject } from '@dangao/bun-server';
|
|
80
|
+
|
|
81
|
+
const GREETER_TOKEN = Symbol('Greeter');
|
|
82
|
+
|
|
83
|
+
@Controller('/api')
|
|
84
|
+
class GreetController {
|
|
85
|
+
public constructor(@Inject(GREETER_TOKEN) private greeter: { greet: (s: string) => string }) {}
|
|
86
|
+
|
|
87
|
+
@GET('/greet')
|
|
88
|
+
public greet(): object {
|
|
89
|
+
return { message: this.greeter.greet('World') };
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
describe('GreetController', () => {
|
|
94
|
+
test('should return mock greeting', async () => {
|
|
95
|
+
const mockGreeter = { greet: (name: string) => `Mock: ${name}` };
|
|
96
|
+
|
|
97
|
+
const module = await Test.createTestingModule({
|
|
98
|
+
controllers: [GreetController],
|
|
99
|
+
providers: [{ provide: GREETER_TOKEN, useValue: mockGreeter }],
|
|
100
|
+
}).compile();
|
|
101
|
+
|
|
102
|
+
const client = await module.createHttpClient();
|
|
103
|
+
const res = await client.get('/api/greet');
|
|
104
|
+
await client.close();
|
|
105
|
+
|
|
106
|
+
expect(res.status).toBe(200);
|
|
107
|
+
expect(res.body).toEqual({ message: 'Mock: World' });
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
```
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# 异步模块配置
|
|
2
|
+
|
|
3
|
+
当模块配置需要异步加载(如远程配置中心、密钥库、数据库连接等)时,可使用 `forRootAsync()` 模式。
|
|
4
|
+
|
|
5
|
+
## AsyncModuleOptions 接口
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
interface AsyncModuleOptions<T> {
|
|
9
|
+
imports?: ModuleClass[]; // 需要导入的模块(提供 inject 依赖)
|
|
10
|
+
inject?: ProviderToken[]; // 注入到 useFactory 的 provider tokens
|
|
11
|
+
useFactory: (...deps: unknown[]) => T | Promise<T>; // 异步工厂函数
|
|
12
|
+
}
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## 支持的模块
|
|
16
|
+
|
|
17
|
+
- **ConfigModule**:异步加载配置
|
|
18
|
+
- **DatabaseModule**:异步配置数据库连接
|
|
19
|
+
- **CacheModule**:异步配置缓存(如 Redis 连接)
|
|
20
|
+
|
|
21
|
+
## 示例:ConfigModule.forRootAsync
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import {
|
|
25
|
+
Application,
|
|
26
|
+
ConfigModule,
|
|
27
|
+
ConfigService,
|
|
28
|
+
CONFIG_SERVICE_TOKEN,
|
|
29
|
+
Controller,
|
|
30
|
+
GET,
|
|
31
|
+
Inject,
|
|
32
|
+
Module,
|
|
33
|
+
} from '@dangao/bun-server';
|
|
34
|
+
|
|
35
|
+
async function loadRemoteConfig(): Promise<{ app: { name: string; port: number } }> {
|
|
36
|
+
console.log('[Config] 正在加载远程配置...');
|
|
37
|
+
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
38
|
+
return {
|
|
39
|
+
app: { name: 'AsyncConfigApp', port: 3000 },
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
@Controller('/api')
|
|
44
|
+
class AppController {
|
|
45
|
+
public constructor(
|
|
46
|
+
@Inject(CONFIG_SERVICE_TOKEN) private readonly config: ConfigService,
|
|
47
|
+
) {}
|
|
48
|
+
|
|
49
|
+
@GET('/config')
|
|
50
|
+
public getConfig(): object {
|
|
51
|
+
return {
|
|
52
|
+
name: this.config.get('app.name'),
|
|
53
|
+
port: this.config.get('app.port'),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
@Module({
|
|
59
|
+
imports: [
|
|
60
|
+
ConfigModule.forRootAsync({
|
|
61
|
+
useFactory: async () => {
|
|
62
|
+
const remoteConfig = await loadRemoteConfig();
|
|
63
|
+
return { defaultConfig: remoteConfig };
|
|
64
|
+
},
|
|
65
|
+
}),
|
|
66
|
+
],
|
|
67
|
+
controllers: [AppController],
|
|
68
|
+
})
|
|
69
|
+
class AppModule {}
|
|
70
|
+
|
|
71
|
+
const app = new Application({ port: 3000 });
|
|
72
|
+
app.registerModule(AppModule);
|
|
73
|
+
await app.listen();
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## 示例:DatabaseModule.forRootAsync 配合 ConfigService
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
import { ConfigModule, CONFIG_SERVICE_TOKEN } from '@dangao/bun-server';
|
|
80
|
+
import type { ConfigService } from '@dangao/bun-server';
|
|
81
|
+
|
|
82
|
+
@Module({
|
|
83
|
+
imports: [
|
|
84
|
+
ConfigModule.forRoot({ defaultConfig: { db: { url: '...' } } }),
|
|
85
|
+
DatabaseModule.forRootAsync({
|
|
86
|
+
imports: [ConfigModule],
|
|
87
|
+
inject: [CONFIG_SERVICE_TOKEN],
|
|
88
|
+
useFactory: (config: ConfigService) => ({
|
|
89
|
+
url: config.get<string>('db.url'),
|
|
90
|
+
// 其他数据库配置
|
|
91
|
+
}),
|
|
92
|
+
}),
|
|
93
|
+
],
|
|
94
|
+
})
|
|
95
|
+
class AppModule {}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
异步 provider 在 `Application.listen()` 期间顺序初始化,确保在应用开始处理请求前配置已就绪。
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# 类型安全客户端生成
|
|
2
|
+
|
|
3
|
+
Bun Server 支持从服务端路由自动生成类型安全的 API 客户端,便于前端或微服务调用。
|
|
4
|
+
|
|
5
|
+
## ClientGenerator.generate()
|
|
6
|
+
|
|
7
|
+
从当前已注册的 `RouteRegistry` 提取路由清单:
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { ClientGenerator } from '@dangao/bun-server';
|
|
11
|
+
|
|
12
|
+
const manifest = ClientGenerator.generate();
|
|
13
|
+
// { routes: [{ method, path, controllerName, methodName }, ...] }
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## createClient(manifest, config)
|
|
17
|
+
|
|
18
|
+
根据清单和配置创建 API 客户端:
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
import { createClient } from '@dangao/bun-server';
|
|
22
|
+
|
|
23
|
+
const client = createClient(manifest, {
|
|
24
|
+
baseUrl: 'http://localhost:3001',
|
|
25
|
+
headers: { 'X-API-Key': 'xxx' },
|
|
26
|
+
});
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## 客户端结构
|
|
30
|
+
|
|
31
|
+
客户端按控制器名称分组(去除 `Controller` 后缀并首字母小写),每个方法名对应一个函数:
|
|
32
|
+
|
|
33
|
+
- `UserController` → `client.user`
|
|
34
|
+
- `getUser` → `client.user.getUser({ params: { id: '1' } })`
|
|
35
|
+
|
|
36
|
+
## 完整示例
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
import {
|
|
40
|
+
Application,
|
|
41
|
+
Controller,
|
|
42
|
+
GET,
|
|
43
|
+
POST,
|
|
44
|
+
Param,
|
|
45
|
+
Body,
|
|
46
|
+
Module,
|
|
47
|
+
ClientGenerator,
|
|
48
|
+
createClient,
|
|
49
|
+
} from '@dangao/bun-server';
|
|
50
|
+
|
|
51
|
+
@Controller('/api/users')
|
|
52
|
+
class UserController {
|
|
53
|
+
@GET('/')
|
|
54
|
+
public listUsers(): object {
|
|
55
|
+
return [{ id: '1', name: 'Alice' }, { id: '2', name: 'Bob' }];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
@GET('/:id')
|
|
59
|
+
public getUser(@Param('id') id: string): object {
|
|
60
|
+
return { id, name: 'Alice' };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
@POST('/')
|
|
64
|
+
public createUser(@Body() body: unknown): object {
|
|
65
|
+
return { ...(body as object), id: '3' };
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
@Module({ controllers: [UserController] })
|
|
70
|
+
class AppModule {}
|
|
71
|
+
|
|
72
|
+
const app = new Application({ port: 3001, enableSignalHandlers: false });
|
|
73
|
+
app.registerModule(AppModule);
|
|
74
|
+
await app.listen();
|
|
75
|
+
|
|
76
|
+
// 从已注册控制器生成路由清单
|
|
77
|
+
const manifest = ClientGenerator.generate();
|
|
78
|
+
|
|
79
|
+
// 创建类型安全客户端
|
|
80
|
+
const client = createClient(manifest, {
|
|
81
|
+
baseUrl: 'http://localhost:3001',
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// 调用 API
|
|
85
|
+
const users = await client.user.listUsers();
|
|
86
|
+
const user = await client.user.getUser({ params: { id: '42' } });
|
|
87
|
+
const newUser = await client.user.createUser({ body: { name: 'Charlie' } });
|
|
88
|
+
|
|
89
|
+
await app.stop();
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
`ClientRequestOptions` 支持 `params`、`query`、`body`、`headers`,路径中的 `:id` 等占位符会通过 `params` 自动替换。
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# 零配置集群
|
|
2
|
+
|
|
3
|
+
`ClusterManager` 提供零配置集群模式,自动派生多个 worker 进程,利用多核 CPU 提升吞吐量。
|
|
4
|
+
|
|
5
|
+
## ClusterManager 类
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import { ClusterManager } from '@dangao/bun-server';
|
|
9
|
+
|
|
10
|
+
const manager = new ClusterManager({
|
|
11
|
+
workers: 'auto', // 或具体数字,'auto' 表示 CPU 核心数
|
|
12
|
+
scriptPath: import.meta.path,
|
|
13
|
+
port: 3300,
|
|
14
|
+
hostname: '0.0.0.0', // 可选
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
manager.start();
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## 主进程 / 工作进程
|
|
21
|
+
|
|
22
|
+
- **ClusterManager.isWorker()**:判断当前进程是否为 worker
|
|
23
|
+
- **ClusterManager.getWorkerId()**:获取当前 worker ID(仅 worker 进程有效,主进程返回 -1)
|
|
24
|
+
|
|
25
|
+
主进程负责派生 worker,worker 进程运行应用逻辑。每个 worker 使用 `reusePort` 绑定同一端口。
|
|
26
|
+
|
|
27
|
+
## Linux reusePort 限制
|
|
28
|
+
|
|
29
|
+
`reusePort`(SO_REUSEPORT)仅在 **Linux** 上有效。macOS 和 Windows 会忽略该选项,多 worker 将无法共享同一端口,需在对应系统上单独验证行为。
|
|
30
|
+
|
|
31
|
+
## 使用示例
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
import { ClusterManager, Application, Controller, GET, Module } from '@dangao/bun-server';
|
|
35
|
+
|
|
36
|
+
const PORT = Number(process.env.PORT ?? 3300);
|
|
37
|
+
|
|
38
|
+
if (!ClusterManager.isWorker()) {
|
|
39
|
+
// 主进程:派生 worker
|
|
40
|
+
const manager = new ClusterManager({
|
|
41
|
+
workers: process.env.WORKERS ? Number(process.env.WORKERS) : 'auto',
|
|
42
|
+
scriptPath: import.meta.path,
|
|
43
|
+
port: PORT,
|
|
44
|
+
});
|
|
45
|
+
manager.start();
|
|
46
|
+
|
|
47
|
+
process.on('SIGINT', async () => {
|
|
48
|
+
await manager.stop();
|
|
49
|
+
process.exit(0);
|
|
50
|
+
});
|
|
51
|
+
} else {
|
|
52
|
+
// Worker 进程:运行应用
|
|
53
|
+
@Controller('/api')
|
|
54
|
+
class PerfController {
|
|
55
|
+
@GET('/ping')
|
|
56
|
+
public ping(): object {
|
|
57
|
+
return { pong: true, worker: ClusterManager.getWorkerId() };
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
@Module({ controllers: [PerfController] })
|
|
62
|
+
class WorkerModule {}
|
|
63
|
+
|
|
64
|
+
const app = new Application({
|
|
65
|
+
port: PORT,
|
|
66
|
+
reusePort: true,
|
|
67
|
+
enableSignalHandlers: true,
|
|
68
|
+
});
|
|
69
|
+
app.registerModule(WorkerModule);
|
|
70
|
+
await app.listen();
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Worker 异常退出时,主进程会自动重启对应 worker。
|
|
@@ -29,12 +29,10 @@ AOP(面向切面编程)。这使您能够添加横切关注点,如缓存
|
|
|
29
29
|
|
|
30
30
|
### 基本装饰器模式
|
|
31
31
|
|
|
32
|
-
自定义装饰器是一个返回 `MethodDecorator` 的函数。它使用
|
|
32
|
+
自定义装饰器是一个返回 `MethodDecorator` 的函数。它使用 Reflect 元数据 API
|
|
33
33
|
在方法上存储元数据。
|
|
34
34
|
|
|
35
35
|
```typescript
|
|
36
|
-
import "reflect-metadata";
|
|
37
|
-
|
|
38
36
|
// 1. 定义元数据键(使用 Symbol 确保唯一性)
|
|
39
37
|
export const MY_METADATA_KEY = Symbol("@my-app:my-decorator");
|
|
40
38
|
|
|
@@ -280,8 +278,6 @@ class MyInterceptor extends BaseInterceptor {
|
|
|
280
278
|
### 存储元数据
|
|
281
279
|
|
|
282
280
|
```typescript
|
|
283
|
-
import "reflect-metadata";
|
|
284
|
-
|
|
285
281
|
const METADATA_KEY = Symbol("my-metadata");
|
|
286
282
|
|
|
287
283
|
// 存储元数据
|
|
@@ -364,7 +360,6 @@ class MyInterceptor extends BaseInterceptor {
|
|
|
364
360
|
### 示例 1:简单日志拦截器
|
|
365
361
|
|
|
366
362
|
```typescript
|
|
367
|
-
import 'reflect-metadata';
|
|
368
363
|
import { BaseInterceptor } from '@dangao/bun-server';
|
|
369
364
|
import type { Container } from '@dangao/bun-server';
|
|
370
365
|
import type { Context } from '@dangao/bun-server';
|
|
@@ -402,7 +397,6 @@ export class LogInterceptor extends BaseInterceptor {
|
|
|
402
397
|
### 示例 2:限流拦截器
|
|
403
398
|
|
|
404
399
|
```typescript
|
|
405
|
-
import 'reflect-metadata';
|
|
406
400
|
import { BaseInterceptor, HttpException } from '@dangao/bun-server';
|
|
407
401
|
import type { Container } from '@dangao/bun-server';
|
|
408
402
|
import type { Context } from '@dangao/bun-server';
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# 监控仪表盘模块
|
|
2
|
+
|
|
3
|
+
`DashboardModule` 提供 Web UI 和 API,用于监控应用状态、路由和健康检查。
|
|
4
|
+
|
|
5
|
+
## 配置
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import { DashboardModule } from '@dangao/bun-server';
|
|
9
|
+
|
|
10
|
+
DashboardModule.forRoot({
|
|
11
|
+
path: '/_dashboard', // UI 路径,默认 /_dashboard
|
|
12
|
+
auth: { // 可选,Basic Auth
|
|
13
|
+
username: 'admin',
|
|
14
|
+
password: 'admin',
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## 可用端点
|
|
20
|
+
|
|
21
|
+
- **UI**:`GET /_dashboard`,监控仪表盘主页面
|
|
22
|
+
- **系统信息**:`GET /_dashboard/api/system`,运行时间、内存、平台、Bun 版本
|
|
23
|
+
- **路由列表**:`GET /_dashboard/api/routes`,已注册路由及控制器、方法名
|
|
24
|
+
- **健康检查**:`GET /_dashboard/api/health`,若已注册 HealthModule 则执行健康指示器
|
|
25
|
+
|
|
26
|
+
## Basic Auth
|
|
27
|
+
|
|
28
|
+
配置 `auth` 后,访问上述端点需提供 Basic 认证。未配置时无需认证。
|
|
29
|
+
|
|
30
|
+
## 使用示例
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
import {
|
|
34
|
+
Application,
|
|
35
|
+
Controller,
|
|
36
|
+
GET,
|
|
37
|
+
Module,
|
|
38
|
+
DashboardModule,
|
|
39
|
+
} from '@dangao/bun-server';
|
|
40
|
+
|
|
41
|
+
@Controller('/api')
|
|
42
|
+
class AppController {
|
|
43
|
+
@GET('/hello')
|
|
44
|
+
public hello(): object {
|
|
45
|
+
return { message: 'Hello from Dashboard!' };
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
@Module({
|
|
50
|
+
imports: [
|
|
51
|
+
DashboardModule.forRoot({
|
|
52
|
+
path: '/_dashboard',
|
|
53
|
+
auth: { username: 'admin', password: 'admin' },
|
|
54
|
+
}),
|
|
55
|
+
],
|
|
56
|
+
controllers: [AppController],
|
|
57
|
+
})
|
|
58
|
+
class AppModule {}
|
|
59
|
+
|
|
60
|
+
const app = new Application({ port: 3000 });
|
|
61
|
+
app.registerModule(AppModule);
|
|
62
|
+
await app.listen();
|
|
63
|
+
// Dashboard UI: http://localhost:3000/_dashboard
|
|
64
|
+
// 认证:admin / admin
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## UI 界面
|
|
68
|
+
|
|
69
|
+
仪表盘 UI 展示系统信息、路由列表和健康状态,便于运维和调试。
|
package/docs/zh/debug.md
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# 调试与请求重放模块
|
|
2
|
+
|
|
3
|
+
`DebugModule` 在开发模式下录制 HTTP 请求,支持查看和重放,便于调试和问题复现。
|
|
4
|
+
|
|
5
|
+
## 配置
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import { DebugModule } from '@dangao/bun-server';
|
|
9
|
+
|
|
10
|
+
DebugModule.forRoot({
|
|
11
|
+
enabled: true, // 是否启用,默认 true
|
|
12
|
+
maxRecords: 500, // 最大录制数(环形缓冲区),默认 500
|
|
13
|
+
recordBody: true, // 是否录制请求体,默认 true
|
|
14
|
+
path: '/_debug', // Debug UI 路径,默认 /_debug
|
|
15
|
+
});
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## 录制内容
|
|
19
|
+
|
|
20
|
+
每条记录包含:
|
|
21
|
+
|
|
22
|
+
- **request**:方法、路径、头部、请求体(可选)
|
|
23
|
+
- **response**:状态码、响应头、响应体大小
|
|
24
|
+
- **timing**:总耗时
|
|
25
|
+
- **metadata**:匹配路由、控制器、方法名
|
|
26
|
+
|
|
27
|
+
## Debug UI 端点
|
|
28
|
+
|
|
29
|
+
- **UI**:`GET /_debug`,调试界面
|
|
30
|
+
- **记录列表**:`GET /_debug/api/records`,获取所有录制
|
|
31
|
+
- **清空记录**:`DELETE /_debug/api/records`
|
|
32
|
+
- **单条记录**:`GET /_debug/api/records/:id`
|
|
33
|
+
- **请求重放**:`POST /_debug/api/replay/:id`,按原请求重放并返回结果
|
|
34
|
+
|
|
35
|
+
## 使用示例
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
import {
|
|
39
|
+
Application,
|
|
40
|
+
Controller,
|
|
41
|
+
GET,
|
|
42
|
+
POST,
|
|
43
|
+
Body,
|
|
44
|
+
Module,
|
|
45
|
+
DebugModule,
|
|
46
|
+
} from '@dangao/bun-server';
|
|
47
|
+
|
|
48
|
+
@Controller('/api')
|
|
49
|
+
class AppController {
|
|
50
|
+
@GET('/hello')
|
|
51
|
+
public hello(): object {
|
|
52
|
+
return { message: 'Hello!' };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
@POST('/echo')
|
|
56
|
+
public echo(@Body() body: unknown): unknown {
|
|
57
|
+
return body;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
@Module({
|
|
62
|
+
imports: [
|
|
63
|
+
DebugModule.forRoot({
|
|
64
|
+
enabled: true,
|
|
65
|
+
maxRecords: 100,
|
|
66
|
+
recordBody: true,
|
|
67
|
+
path: '/_debug',
|
|
68
|
+
}),
|
|
69
|
+
],
|
|
70
|
+
controllers: [AppController],
|
|
71
|
+
})
|
|
72
|
+
class AppModule {}
|
|
73
|
+
|
|
74
|
+
const app = new Application({ port: 3000 });
|
|
75
|
+
app.registerModule(AppModule);
|
|
76
|
+
await app.listen();
|
|
77
|
+
// Debug UI: http://localhost:3000/_debug
|
|
78
|
+
// 发送请求后可在 UI 中查看录制并重放
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
建议仅在开发环境启用,生产环境可通过 `enabled: false` 关闭。
|
package/docs/zh/extensions.md
CHANGED