@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.
Files changed (209) hide show
  1. package/README.md +79 -6
  2. package/dist/cache/cache-module.d.ts +6 -0
  3. package/dist/cache/cache-module.d.ts.map +1 -1
  4. package/dist/client/generator.d.ts +16 -0
  5. package/dist/client/generator.d.ts.map +1 -0
  6. package/dist/client/index.d.ts +4 -0
  7. package/dist/client/index.d.ts.map +1 -0
  8. package/dist/client/runtime.d.ts +15 -0
  9. package/dist/client/runtime.d.ts.map +1 -0
  10. package/dist/client/types.d.ts +36 -0
  11. package/dist/client/types.d.ts.map +1 -0
  12. package/dist/config/config-module.d.ts +7 -0
  13. package/dist/config/config-module.d.ts.map +1 -1
  14. package/dist/config/index.d.ts +1 -1
  15. package/dist/config/index.d.ts.map +1 -1
  16. package/dist/config/service.d.ts +13 -0
  17. package/dist/config/service.d.ts.map +1 -1
  18. package/dist/config/types.d.ts +10 -0
  19. package/dist/config/types.d.ts.map +1 -1
  20. package/dist/core/application.d.ts +7 -0
  21. package/dist/core/application.d.ts.map +1 -1
  22. package/dist/core/apply-decorators.d.ts +6 -0
  23. package/dist/core/apply-decorators.d.ts.map +1 -0
  24. package/dist/core/cluster.d.ts +47 -0
  25. package/dist/core/cluster.d.ts.map +1 -0
  26. package/dist/core/index.d.ts +1 -0
  27. package/dist/core/index.d.ts.map +1 -1
  28. package/dist/core/server.d.ts +8 -0
  29. package/dist/core/server.d.ts.map +1 -1
  30. package/dist/dashboard/controller.d.ts +55 -0
  31. package/dist/dashboard/controller.d.ts.map +1 -0
  32. package/dist/dashboard/dashboard-extension.d.ts +20 -0
  33. package/dist/dashboard/dashboard-extension.d.ts.map +1 -0
  34. package/dist/dashboard/dashboard-module.d.ts +13 -0
  35. package/dist/dashboard/dashboard-module.d.ts.map +1 -0
  36. package/dist/dashboard/index.d.ts +4 -0
  37. package/dist/dashboard/index.d.ts.map +1 -0
  38. package/dist/dashboard/types.d.ts +16 -0
  39. package/dist/dashboard/types.d.ts.map +1 -0
  40. package/dist/dashboard/ui.d.ts +7 -0
  41. package/dist/dashboard/ui.d.ts.map +1 -0
  42. package/dist/database/database-module.d.ts +7 -0
  43. package/dist/database/database-module.d.ts.map +1 -1
  44. package/dist/debug/debug-module.d.ts +13 -0
  45. package/dist/debug/debug-module.d.ts.map +1 -0
  46. package/dist/debug/debug-ui-middleware.d.ts +8 -0
  47. package/dist/debug/debug-ui-middleware.d.ts.map +1 -0
  48. package/dist/debug/index.d.ts +5 -0
  49. package/dist/debug/index.d.ts.map +1 -0
  50. package/dist/debug/middleware.d.ts +12 -0
  51. package/dist/debug/middleware.d.ts.map +1 -0
  52. package/dist/debug/recorder.d.ts +61 -0
  53. package/dist/debug/recorder.d.ts.map +1 -0
  54. package/dist/debug/types.d.ts +48 -0
  55. package/dist/debug/types.d.ts.map +1 -0
  56. package/dist/debug/ui.d.ts +6 -0
  57. package/dist/debug/ui.d.ts.map +1 -0
  58. package/dist/di/async-module.d.ts +49 -0
  59. package/dist/di/async-module.d.ts.map +1 -0
  60. package/dist/di/lifecycle.d.ts +49 -0
  61. package/dist/di/lifecycle.d.ts.map +1 -0
  62. package/dist/di/module-registry.d.ts +24 -0
  63. package/dist/di/module-registry.d.ts.map +1 -1
  64. package/dist/index.d.ts +9 -0
  65. package/dist/index.d.ts.map +1 -1
  66. package/dist/index.js +1887 -35
  67. package/dist/router/route.d.ts +5 -7
  68. package/dist/router/route.d.ts.map +1 -1
  69. package/dist/swagger/generator.d.ts +10 -0
  70. package/dist/swagger/generator.d.ts.map +1 -1
  71. package/dist/testing/test-client.d.ts +49 -0
  72. package/dist/testing/test-client.d.ts.map +1 -0
  73. package/dist/testing/testing-module.d.ts +90 -0
  74. package/dist/testing/testing-module.d.ts.map +1 -0
  75. package/dist/websocket/registry.d.ts +1 -6
  76. package/dist/websocket/registry.d.ts.map +1 -1
  77. package/docs/async-module.md +59 -0
  78. package/docs/client-generation.md +100 -0
  79. package/docs/cluster.md +81 -0
  80. package/docs/custom-decorators.md +1 -7
  81. package/docs/dashboard.md +54 -0
  82. package/docs/debug.md +58 -0
  83. package/docs/extensions.md +0 -2
  84. package/docs/guide.md +0 -1
  85. package/docs/lifecycle.md +72 -0
  86. package/docs/testing.md +110 -0
  87. package/docs/zh/async-module.md +98 -0
  88. package/docs/zh/client-generation.md +92 -0
  89. package/docs/zh/cluster.md +74 -0
  90. package/docs/zh/custom-decorators.md +1 -7
  91. package/docs/zh/dashboard.md +69 -0
  92. package/docs/zh/debug.md +81 -0
  93. package/docs/zh/extensions.md +0 -2
  94. package/docs/zh/guide.md +0 -1
  95. package/docs/zh/lifecycle.md +87 -0
  96. package/docs/zh/migration.md +0 -5
  97. package/docs/zh/testing.md +119 -0
  98. package/package.json +4 -4
  99. package/src/cache/cache-module.ts +25 -0
  100. package/src/client/generator.ts +36 -0
  101. package/src/client/index.ts +8 -0
  102. package/src/client/runtime.ts +101 -0
  103. package/src/client/types.ts +38 -0
  104. package/src/config/config-module.ts +44 -4
  105. package/src/config/index.ts +1 -0
  106. package/src/config/service.ts +50 -0
  107. package/src/config/types.ts +12 -0
  108. package/src/core/application.ts +37 -0
  109. package/src/core/apply-decorators.ts +31 -0
  110. package/src/core/cluster.ts +143 -0
  111. package/src/core/index.ts +1 -0
  112. package/src/core/server.ts +14 -1
  113. package/src/dashboard/controller.ts +227 -0
  114. package/src/dashboard/dashboard-extension.ts +26 -0
  115. package/src/dashboard/dashboard-module.ts +38 -0
  116. package/src/dashboard/index.ts +3 -0
  117. package/src/dashboard/types.ts +16 -0
  118. package/src/dashboard/ui.ts +219 -0
  119. package/src/database/database-module.ts +20 -0
  120. package/src/debug/debug-module.ts +70 -0
  121. package/src/debug/debug-ui-middleware.ts +110 -0
  122. package/src/debug/index.ts +9 -0
  123. package/src/debug/middleware.ts +126 -0
  124. package/src/debug/recorder.ts +141 -0
  125. package/src/debug/types.ts +49 -0
  126. package/src/debug/ui.ts +393 -0
  127. package/src/di/async-module.ts +141 -0
  128. package/src/di/lifecycle.ts +117 -0
  129. package/src/di/module-registry.ts +75 -0
  130. package/src/index.ts +35 -0
  131. package/src/router/route.ts +20 -20
  132. package/src/swagger/generator.ts +100 -0
  133. package/src/testing/test-client.ts +112 -0
  134. package/src/testing/testing-module.ts +238 -0
  135. package/src/websocket/registry.ts +3 -16
  136. package/tests/auth/auth-decorators.test.ts +0 -1
  137. package/tests/auth/oauth2-service.test.ts +0 -1
  138. package/tests/cache/cache-decorators-extended.test.ts +0 -1
  139. package/tests/cache/cache-decorators.test.ts +0 -1
  140. package/tests/cache/cache-interceptors.test.ts +0 -1
  141. package/tests/cache/cache-module.test.ts +0 -1
  142. package/tests/cache/cache-service-proxy.test.ts +0 -1
  143. package/tests/client/client-generator.test.ts +142 -0
  144. package/tests/config/config-center-integration.test.ts +0 -1
  145. package/tests/config/config-module-extended.test.ts +0 -1
  146. package/tests/config/config-module.test.ts +0 -1
  147. package/tests/controller/controller.test.ts +0 -1
  148. package/tests/controller/param-binder.test.ts +0 -1
  149. package/tests/controller/path-combination.test.ts +0 -1
  150. package/tests/core/application.test.ts +34 -0
  151. package/tests/core/apply-decorators.test.ts +109 -0
  152. package/tests/core/cluster.test.ts +32 -0
  153. package/tests/dashboard/dashboard-module.test.ts +85 -0
  154. package/tests/database/database-module.test.ts +0 -1
  155. package/tests/database/orm.test.ts +0 -1
  156. package/tests/database/postgres-mysql-integration.test.ts +0 -1
  157. package/tests/database/transaction.test.ts +0 -1
  158. package/tests/debug/debug-module.test.ts +141 -0
  159. package/tests/di/async-module.test.ts +125 -0
  160. package/tests/di/container.test.ts +0 -1
  161. package/tests/di/lifecycle.test.ts +140 -0
  162. package/tests/error/error-handler.test.ts +0 -1
  163. package/tests/events/event-decorators.test.ts +0 -1
  164. package/tests/events/event-listener-scanner.test.ts +0 -1
  165. package/tests/events/event-module.test.ts +0 -1
  166. package/tests/extensions/logger-module.test.ts +0 -1
  167. package/tests/health/health-module.test.ts +0 -1
  168. package/tests/integration/oauth2-e2e.test.ts +0 -1
  169. package/tests/integration/session-e2e.test.ts +0 -1
  170. package/tests/interceptor/base-interceptor.test.ts +0 -1
  171. package/tests/interceptor/builtin/cache-interceptor.test.ts +0 -1
  172. package/tests/interceptor/builtin/log-interceptor.test.ts +0 -1
  173. package/tests/interceptor/builtin/permission-interceptor.test.ts +0 -1
  174. package/tests/interceptor/interceptor-advanced-integration.test.ts +0 -1
  175. package/tests/interceptor/interceptor-chain.test.ts +0 -1
  176. package/tests/interceptor/interceptor-integration.test.ts +0 -1
  177. package/tests/interceptor/interceptor-metadata.test.ts +0 -1
  178. package/tests/interceptor/interceptor-registry.test.ts +0 -1
  179. package/tests/interceptor/perf/interceptor-performance.test.ts +0 -1
  180. package/tests/metrics/metrics-module.test.ts +0 -1
  181. package/tests/microservice/config-center.test.ts +0 -1
  182. package/tests/microservice/service-client-decorators.test.ts +0 -1
  183. package/tests/microservice/service-registry-decorators.test.ts +0 -1
  184. package/tests/microservice/service-registry.test.ts +0 -1
  185. package/tests/middleware/builtin/middleware-builtin-extended.test.ts +0 -1
  186. package/tests/middleware/builtin/rate-limit.test.ts +0 -1
  187. package/tests/middleware/middleware-decorators.test.ts +0 -1
  188. package/tests/middleware/middleware-pipeline.test.ts +0 -1
  189. package/tests/middleware/middleware.test.ts +0 -1
  190. package/tests/perf/optimization.test.ts +0 -1
  191. package/tests/queue/queue-decorators.test.ts +0 -1
  192. package/tests/queue/queue-module.test.ts +0 -1
  193. package/tests/queue/queue-service.test.ts +0 -1
  194. package/tests/router/router-decorators.test.ts +0 -1
  195. package/tests/router/router-extended.test.ts +0 -1
  196. package/tests/security/guards/guards-integration.test.ts +0 -1
  197. package/tests/security/guards/guards.test.ts +0 -1
  198. package/tests/security/guards/reflector.test.ts +0 -1
  199. package/tests/security/security-filter.test.ts +0 -1
  200. package/tests/security/security-module-extended.test.ts +0 -1
  201. package/tests/security/security-module.test.ts +0 -1
  202. package/tests/session/session-decorators.test.ts +0 -1
  203. package/tests/session/session-module.test.ts +0 -1
  204. package/tests/swagger/decorators.test.ts +0 -1
  205. package/tests/swagger/swagger-module.test.ts +0 -1
  206. package/tests/swagger/ui.test.ts +0 -1
  207. package/tests/testing/testing-module.test.ts +129 -0
  208. package/tests/validation/class-validator.test.ts +0 -1
  209. package/tests/validation/controller-validation.test.ts +0 -1
