@dangao/bun-server 1.0.0 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (200) hide show
  1. package/package.json +4 -2
  2. package/readme.md +163 -2
  3. package/src/auth/controller.ts +148 -0
  4. package/src/auth/decorators.ts +81 -0
  5. package/src/auth/index.ts +12 -0
  6. package/src/auth/jwt.ts +169 -0
  7. package/src/auth/oauth2.ts +244 -0
  8. package/src/auth/types.ts +248 -0
  9. package/src/cache/cache-module.ts +67 -0
  10. package/src/cache/decorators.ts +202 -0
  11. package/src/cache/index.ts +27 -0
  12. package/src/cache/service.ts +151 -0
  13. package/src/cache/types.ts +420 -0
  14. package/src/config/config-module.ts +76 -0
  15. package/src/config/index.ts +8 -0
  16. package/src/config/service.ts +93 -0
  17. package/src/config/types.ts +27 -0
  18. package/src/controller/controller.ts +251 -0
  19. package/src/controller/decorators.ts +84 -0
  20. package/src/controller/index.ts +7 -0
  21. package/src/controller/metadata.ts +27 -0
  22. package/src/controller/param-binder.ts +157 -0
  23. package/src/core/application.ts +233 -0
  24. package/src/core/context.ts +228 -0
  25. package/src/core/index.ts +4 -0
  26. package/src/core/server.ts +128 -0
  27. package/src/core/types.ts +2 -0
  28. package/src/database/connection-manager.ts +239 -0
  29. package/src/database/connection-pool.ts +322 -0
  30. package/src/database/database-extension.ts +62 -0
  31. package/src/database/database-module.ts +115 -0
  32. package/src/database/health-indicator.ts +51 -0
  33. package/src/database/index.ts +47 -0
  34. package/src/database/orm/decorators.ts +155 -0
  35. package/src/database/orm/drizzle-repository.ts +39 -0
  36. package/src/database/orm/index.ts +23 -0
  37. package/src/database/orm/repository-decorator.ts +39 -0
  38. package/src/database/orm/repository.ts +103 -0
  39. package/src/database/orm/service.ts +49 -0
  40. package/src/database/orm/transaction-decorator.ts +45 -0
  41. package/src/database/orm/transaction-interceptor.ts +243 -0
  42. package/src/database/orm/transaction-manager.ts +276 -0
  43. package/src/database/orm/transaction-types.ts +140 -0
  44. package/src/database/orm/types.ts +99 -0
  45. package/src/database/service.ts +221 -0
  46. package/src/database/types.ts +171 -0
  47. package/src/di/container.ts +398 -0
  48. package/src/di/decorators.ts +228 -0
  49. package/src/di/index.ts +4 -0
  50. package/src/di/module-registry.ts +188 -0
  51. package/src/di/module.ts +65 -0
  52. package/src/di/types.ts +67 -0
  53. package/src/error/error-codes.ts +222 -0
  54. package/src/error/filter.ts +43 -0
  55. package/src/error/handler.ts +66 -0
  56. package/src/error/http-exception.ts +115 -0
  57. package/src/error/i18n.ts +217 -0
  58. package/src/error/index.ts +16 -0
  59. package/src/extensions/index.ts +5 -0
  60. package/src/extensions/logger-extension.ts +31 -0
  61. package/src/extensions/logger-module.ts +69 -0
  62. package/src/extensions/types.ts +14 -0
  63. package/src/files/index.ts +5 -0
  64. package/src/files/static-middleware.ts +53 -0
  65. package/src/files/storage.ts +67 -0
  66. package/src/files/types.ts +33 -0
  67. package/src/files/upload-middleware.ts +45 -0
  68. package/src/health/controller.ts +76 -0
  69. package/src/health/health-module.ts +51 -0
  70. package/src/health/index.ts +12 -0
  71. package/src/health/types.ts +28 -0
  72. package/src/index.ts +270 -0
  73. package/src/metrics/collector.ts +209 -0
  74. package/src/metrics/controller.ts +40 -0
  75. package/src/metrics/index.ts +15 -0
  76. package/src/metrics/metrics-module.ts +58 -0
  77. package/src/metrics/middleware.ts +46 -0
  78. package/src/metrics/prometheus.ts +79 -0
  79. package/src/metrics/types.ts +103 -0
  80. package/src/middleware/builtin/cors.ts +60 -0
  81. package/src/middleware/builtin/error-handler.ts +90 -0
  82. package/src/middleware/builtin/file-upload.ts +42 -0
  83. package/src/middleware/builtin/index.ts +14 -0
  84. package/src/middleware/builtin/logger.ts +91 -0
  85. package/src/middleware/builtin/rate-limit.ts +252 -0
  86. package/src/middleware/builtin/static-file.ts +88 -0
  87. package/src/middleware/decorators.ts +91 -0
  88. package/src/middleware/index.ts +11 -0
  89. package/src/middleware/middleware.ts +13 -0
  90. package/src/middleware/pipeline.ts +93 -0
  91. package/src/queue/decorators.ts +110 -0
  92. package/src/queue/index.ts +26 -0
  93. package/src/queue/queue-module.ts +64 -0
  94. package/src/queue/service.ts +302 -0
  95. package/src/queue/types.ts +341 -0
  96. package/src/request/body-parser.ts +133 -0
  97. package/src/request/file-handler.ts +46 -0
  98. package/src/request/index.ts +5 -0
  99. package/src/request/request.ts +107 -0
  100. package/src/request/response.ts +150 -0
  101. package/src/router/decorators.ts +122 -0
  102. package/src/router/index.ts +6 -0
  103. package/src/router/registry.ts +98 -0
  104. package/src/router/route.ts +140 -0
  105. package/src/router/router.ts +241 -0
  106. package/src/router/types.ts +27 -0
  107. package/src/security/access-decision-manager.ts +34 -0
  108. package/src/security/authentication-manager.ts +47 -0
  109. package/src/security/context.ts +92 -0
  110. package/src/security/filter.ts +162 -0
  111. package/src/security/index.ts +8 -0
  112. package/src/security/providers/index.ts +3 -0
  113. package/src/security/providers/jwt-provider.ts +60 -0
  114. package/src/security/providers/oauth2-provider.ts +70 -0
  115. package/src/security/security-module.ts +145 -0
  116. package/src/security/types.ts +165 -0
  117. package/src/session/decorators.ts +45 -0
  118. package/src/session/index.ts +19 -0
  119. package/src/session/middleware.ts +143 -0
  120. package/src/session/service.ts +218 -0
  121. package/src/session/session-module.ts +69 -0
  122. package/src/session/types.ts +373 -0
  123. package/src/swagger/decorators.ts +133 -0
  124. package/src/swagger/generator.ts +234 -0
  125. package/src/swagger/index.ts +7 -0
  126. package/src/swagger/swagger-extension.ts +41 -0
  127. package/src/swagger/swagger-module.ts +83 -0
  128. package/src/swagger/types.ts +188 -0
  129. package/src/swagger/ui.ts +98 -0
  130. package/src/testing/harness.ts +96 -0
  131. package/src/validation/decorators.ts +95 -0
  132. package/src/validation/errors.ts +28 -0
  133. package/src/validation/index.ts +14 -0
  134. package/src/validation/types.ts +35 -0
  135. package/src/validation/validator.ts +63 -0
  136. package/src/websocket/decorators.ts +51 -0
  137. package/src/websocket/index.ts +12 -0
  138. package/src/websocket/registry.ts +133 -0
  139. package/tests/cache/cache-module.test.ts +212 -0
  140. package/tests/config/config-module.test.ts +151 -0
  141. package/tests/controller/controller.test.ts +189 -0
  142. package/tests/core/application.test.ts +57 -0
  143. package/tests/core/context-body.test.ts +44 -0
  144. package/tests/core/context.test.ts +86 -0
  145. package/tests/core/edge-cases.test.ts +432 -0
  146. package/tests/database/database-module.test.ts +385 -0
  147. package/tests/database/orm.test.ts +164 -0
  148. package/tests/database/postgres-mysql-integration.test.ts +395 -0
  149. package/tests/database/transaction.test.ts +238 -0
  150. package/tests/di/container.test.ts +264 -0
  151. package/tests/di/module.test.ts +128 -0
  152. package/tests/error/error-codes.test.ts +121 -0
  153. package/tests/error/error-handler.test.ts +68 -0
  154. package/tests/error/error-handling.test.ts +254 -0
  155. package/tests/error/http-exception.test.ts +37 -0
  156. package/tests/error/i18n-integration.test.ts +175 -0
  157. package/tests/extensions/logger-extension.test.ts +40 -0
  158. package/tests/files/static-middleware.test.ts +67 -0
  159. package/tests/files/upload-middleware.test.ts +43 -0
  160. package/tests/health/health-module.test.ts +116 -0
  161. package/tests/integration/application-router.test.ts +85 -0
  162. package/tests/integration/body-parsing.test.ts +88 -0
  163. package/tests/integration/cache-e2e.test.ts +114 -0
  164. package/tests/integration/oauth2-e2e.test.ts +615 -0
  165. package/tests/integration/session-e2e.test.ts +207 -0
  166. package/tests/metrics/metrics-module.test.ts +178 -0
  167. package/tests/middleware/builtin.test.ts +206 -0
  168. package/tests/middleware/file-upload.test.ts +41 -0
  169. package/tests/middleware/middleware.test.ts +120 -0
  170. package/tests/middleware/pipeline.test.ts +72 -0
  171. package/tests/middleware/rate-limit.test.ts +314 -0
  172. package/tests/middleware/static-file.test.ts +62 -0
  173. package/tests/perf/harness.test.ts +48 -0
  174. package/tests/perf/optimization.test.ts +183 -0
  175. package/tests/perf/regression.test.ts +120 -0
  176. package/tests/queue/queue-module.test.ts +217 -0
  177. package/tests/request/body-parser.test.ts +96 -0
  178. package/tests/request/response.test.ts +99 -0
  179. package/tests/router/decorators.test.ts +48 -0
  180. package/tests/router/registry.test.ts +51 -0
  181. package/tests/router/route.test.ts +71 -0
  182. package/tests/router/router-normalization.test.ts +106 -0
  183. package/tests/router/router.test.ts +133 -0
  184. package/tests/security/access-decision-manager.test.ts +84 -0
  185. package/tests/security/authentication-manager.test.ts +81 -0
  186. package/tests/security/context.test.ts +302 -0
  187. package/tests/security/filter.test.ts +225 -0
  188. package/tests/security/jwt-provider.test.ts +106 -0
  189. package/tests/security/oauth2-provider.test.ts +269 -0
  190. package/tests/security/security-module.test.ts +143 -0
  191. package/tests/session/session-module.test.ts +307 -0
  192. package/tests/stress/di-stress.test.ts +30 -0
  193. package/tests/swagger/decorators.test.ts +153 -0
  194. package/tests/swagger/generator.test.ts +202 -0
  195. package/tests/swagger/swagger-extension.test.ts +72 -0
  196. package/tests/swagger/swagger-module.test.ts +79 -0
  197. package/tests/utils/test-port.ts +10 -0
  198. package/tests/validation/controller-validation.test.ts +64 -0
  199. package/tests/validation/validation.test.ts +42 -0
  200. package/tests/websocket/gateway.test.ts +68 -0