@@ -0,0 +1,141 @@
1
+ import type { ModuleClass, ModuleProvider, ProviderToken } from './module';
2
+ import { MODULE_METADATA_KEY } from './module';
3
+ import type { Container } from './container';
4
+ import { ModuleRegistry } from './module-registry';
5
+
6
+ /**
7
+ * 异步模块配置选项
8
+ */
9
+ export interface AsyncModuleOptions<T> {
10
+ /**
11
+ * 需要导入的模块(提供 inject 依赖的来源)
12
+ */
13
+ imports?: ModuleClass[];
14
+ /**
15
+ * 要注入到 useFactory 中的 provider tokens
16
+ */
17
+ inject?: ProviderToken[];
18
+ /**
19
+ * 异步工厂函数,返回模块配置
20
+ */
21
+ useFactory: (...deps: unknown[]) => T | Promise<T>;
22
+ }
23
+
24
+ /**
25
+ * 异步 provider 注册表
26
+ * 在 Application.listen() 期间,所有待初始化的异步 provider 将被顺序执行
27
+ */
28
+ export class AsyncProviderRegistry {
29
+ private static instance: AsyncProviderRegistry;
30
+ private readonly pendingFactories: Array<{
31
+ token: ProviderToken;
32
+ factory: (container: Container) => Promise<unknown>;
33
+ }> = [];
34
+
35
+ public static getInstance(): AsyncProviderRegistry {
36
+ if (!AsyncProviderRegistry.instance) {
37
+ AsyncProviderRegistry.instance = new AsyncProviderRegistry();
38
+ }
39
+ return AsyncProviderRegistry.instance;
40
+ }
41
+
42
+ /**
43
+ * 注册一个异步 provider
44
+ */
45
+ public register(
46
+ token: ProviderToken,
47
+ factory: (container: Container) => Promise<unknown>,
48
+ ): void {
49
+ this.pendingFactories.push({ token, factory });
50
+ }
51
+
52
+ /**
53
+ * 初始化所有异步 providers
54
+ * 同时注册到根容器和所有模块子容器(覆盖占位 factory)
55
+ * @param container - 根 DI 容器
56
+ */
57
+ public async initializeAll(container: Container): Promise<void> {
58
+ const moduleContainers = ModuleRegistry.getInstance().getAllModuleContainers();
59
+
60
+ for (const { token, factory } of this.pendingFactories) {
61
+ const value = await factory(container);
62
+ container.registerInstance(token, value);
63
+
64
+ for (const moduleContainer of moduleContainers) {
65
+ if (moduleContainer.isRegistered(token)) {
66
+ moduleContainer.registerInstance(token, value);
67
+ }
68
+ }
69
+ }
70
+ this.pendingFactories.length = 0;
71
+ }
72
+
73
+ /**
74
+ * 检查是否有待初始化的异步 providers
75
+ */
76
+ public hasPending(): boolean {
77
+ return this.pendingFactories.length > 0;
78
+ }
79
+
80
+ public clear(): void {
81
+ this.pendingFactories.length = 0;
82
+ }
83
+ }
84
+
85
+ /**
86
+ * 辅助函数:为模块添加 forRootAsync 的标准实现
87
+ * 各模块可直接在 forRootAsync 中调用此函数
88
+ */
89
+ export function registerAsyncProviders<T>(
90
+ moduleClass: ModuleClass,
91
+ asyncOptions: AsyncModuleOptions<T>,
92
+ tokenMap: Map<ProviderToken, (config: T) => unknown>,
93
+ extraProviders?: ModuleProvider[],
94
+ ): typeof moduleClass {
95
+ const providers: ModuleProvider[] = [];
96
+ const exports: ProviderToken[] = [];
97
+
98
+ for (const [token] of tokenMap) {
99
+ // Placeholder factory that will be resolved async during init
100
+ providers.push({
101
+ provide: token,
102
+ useFactory: () => {
103
+ throw new Error(
104
+ `Async provider ${String(token)} not yet initialized. ` +
105
+ 'Ensure Application.listen() is called before resolving async providers.',
106
+ );
107
+ },
108
+ });
109
+ exports.push(token);
110
+ }
111
+
112
+ if (extraProviders) {
113
+ providers.push(...extraProviders);
114
+ }
115
+
116
+ // Register the async factory
117
+ const registry = AsyncProviderRegistry.getInstance();
118
+ for (const [token, mapper] of tokenMap) {
119
+ registry.register(token, async (container) => {
120
+ const deps: unknown[] = [];
121
+ if (asyncOptions.inject) {
122
+ for (const injectToken of asyncOptions.inject) {
123
+ deps.push(container.resolve(injectToken as any));
124
+ }
125
+ }
126
+ const config = await asyncOptions.useFactory(...deps);
127
+ return mapper(config);
128
+ });
129
+ }
130
+
131
+ const existingMetadata = Reflect.getMetadata(MODULE_METADATA_KEY, moduleClass) || {};
132
+ const metadata = {
133
+ ...existingMetadata,
134
+ imports: [...(existingMetadata.imports || []), ...(asyncOptions.imports || [])],
135
+ providers: [...(existingMetadata.providers || []), ...providers],
136
+ exports: [...(existingMetadata.exports || []), ...exports],
137
+ };
138
+ Reflect.defineMetadata(MODULE_METADATA_KEY, metadata, moduleClass);
139
+
140
+ return moduleClass;
141
+ }
@@ -0,0 +1,117 @@
1
+ /**
2
+ * 模块初始化钩子
3
+ * 在模块所有 providers 注册完成后调用
4
+ */
5
+ export interface OnModuleInit {
6
+ onModuleInit(): Promise<void> | void;
7
+ }
8
+
9
+ /**
10
+ * 模块销毁钩子
11
+ * 在应用关闭时调用(反向顺序)
12
+ */
13
+ export interface OnModuleDestroy {
14
+ onModuleDestroy(): Promise<void> | void;
15
+ }
16
+
17
+ /**
18
+ * 应用启动钩子
19
+ * 在所有模块初始化完成后、服务器开始监听前调用
20
+ */
21
+ export interface OnApplicationBootstrap {
22
+ onApplicationBootstrap(): Promise<void> | void;
23
+ }
24
+
25
+ /**
26
+ * 应用关闭钩子
27
+ * 在优雅停机开始时调用
28
+ */
29
+ export interface OnApplicationShutdown {
30
+ onApplicationShutdown(signal?: string): Promise<void> | void;
31
+ }
32
+
33
+ export function hasOnModuleInit(instance: unknown): instance is OnModuleInit {
34
+ return (
35
+ instance !== null &&
36
+ instance !== undefined &&
37
+ typeof instance === 'object' &&
38
+ 'onModuleInit' in instance &&
39
+ typeof (instance as OnModuleInit).onModuleInit === 'function'
40
+ );
41
+ }
42
+
43
+ export function hasOnModuleDestroy(instance: unknown): instance is OnModuleDestroy {
44
+ return (
45
+ instance !== null &&
46
+ instance !== undefined &&
47
+ typeof instance === 'object' &&
48
+ 'onModuleDestroy' in instance &&
49
+ typeof (instance as OnModuleDestroy).onModuleDestroy === 'function'
50
+ );
51
+ }
52
+
53
+ export function hasOnApplicationBootstrap(instance: unknown): instance is OnApplicationBootstrap {
54
+ return (
55
+ instance !== null &&
56
+ instance !== undefined &&
57
+ typeof instance === 'object' &&
58
+ 'onApplicationBootstrap' in instance &&
59
+ typeof (instance as OnApplicationBootstrap).onApplicationBootstrap === 'function'
60
+ );
61
+ }
62
+
63
+ export function hasOnApplicationShutdown(instance: unknown): instance is OnApplicationShutdown {
64
+ return (
65
+ instance !== null &&
66
+ instance !== undefined &&
67
+ typeof instance === 'object' &&
68
+ 'onApplicationShutdown' in instance &&
69
+ typeof (instance as OnApplicationShutdown).onApplicationShutdown === 'function'
70
+ );
71
+ }
72
+
73
+ /**
74
+ * 按顺序调用 onModuleInit
75
+ */
76
+ export async function callOnModuleInit(instances: unknown[]): Promise<void> {
77
+ for (const instance of instances) {
78
+ if (hasOnModuleInit(instance)) {
79
+ await instance.onModuleInit();
80
+ }
81
+ }
82
+ }
83
+
84
+ /**
85
+ * 按顺序调用 onApplicationBootstrap
86
+ */
87
+ export async function callOnApplicationBootstrap(instances: unknown[]): Promise<void> {
88
+ for (const instance of instances) {
89
+ if (hasOnApplicationBootstrap(instance)) {
90
+ await instance.onApplicationBootstrap();
91
+ }
92
+ }
93
+ }
94
+
95
+ /**
96
+ * 按反向顺序调用 onModuleDestroy
97
+ */
98
+ export async function callOnModuleDestroy(instances: unknown[]): Promise<void> {
99
+ for (let i = instances.length - 1; i >= 0; i--) {
100
+ const instance = instances[i];
101
+ if (hasOnModuleDestroy(instance)) {
102
+ await instance.onModuleDestroy();
103
+ }
104
+ }
105
+ }
106
+
107
+ /**
108
+ * 按反向顺序调用 onApplicationShutdown
109
+ */
110
+ export async function callOnApplicationShutdown(instances: unknown[], signal?: string): Promise<void> {
111
+ for (let i = instances.length - 1; i >= 0; i--) {
112
+ const instance = instances[i];
113
+ if (hasOnApplicationShutdown(instance)) {
114
+ await instance.onApplicationShutdown(signal);
115
+ }
116
+ }
117
+ }
@@ -6,6 +6,12 @@ import { isGlobalModule } from './decorators';
6
6
  import type { Constructor } from '@/core/types';
7
7
  import type { ApplicationExtension } from '../extensions/types';
8
8
  import type { Middleware } from '../middleware';
9
+ import {
10
+ callOnModuleInit,
11
+ callOnModuleDestroy,
12
+ callOnApplicationBootstrap,
13
+ callOnApplicationShutdown,
14
+ } from './lifecycle';
9
15
 
10
16
  interface ModuleRef {
11
17
  moduleClass: ModuleClass;
@@ -49,6 +55,17 @@ export class ModuleRegistry {
49
55
  return this.moduleRefs.get(moduleClass);
50
56
  }
51
57
 
58
+ /**
59
+ * 获取所有模块的子容器(用于异步 provider 初始化)
60
+ */
61
+ public getAllModuleContainers(): Container[] {
62
+ const containers: Container[] = [];
63
+ for (const [, ref] of this.moduleRefs) {
64
+ containers.push(ref.container);
65
+ }
66
+ return containers;
67
+ }
68
+
52
69
  public clear(): void {
53
70
  this.moduleRefs.clear();
54
71
  this.processing.clear();
@@ -207,6 +224,64 @@ export class ModuleRegistry {
207
224
  return moduleRef.middlewares;
208
225
  }
209
226
 
227
+ /**
228
+ * 解析所有模块中的 provider 实例,用于生命周期钩子调用
229
+ */
230
+ public resolveAllProviderInstances(): unknown[] {
231
+ const instances: unknown[] = [];
232
+ for (const [, ref] of this.moduleRefs) {
233
+ for (const provider of ref.metadata.providers) {
234
+ try {
235
+ if (typeof provider === 'function') {
236
+ instances.push(ref.container.resolve(provider));
237
+ } else if ('useClass' in provider) {
238
+ const token = provider.provide ?? provider.useClass;
239
+ instances.push(ref.container.resolve(token as Constructor<unknown>));
240
+ } else if ('useValue' in provider) {
241
+ instances.push(provider.useValue);
242
+ } else if ('useFactory' in provider) {
243
+ instances.push(ref.container.resolve(provider.provide as Constructor<unknown>));
244
+ }
245
+ } catch {
246
+ // skip providers that can't be resolved (e.g. pending async providers)
247
+ }
248
+ }
249
+ }
250
+ return instances;
251
+ }
252
+
253
+ /**
254
+ * 调用所有 provider 的 onModuleInit 钩子
255
+ */
256
+ public async callModuleInitHooks(): Promise<void> {
257
+ const instances = this.resolveAllProviderInstances();
258
+ await callOnModuleInit(instances);
259
+ }
260
+
261
+ /**
262
+ * 调用所有 provider 的 onApplicationBootstrap 钩子
263
+ */
264
+ public async callBootstrapHooks(): Promise<void> {
265
+ const instances = this.resolveAllProviderInstances();
266
+ await callOnApplicationBootstrap(instances);
267
+ }
268
+
269
+ /**
270
+ * 调用所有 provider 的 onModuleDestroy 钩子
271
+ */
272
+ public async callModuleDestroyHooks(): Promise<void> {
273
+ const instances = this.resolveAllProviderInstances();
274
+ await callOnModuleDestroy(instances);
275
+ }
276
+
277
+ /**
278
+ * 调用所有 provider 的 onApplicationShutdown 钩子
279
+ */
280
+ public async callShutdownHooks(signal?: string): Promise<void> {
281
+ const instances = this.resolveAllProviderInstances();
282
+ await callOnApplicationShutdown(instances, signal);
283
+ }
284
+
210
285
  private registerExport(parentContainer: Container, moduleRef: ModuleRef, token: ProviderToken): void {
211
286
  if (!moduleRef.container.isRegistered(token)) {
212
287
  throw new Error(
package/src/index.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  export { Application, type ApplicationOptions } from './core/application';
2
+ export { applyDecorators } from './core/apply-decorators';
2
3
  export { BunServer, type ServerOptions } from './core/server';
4
+ export { ClusterManager, type ClusterOptions } from './core/cluster';
3
5
  export { Context } from './core/context';
4
6
  export { ContextService, CONTEXT_SERVICE_TOKEN, contextStore } from './core/context-service';
5
7
  export { Route, Router, RouteRegistry } from './router';
@@ -34,6 +36,13 @@ export {
34
36
  type ModuleClass,
35
37
  } from './di/module';
36
38
  export { ModuleRegistry } from './di/module-registry';
39
+ export { AsyncProviderRegistry, type AsyncModuleOptions } from './di/async-module';
40
+ export type {
41
+ OnModuleInit,
42
+ OnModuleDestroy,
43
+ OnApplicationBootstrap,
44
+ OnApplicationShutdown,
45
+ } from './di/lifecycle';
37
46
  export {
38
47
  InterceptorRegistry,
39
48
  InterceptorChain,
@@ -271,6 +280,12 @@ export {
271
280
  HEALTH_INDICATORS_TOKEN,
272
281
  HEALTH_OPTIONS_TOKEN,
273
282
  } from './health';
283
+ export {
284
+ DashboardModule,
285
+ DashboardService,
286
+ DASHBOARD_OPTIONS_TOKEN,
287
+ type DashboardModuleOptions,
288
+ } from './dashboard';
274
289
  export {
275
290
  MetricsModule,
276
291
  MetricsCollector,
@@ -351,12 +366,32 @@ export {
351
366
  type AuthContext,
352
367
  type AuthConfig,
353
368
  } from './auth';
369
+ export {
370
+ DebugModule,
371
+ RequestRecorder,
372
+ createDebugMiddleware,
373
+ DEBUG_OPTIONS_TOKEN,
374
+ DEBUG_RECORDER_TOKEN,
375
+ type DebugModuleOptions,
376
+ type RequestRecord,
377
+ } from './debug';
354
378
  export {
355
379
  PerformanceHarness,
356
380
  StressTester,
357
381
  type BenchmarkResult,
358
382
  type StressResult,
359
383
  } from './testing/harness';
384
+ export { Test, TestingModule, TestingModuleBuilder } from './testing/testing-module';
385
+ export { TestHttpClient } from './testing/test-client';
386
+ // Client Generation
387
+ export {
388
+ ClientGenerator,
389
+ createClient,
390
+ type RouteManifest,
391
+ type RouteManifestEntry,
392
+ type ClientConfig,
393
+ type ClientRequestOptions,
394
+ } from './client';
360
395
  // Cache 模块
361
396
  export {
362
397
  CacheModule,
@@ -35,14 +35,14 @@ export class Route {
35
35
  public readonly methodName?: string;
36
36
 
37
37
  /**
38
- * 路径模式(用于匹配)
38
+ * 动态路由匹配用正则(比 URLPattern.exec() 快约 10 倍)
39
39
  */
40
- private readonly pattern: RegExp;
40
+ private readonly pattern?: RegExp;
41
41
 
42
42
  /**
43
- * 路径参数名列表
43
+ * 动态路由参数名列表
44
44
  */
45
- private readonly paramNames: string[];
45
+ private readonly paramNames?: string[];
46
46
 
47
47
  private readonly middlewarePipeline: MiddlewarePipeline | null;
48
48
  private readonly staticKey?: string;
@@ -62,23 +62,21 @@ export class Route {
62
62
  this.controllerClass = controllerClass;
63
63
  this.methodName = methodName;
64
64
 
65
- // 解析路径参数
66
- const { pattern, paramNames } = this.parsePath(path);
67
- this.pattern = pattern;
68
- this.paramNames = paramNames;
69
- this.middlewarePipeline = middlewares.length > 0 ? new MiddlewarePipeline(middlewares) : null;
70
65
  this.isStatic = !path.includes(':') && !path.includes('*');
71
66
  if (this.isStatic) {
72
67
  this.staticKey = `${method}:${path}`;
68
+ } else {
69
+ const { pattern, paramNames } = Route.parsePath(path);
70
+ this.pattern = pattern;
71
+ this.paramNames = paramNames;
73
72
  }
73
+ this.middlewarePipeline = middlewares.length > 0 ? new MiddlewarePipeline(middlewares) : null;
74
74
  }
75
75
 
76
76
  /**
77
77
  * 解析路径,生成匹配模式和参数名列表
78
- * @param path - 路由路径
79
- * @returns 匹配模式和参数名列表
80
78
  */
81
- private parsePath(path: string): { pattern: RegExp; paramNames: string[] } {
79
+ private static parsePath(path: string): { pattern: RegExp; paramNames: string[] } {
82
80
  const paramNames: string[] = [];
83
81
  const patternString = path
84
82
  .replace(/:([^/]+)/g, (_, paramName) => {
@@ -87,8 +85,7 @@ export class Route {
87
85
  })
88
86
  .replace(/\*/g, '.*');
89
87
 
90
- const pattern = new RegExp(`^${patternString}$`);
91
- return { pattern, paramNames };
88
+ return { pattern: new RegExp(`^${patternString}$`), paramNames };
92
89
  }
93
90
 
94
91
  /**
@@ -98,21 +95,24 @@ export class Route {
98
95
  * @returns 匹配结果
99
96
  */
100
97
  public match(method: HttpMethod, path: string): RouteMatch {
101
- // 方法不匹配
102
98
  if (this.method !== method) {
103
99
  return { matched: false, params: {} };
104
100
  }
105
101
 
106
- // 路径不匹配
107
- const match = path.match(this.pattern);
102
+ if (this.isStatic) {
103
+ return path === this.path
104
+ ? { matched: true, params: {} }
105
+ : { matched: false, params: {} };
106
+ }
107
+
108
+ const match = path.match(this.pattern!);
108
109
  if (!match) {
109
110
  return { matched: false, params: {} };
110
111
  }
111
112
 
112
- // 提取路径参数
113
113
  const params: Record<string, string> = {};
114
- for (let i = 0; i < this.paramNames.length; i++) {
115
- params[this.paramNames[i]] = match[i + 1] ?? '';
114
+ for (let i = 0; i < this.paramNames!.length; i++) {
115
+ params[this.paramNames![i]] = match[i + 1] ?? '';
116
116
  }
117
117
 
118
118
  return { matched: true, params };
@@ -214,6 +214,106 @@ export class SwaggerGenerator {
214
214
  return document;
215
215
  }
216
216
 
217
+ /**
218
+ * 生成 Markdown 格式的 API 文档
219
+ * 利用 Bun 1.3.8+ 原生 Bun.markdown 内置解析器
220
+ */
221
+ public generateMarkdown(): string {
222
+ const doc = this.generate();
223
+ const lines: string[] = [];
224
+
225
+ lines.push(`# ${doc.info.title}`);
226
+ lines.push('');
227
+ if (doc.info.description) {
228
+ lines.push(doc.info.description);
229
+ lines.push('');
230
+ }
231
+ lines.push(`**Version:** ${doc.info.version}`);
232
+ lines.push('');
233
+
234
+ if (doc.servers?.length) {
235
+ lines.push('## Servers');
236
+ lines.push('');
237
+ for (const server of doc.servers) {
238
+ lines.push(`- \`${server.url}\`${server.description ? ' — ' + server.description : ''}`);
239
+ }
240
+ lines.push('');
241
+ }
242
+
243
+ if (doc.tags?.length) {
244
+ lines.push('## Tags');
245
+ lines.push('');
246
+ for (const tag of doc.tags) {
247
+ lines.push(`- **${tag.name}**${tag.description ? ': ' + tag.description : ''}`);
248
+ }
249
+ lines.push('');
250
+ }
251
+
252
+ lines.push('## Endpoints');
253
+ lines.push('');
254
+
255
+ const methods = ['get', 'post', 'put', 'delete', 'patch'] as const;
256
+ for (const [path, pathObj] of Object.entries(doc.paths)) {
257
+ for (const method of methods) {
258
+ const operation = pathObj[method];
259
+ if (!operation) continue;
260
+
261
+ lines.push(`### \`${method.toUpperCase()}\` ${path}`);
262
+ lines.push('');
263
+ if (operation.summary) {
264
+ lines.push(`**${operation.summary}**`);
265
+ lines.push('');
266
+ }
267
+ if (operation.description) {
268
+ lines.push(operation.description);
269
+ lines.push('');
270
+ }
271
+ if (operation.tags?.length) {
272
+ lines.push(`Tags: ${operation.tags.map((t) => `\`${t}\``).join(', ')}`);
273
+ lines.push('');
274
+ }
275
+ if (operation.deprecated) {
276
+ lines.push('> **Deprecated**');
277
+ lines.push('');
278
+ }
279
+
280
+ if (operation.parameters?.length) {
281
+ lines.push('| Parameter | In | Type | Required | Description |');
282
+ lines.push('|---|---|---|---|---|');
283
+ for (const param of operation.parameters) {
284
+ const type = param.schema?.type ?? 'string';
285
+ const required = param.required ? 'Yes' : 'No';
286
+ lines.push(`| \`${param.name}\` | ${param.in} | ${type} | ${required} | ${param.description ?? ''} |`);
287
+ }
288
+ lines.push('');
289
+ }
290
+
291
+ if (operation.responses) {
292
+ lines.push('**Responses:**');
293
+ lines.push('');
294
+ for (const [status, resp] of Object.entries(operation.responses)) {
295
+ lines.push(`- **${status}**: ${resp.description ?? ''}`);
296
+ }
297
+ lines.push('');
298
+ }
299
+
300
+ lines.push('---');
301
+ lines.push('');
302
+ }
303
+ }
304
+
305
+ return lines.join('\n');
306
+ }
307
+
308
+ /**
309
+ * 生成 Markdown 并渲染为 HTML
310
+ * 利用 Bun.markdown.html() 进行高性能渲染
311
+ */
312
+ public generateMarkdownHtml(): string {
313
+ const md = this.generateMarkdown();
314
+ return Bun.markdown.html(md, { headings: true });
315
+ }
316
+
217
317
  /**
218
318
  * 规范化路径
219
319
  */