@@ -0,0 +1,64 @@
1
+ import { Module, MODULE_METADATA_KEY, type ModuleProvider } from '../di/module';
2
+
3
+ import { QueueService } from './service';
4
+ import {
5
+ QUEUE_OPTIONS_TOKEN,
6
+ QUEUE_SERVICE_TOKEN,
7
+ MemoryQueueStore,
8
+ type QueueModuleOptions,
9
+ type QueueStore,
10
+ } from './types';
11
+
12
+ @Module({
13
+ providers: [],
14
+ })
15
+ export class QueueModule {
16
+ /**
17
+ * 创建队列模块
18
+ * @param options - 模块配置
19
+ */
20
+ public static forRoot(
21
+ options: QueueModuleOptions = {},
22
+ ): typeof QueueModule {
23
+ const providers: ModuleProvider[] = [];
24
+
25
+ // 如果没有提供 store,使用默认的内存存储
26
+ const store: QueueStore = options.store ?? new MemoryQueueStore();
27
+
28
+ const service = new QueueService({
29
+ store,
30
+ defaultQueue: options.defaultQueue,
31
+ enableWorker: options.enableWorker,
32
+ concurrency: options.concurrency,
33
+ });
34
+
35
+ providers.push(
36
+ {
37
+ provide: QUEUE_SERVICE_TOKEN,
38
+ useValue: service,
39
+ },
40
+ {
41
+ provide: QUEUE_OPTIONS_TOKEN,
42
+ useValue: options,
43
+ },
44
+ QueueService,
45
+ );
46
+
47
+ // 动态更新模块元数据
48
+ const existingMetadata =
49
+ Reflect.getMetadata(MODULE_METADATA_KEY, QueueModule) || {};
50
+ const metadata = {
51
+ ...existingMetadata,
52
+ providers: [...(existingMetadata.providers || []), ...providers],
53
+ exports: [
54
+ ...(existingMetadata.exports || []),
55
+ QUEUE_SERVICE_TOKEN,
56
+ QUEUE_OPTIONS_TOKEN,
57
+ QueueService,
58
+ ],
59
+ };
60
+ Reflect.defineMetadata(MODULE_METADATA_KEY, metadata, QueueModule);
61
+
62
+ return QueueModule;
63
+ }
64
+ }
@@ -0,0 +1,302 @@
1
+ import { Injectable } from '../di/decorators';
2
+ import { Inject } from '../di/decorators';
3
+ import type {
4
+ QueueStore,
5
+ QueueModuleOptions,
6
+ Job,
7
+ JobData,
8
+ JobHandler,
9
+ JobOptions,
10
+ CronOptions,
11
+ } from './types';
12
+ import { QUEUE_OPTIONS_TOKEN } from './types';
13
+
14
+ /**
15
+ * 队列服务
16
+ */
17
+ @Injectable()
18
+ export class QueueService {
19
+ private store: QueueStore;
20
+ private defaultQueue: string;
21
+ private enableWorker: boolean;
22
+ private concurrency: number;
23
+ private workers: Map<string, Set<Promise<void>>> = new Map();
24
+ private cronJobs: Map<string, { handler: JobHandler; options: CronOptions }> =
25
+ new Map();
26
+ private cronTimers: Map<string, ReturnType<typeof setInterval>> = new Map();
27
+
28
+ public constructor(@Inject(QUEUE_OPTIONS_TOKEN) options: QueueModuleOptions) {
29
+ this.store = options.store!;
30
+ this.defaultQueue = options.defaultQueue ?? 'default';
31
+ this.enableWorker = options.enableWorker ?? true;
32
+ this.concurrency = options.concurrency ?? 1;
33
+
34
+ if (this.enableWorker) {
35
+ this.startWorker(this.defaultQueue);
36
+ }
37
+ }
38
+
39
+ /**
40
+ * 添加任务
41
+ * @param jobName - 任务名称
42
+ * @param data - 任务数据
43
+ * @param options - 任务选项
44
+ * @param queueName - 队列名称(可选,默认使用 defaultQueue)
45
+ * @returns 任务 ID
46
+ */
47
+ public async add<T = JobData>(
48
+ jobName: string,
49
+ data: T,
50
+ options?: JobOptions,
51
+ queueName?: string,
52
+ ): Promise<string> {
53
+ const queue = queueName ?? this.defaultQueue;
54
+ const jobId = await this.store.add(queue, {
55
+ name: jobName,
56
+ data,
57
+ options,
58
+ });
59
+
60
+ // 如果启用了工作进程,确保队列有工作进程在运行
61
+ if (this.enableWorker && !this.workers.has(queue)) {
62
+ this.startWorker(queue);
63
+ }
64
+
65
+ return jobId;
66
+ }
67
+
68
+ /**
69
+ * 获取任务
70
+ * @param jobId - 任务 ID
71
+ * @param queueName - 队列名称(可选)
72
+ * @returns 任务,如果不存在则返回 undefined
73
+ */
74
+ public async get<T = JobData>(
75
+ jobId: string,
76
+ queueName?: string,
77
+ ): Promise<Job<T> | undefined> {
78
+ const queue = queueName ?? this.defaultQueue;
79
+ return this.store.get<T>(queue, jobId);
80
+ }
81
+
82
+ /**
83
+ * 删除任务
84
+ * @param jobId - 任务 ID
85
+ * @param queueName - 队列名称(可选)
86
+ * @returns 是否删除成功
87
+ */
88
+ public async delete(jobId: string, queueName?: string): Promise<boolean> {
89
+ const queue = queueName ?? this.defaultQueue;
90
+ return this.store.delete(queue, jobId);
91
+ }
92
+
93
+ /**
94
+ * 清空队列
95
+ * @param queueName - 队列名称(可选)
96
+ * @returns 是否清空成功
97
+ */
98
+ public async clear(queueName?: string): Promise<boolean> {
99
+ const queue = queueName ?? this.defaultQueue;
100
+ return this.store.clear(queue);
101
+ }
102
+
103
+ /**
104
+ * 获取队列中的任务数量
105
+ * @param queueName - 队列名称(可选)
106
+ * @returns 任务数量
107
+ */
108
+ public async count(queueName?: string): Promise<number> {
109
+ const queue = queueName ?? this.defaultQueue;
110
+ return this.store.count(queue);
111
+ }
112
+
113
+ /**
114
+ * 注册任务处理器
115
+ * @param jobName - 任务名称
116
+ * @param handler - 任务处理器
117
+ * @param queueName - 队列名称(可选)
118
+ */
119
+ public async registerHandler<T = JobData>(
120
+ jobName: string,
121
+ handler: JobHandler<T>,
122
+ queueName?: string,
123
+ ): Promise<void> {
124
+ const queue = queueName ?? this.defaultQueue;
125
+ const key = `${queue}:${jobName}`;
126
+
127
+ // 存储处理器(实际实现中可能需要更复杂的注册机制)
128
+ // 这里简化处理,假设处理器可以通过任务名称找到
129
+ // 实际使用时,可能需要一个处理器注册表
130
+ (this as unknown as { handlers: Map<string, JobHandler<JobData>> }).handlers =
131
+ (this as unknown as { handlers: Map<string, JobHandler<JobData>> }).handlers ??
132
+ new Map();
133
+ (
134
+ this as unknown as { handlers: Map<string, JobHandler<JobData>> }
135
+ ).handlers.set(key, handler as JobHandler<JobData>);
136
+ }
137
+
138
+ /**
139
+ * 注册定时任务(Cron)
140
+ * @param jobName - 任务名称
141
+ * @param handler - 任务处理器
142
+ * @param options - Cron 配置
143
+ * @param queueName - 队列名称(可选)
144
+ */
145
+ public async registerCron<T = JobData>(
146
+ jobName: string,
147
+ handler: JobHandler<T>,
148
+ options: CronOptions,
149
+ queueName?: string,
150
+ ): Promise<void> {
151
+ const queue = queueName ?? this.defaultQueue;
152
+ const key = `${queue}:${jobName}`;
153
+
154
+ this.cronJobs.set(key, { handler: handler as JobHandler<JobData>, options });
155
+
156
+ // 如果设置了立即执行,先执行一次
157
+ if (options.runOnInit) {
158
+ await this.add(jobName, {} as T, undefined, queue);
159
+ }
160
+
161
+ // 解析 Cron 表达式并设置定时器
162
+ // 注意:这里简化实现,实际应该使用 cron 解析库
163
+ const interval = this.parseCronInterval(options.pattern);
164
+ if (interval > 0) {
165
+ const timer = setInterval(async () => {
166
+ await this.add(jobName, {} as T, undefined, queue);
167
+ }, interval);
168
+ this.cronTimers.set(key, timer);
169
+ }
170
+ }
171
+
172
+ /**
173
+ * 启动工作进程
174
+ * @param queueName - 队列名称
175
+ */
176
+ private startWorker(queueName: string): void {
177
+ if (this.workers.has(queueName)) {
178
+ return;
179
+ }
180
+
181
+ const workerSet = new Set<Promise<void>>();
182
+ this.workers.set(queueName, workerSet);
183
+
184
+ // 启动并发工作进程
185
+ for (let i = 0; i < this.concurrency; i++) {
186
+ const worker = this.processQueue(queueName);
187
+ workerSet.add(worker);
188
+ }
189
+ }
190
+
191
+ /**
192
+ * 处理队列
193
+ * @param queueName - 队列名称
194
+ */
195
+ private async processQueue(queueName: string): Promise<void> {
196
+ while (true) {
197
+ try {
198
+ const job = await this.store.getNext(queueName);
199
+ if (!job) {
200
+ // 没有任务,等待一段时间后重试
201
+ await new Promise((resolve) => setTimeout(resolve, 1000));
202
+ continue;
203
+ }
204
+
205
+ // 更新任务状态为 active
206
+ await this.store.updateStatus(queueName, job.id, 'active');
207
+
208
+ try {
209
+ // 获取处理器
210
+ const handler = this.getHandler(job.name, queueName);
211
+ if (handler) {
212
+ await handler(job as Job<JobData>);
213
+ await this.store.updateStatus(queueName, job.id, 'completed');
214
+ } else {
215
+ // 没有找到处理器,标记为失败
216
+ await this.store.updateStatus(queueName, job.id, 'failed');
217
+ }
218
+ } catch (error) {
219
+ // 处理任务失败
220
+ await this.store.updateStatus(queueName, job.id, 'failed');
221
+ // 可以在这里添加重试逻辑
222
+ console.error(`Job ${job.id} failed:`, error);
223
+ }
224
+ } catch (error) {
225
+ console.error(`Error processing queue ${queueName}:`, error);
226
+ await new Promise((resolve) => setTimeout(resolve, 1000));
227
+ }
228
+ }
229
+ }
230
+
231
+ /**
232
+ * 获取处理器
233
+ * @param jobName - 任务名称
234
+ * @param queueName - 队列名称
235
+ * @returns 处理器,如果不存在则返回 undefined
236
+ */
237
+ private getHandler(
238
+ jobName: string,
239
+ queueName: string,
240
+ ): JobHandler<JobData> | undefined {
241
+ const key = `${queueName}:${jobName}`;
242
+ return (
243
+ this as unknown as { handlers: Map<string, JobHandler<JobData>> }
244
+ ).handlers?.get(key);
245
+ }
246
+
247
+ /**
248
+ * 解析 Cron 表达式为间隔时间(毫秒)
249
+ * 注意:这是简化实现,实际应该使用专业的 cron 解析库
250
+ * @param pattern - Cron 表达式
251
+ * @returns 间隔时间(毫秒),如果无法解析则返回 -1
252
+ */
253
+ private parseCronInterval(pattern: string): number {
254
+ // 简化实现:只支持简单的格式
255
+ // 实际应该使用 cron-parser 等库
256
+ const parts = pattern.trim().split(/\s+/);
257
+ if (parts.length !== 5) {
258
+ return -1;
259
+ }
260
+
261
+ const [minute, hour, day, month, weekday] = parts;
262
+
263
+ // 如果所有字段都是 *,表示每分钟执行
264
+ if (
265
+ minute === '*' &&
266
+ hour === '*' &&
267
+ day === '*' &&
268
+ month === '*' &&
269
+ weekday === '*'
270
+ ) {
271
+ return 60000; // 1 分钟
272
+ }
273
+
274
+ // 如果分钟是数字,其他都是 *,表示每 N 分钟执行
275
+ if (
276
+ /^\d+$/.test(minute) &&
277
+ hour === '*' &&
278
+ day === '*' &&
279
+ month === '*' &&
280
+ weekday === '*'
281
+ ) {
282
+ return parseInt(minute, 10) * 60000;
283
+ }
284
+
285
+ // 默认返回 -1,表示需要更复杂的解析
286
+ // 实际实现中应该使用 cron-parser
287
+ return -1;
288
+ }
289
+
290
+ /**
291
+ * 销毁服务,清理资源
292
+ */
293
+ public destroy(): void {
294
+ // 清理所有定时器
295
+ for (const timer of this.cronTimers.values()) {
296
+ clearInterval(timer);
297
+ }
298
+ this.cronTimers.clear();
299
+ this.cronJobs.clear();
300
+ this.workers.clear();
301
+ }
302
+ }
@@ -0,0 +1,341 @@
1
+ /**
2
+ * 任务数据
3
+ */
4
+ export interface JobData {
5
+ [key: string]: unknown;
6
+ }
7
+
8
+ /**
9
+ * 任务选项
10
+ */
11
+ export interface JobOptions {
12
+ /**
13
+ * 任务延迟(毫秒)
14
+ */
15
+ delay?: number;
16
+
17
+ /**
18
+ * 任务优先级(数字越大优先级越高)
19
+ */
20
+ priority?: number;
21
+
22
+ /**
23
+ * 任务重试次数
24
+ */
25
+ attempts?: number;
26
+
27
+ /**
28
+ * 任务重试延迟(毫秒)
29
+ */
30
+ backoff?: {
31
+ type: 'fixed' | 'exponential';
32
+ delay: number;
33
+ };
34
+
35
+ /**
36
+ * 任务超时时间(毫秒)
37
+ */
38
+ timeout?: number;
39
+
40
+ /**
41
+ * 任务 ID(如果不提供则自动生成)
42
+ */
43
+ jobId?: string;
44
+
45
+ /**
46
+ * 是否移除任务(如果已完成)
47
+ */
48
+ removeOnComplete?: boolean | number;
49
+
50
+ /**
51
+ * 是否移除任务(如果失败)
52
+ */
53
+ removeOnFail?: boolean | number;
54
+ }
55
+
56
+ /**
57
+ * 任务
58
+ */
59
+ export interface Job<T = JobData> {
60
+ /**
61
+ * 任务 ID
62
+ */
63
+ id: string;
64
+
65
+ /**
66
+ * 任务名称
67
+ */
68
+ name: string;
69
+
70
+ /**
71
+ * 任务数据
72
+ */
73
+ data: T;
74
+
75
+ /**
76
+ * 任务选项
77
+ */
78
+ options?: JobOptions;
79
+
80
+ /**
81
+ * 任务状态
82
+ */
83
+ status: 'waiting' | 'active' | 'completed' | 'failed' | 'delayed';
84
+
85
+ /**
86
+ * 创建时间
87
+ */
88
+ createdAt: number;
89
+
90
+ /**
91
+ * 更新时间
92
+ */
93
+ updatedAt: number;
94
+ }
95
+
96
+ /**
97
+ * 任务处理器
98
+ */
99
+ export type JobHandler<T = JobData> = (job: Job<T>) => Promise<void> | void;
100
+
101
+ /**
102
+ * 队列存储接口
103
+ */
104
+ export interface QueueStore {
105
+ /**
106
+ * 添加任务
107
+ * @param queueName - 队列名称
108
+ * @param job - 任务
109
+ * @returns 任务 ID
110
+ */
111
+ add<T = JobData>(
112
+ queueName: string,
113
+ job: Omit<Job<T>, 'id' | 'status' | 'createdAt' | 'updatedAt'>,
114
+ ): Promise<string>;
115
+
116
+ /**
117
+ * 获取任务
118
+ * @param queueName - 队列名称
119
+ * @param jobId - 任务 ID
120
+ * @returns 任务,如果不存在则返回 undefined
121
+ */
122
+ get<T = JobData>(queueName: string, jobId: string): Promise<Job<T> | undefined>;
123
+
124
+ /**
125
+ * 获取下一个待处理的任务
126
+ * @param queueName - 队列名称
127
+ * @returns 任务,如果没有则返回 undefined
128
+ */
129
+ getNext<T = JobData>(queueName: string): Promise<Job<T> | undefined>;
130
+
131
+ /**
132
+ * 更新任务状态
133
+ * @param queueName - 队列名称
134
+ * @param jobId - 任务 ID
135
+ * @param status - 新状态
136
+ * @returns 是否更新成功
137
+ */
138
+ updateStatus(
139
+ queueName: string,
140
+ jobId: string,
141
+ status: Job['status'],
142
+ ): Promise<boolean>;
143
+
144
+ /**
145
+ * 删除任务
146
+ * @param queueName - 队列名称
147
+ * @param jobId - 任务 ID
148
+ * @returns 是否删除成功
149
+ */
150
+ delete(queueName: string, jobId: string): Promise<boolean>;
151
+
152
+ /**
153
+ * 清空队列
154
+ * @param queueName - 队列名称
155
+ * @returns 是否清空成功
156
+ */
157
+ clear(queueName: string): Promise<boolean>;
158
+
159
+ /**
160
+ * 获取队列中的任务数量
161
+ * @param queueName - 队列名称
162
+ * @returns 任务数量
163
+ */
164
+ count(queueName: string): Promise<number>;
165
+ }
166
+
167
+ /**
168
+ * 内存队列存储实现
169
+ */
170
+ export class MemoryQueueStore implements QueueStore {
171
+ private queues: Map<string, Map<string, Job>> = new Map();
172
+ private jobCounter = 0;
173
+
174
+ public async add<T = JobData>(
175
+ queueName: string,
176
+ job: Omit<Job<T>, 'id' | 'status' | 'createdAt' | 'updatedAt'>,
177
+ ): Promise<string> {
178
+ const queue = this.getQueue(queueName);
179
+ const jobId = job.options?.jobId ?? `job_${++this.jobCounter}_${Date.now()}`;
180
+ const now = Date.now();
181
+
182
+ const fullJob: Job<T> = {
183
+ id: jobId,
184
+ name: job.name,
185
+ data: job.data,
186
+ options: job.options,
187
+ status: job.options?.delay ? 'delayed' : 'waiting',
188
+ createdAt: now,
189
+ updatedAt: now,
190
+ };
191
+
192
+ queue.set(jobId, fullJob as Job<JobData>);
193
+ return jobId;
194
+ }
195
+
196
+ public async get<T = JobData>(
197
+ queueName: string,
198
+ jobId: string,
199
+ ): Promise<Job<T> | undefined> {
200
+ const queue = this.getQueue(queueName);
201
+ return queue.get(jobId) as Job<T> | undefined;
202
+ }
203
+
204
+ public async getNext<T = JobData>(
205
+ queueName: string,
206
+ ): Promise<Job<T> | undefined> {
207
+ const queue = this.getQueue(queueName);
208
+ const now = Date.now();
209
+
210
+ // 查找下一个待处理的任务(优先处理 delayed 且已到期的任务,然后处理 waiting 的任务)
211
+ let nextJob: Job<T> | undefined;
212
+ let highestPriority = -Infinity;
213
+
214
+ for (const job of queue.values()) {
215
+ // 检查 delayed 任务是否到期
216
+ if (job.status === 'delayed' && job.options?.delay) {
217
+ const delayEndTime = job.createdAt + job.options.delay;
218
+ if (now >= delayEndTime) {
219
+ if (
220
+ (job.options.priority ?? 0) > highestPriority ||
221
+ !nextJob
222
+ ) {
223
+ nextJob = job as Job<T>;
224
+ highestPriority = job.options?.priority ?? 0;
225
+ }
226
+ }
227
+ } else if (job.status === 'waiting') {
228
+ if (
229
+ (job.options?.priority ?? 0) > highestPriority ||
230
+ !nextJob
231
+ ) {
232
+ nextJob = job as Job<T>;
233
+ highestPriority = job.options?.priority ?? 0;
234
+ }
235
+ }
236
+ }
237
+
238
+ return nextJob;
239
+ }
240
+
241
+ public async updateStatus(
242
+ queueName: string,
243
+ jobId: string,
244
+ status: Job['status'],
245
+ ): Promise<boolean> {
246
+ const queue = this.getQueue(queueName);
247
+ const job = queue.get(jobId);
248
+ if (!job) {
249
+ return false;
250
+ }
251
+
252
+ job.status = status;
253
+ job.updatedAt = Date.now();
254
+ return true;
255
+ }
256
+
257
+ public async delete(queueName: string, jobId: string): Promise<boolean> {
258
+ const queue = this.getQueue(queueName);
259
+ return queue.delete(jobId);
260
+ }
261
+
262
+ public async clear(queueName: string): Promise<boolean> {
263
+ const queue = this.getQueue(queueName);
264
+ queue.clear();
265
+ return true;
266
+ }
267
+
268
+ public async count(queueName: string): Promise<number> {
269
+ const queue = this.getQueue(queueName);
270
+ return queue.size;
271
+ }
272
+
273
+ private getQueue(queueName: string): Map<string, Job<JobData>> {
274
+ if (!this.queues.has(queueName)) {
275
+ this.queues.set(queueName, new Map());
276
+ }
277
+ return this.queues.get(queueName)!;
278
+ }
279
+ }
280
+
281
+ /**
282
+ * Cron 表达式配置
283
+ */
284
+ export interface CronOptions {
285
+ /**
286
+ * Cron 表达式(支持标准 5 字段格式:分 时 日 月 周)
287
+ * 例如:'0 0 * * *' 表示每天午夜执行
288
+ */
289
+ pattern: string;
290
+
291
+ /**
292
+ * 时区
293
+ * @default 'UTC'
294
+ */
295
+ timezone?: string;
296
+
297
+ /**
298
+ * 是否立即执行一次
299
+ * @default false
300
+ */
301
+ runOnInit?: boolean;
302
+ }
303
+
304
+ /**
305
+ * QueueModule 配置选项
306
+ */
307
+ export interface QueueModuleOptions {
308
+ /**
309
+ * 队列存储实现
310
+ * @default MemoryQueueStore
311
+ */
312
+ store?: QueueStore;
313
+
314
+ /**
315
+ * 默认队列名称
316
+ * @default 'default'
317
+ */
318
+ defaultQueue?: string;
319
+
320
+ /**
321
+ * 是否启用工作进程
322
+ * @default true
323
+ */
324
+ enableWorker?: boolean;
325
+
326
+ /**
327
+ * 工作进程并发数
328
+ * @default 1
329
+ */
330
+ concurrency?: number;
331
+ }
332
+
333
+ /**
334
+ * QueueService Token
335
+ */
336
+ export const QUEUE_SERVICE_TOKEN = Symbol('@dangao/bun-server:queue:service');
337
+
338
+ /**
339
+ * QueueModule Options Token
340
+ */
341
+ export const QUEUE_OPTIONS_TOKEN = Symbol('@dangao/bun-server:queue:options');