@dangao/bun-server 3.2.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/sqlite-adapter.d.ts +4 -2
- package/dist/database/sqlite-adapter.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 +63 -25
- package/dist/index.node.mjs +63 -25
- 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/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/sqlite-adapter.ts +4 -3
- 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/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
|
@@ -15,11 +15,11 @@ export abstract class BaseInterceptor implements Interceptor {
|
|
|
15
15
|
public abstract execute<T>(
|
|
16
16
|
target: unknown,
|
|
17
17
|
propertyKey: string | symbol,
|
|
18
|
-
originalMethod: (...args:
|
|
19
|
-
args:
|
|
18
|
+
originalMethod: (...args: any[]) => T | Promise<T>,
|
|
19
|
+
args: any[],
|
|
20
20
|
container: Container,
|
|
21
21
|
context?: Context,
|
|
22
|
-
): Promise<
|
|
22
|
+
): Promise<any>;
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
25
|
* 前置处理(可选)
|
|
@@ -33,7 +33,7 @@ export abstract class BaseInterceptor implements Interceptor {
|
|
|
33
33
|
protected async before(
|
|
34
34
|
target: unknown,
|
|
35
35
|
propertyKey: string | symbol,
|
|
36
|
-
args:
|
|
36
|
+
args: any[],
|
|
37
37
|
container: Container,
|
|
38
38
|
context?: Context,
|
|
39
39
|
): Promise<void> {
|
|
@@ -141,7 +141,7 @@ export abstract class BaseInterceptor implements Interceptor {
|
|
|
141
141
|
*/
|
|
142
142
|
protected resolveService<T>(
|
|
143
143
|
container: Container,
|
|
144
|
-
token: (new (...args:
|
|
144
|
+
token: (new (...args: any[]) => T) | string | symbol,
|
|
145
145
|
): T {
|
|
146
146
|
return container.resolve<T>(token);
|
|
147
147
|
}
|
|
@@ -200,4 +200,3 @@ export abstract class BaseInterceptor implements Interceptor {
|
|
|
200
200
|
return context.getParam(paramName);
|
|
201
201
|
}
|
|
202
202
|
}
|
|
203
|
-
|
|
@@ -84,8 +84,8 @@ export class LogInterceptor extends BaseInterceptor {
|
|
|
84
84
|
public async execute<T>(
|
|
85
85
|
target: unknown,
|
|
86
86
|
propertyKey: string | symbol,
|
|
87
|
-
originalMethod: (...args:
|
|
88
|
-
args:
|
|
87
|
+
originalMethod: (...args: any[]) => T | Promise<T>,
|
|
88
|
+
args: any[],
|
|
89
89
|
container: Container,
|
|
90
90
|
context?: Context,
|
|
91
91
|
): Promise<T> {
|
|
@@ -175,4 +175,3 @@ export class LogInterceptor extends BaseInterceptor {
|
|
|
175
175
|
}
|
|
176
176
|
}
|
|
177
177
|
}
|
|
178
|
-
|
|
@@ -91,8 +91,8 @@ export class PermissionInterceptor 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> {
|
|
@@ -170,4 +170,3 @@ export class PermissionInterceptor extends BaseInterceptor {
|
|
|
170
170
|
return null;
|
|
171
171
|
}
|
|
172
172
|
}
|
|
173
|
-
|
|
@@ -22,8 +22,8 @@ export class InterceptorChain {
|
|
|
22
22
|
interceptors: Interceptor[],
|
|
23
23
|
target: unknown,
|
|
24
24
|
propertyKey: string | symbol,
|
|
25
|
-
originalMethod: (...args:
|
|
26
|
-
args:
|
|
25
|
+
originalMethod: (...args: any[]) => T | Promise<T>,
|
|
26
|
+
args: any[],
|
|
27
27
|
container: Container,
|
|
28
28
|
context?: Context,
|
|
29
29
|
): Promise<T> {
|
|
@@ -36,7 +36,7 @@ export class InterceptorChain {
|
|
|
36
36
|
let index = 0;
|
|
37
37
|
let currentArgs = args; // Track arguments that might be modified by interceptors
|
|
38
38
|
|
|
39
|
-
const next = async (modifiedArgs?:
|
|
39
|
+
const next = async (modifiedArgs?: any[]): Promise<T> => {
|
|
40
40
|
// 如果拦截器传递了新参数,使用新参数;否则使用当前参数
|
|
41
41
|
if (modifiedArgs && modifiedArgs.length > 0) {
|
|
42
42
|
currentArgs = modifiedArgs;
|
|
@@ -50,11 +50,11 @@ export class InterceptorChain {
|
|
|
50
50
|
const interceptor = interceptors[index++];
|
|
51
51
|
|
|
52
52
|
// 执行当前拦截器,传递 next 作为下一个执行函数
|
|
53
|
-
// 注意:拦截器接口要求 originalMethod 的类型是 (...args:
|
|
53
|
+
// 注意:拦截器接口要求 originalMethod 的类型是 (...args: any[]) => T | Promise<T>
|
|
54
54
|
// 这允许原始方法可以是同步(返回 T)或异步(返回 Promise<T>)
|
|
55
55
|
// 虽然 next 函数是异步的(总是返回 Promise<T>),但我们保持类型签名为 T | Promise<T>
|
|
56
56
|
// 以符合拦截器接口的要求。拦截器应该使用 Promise.resolve() 来统一处理同步和异步返回值
|
|
57
|
-
const wrappedNext = (...nextArgs:
|
|
57
|
+
const wrappedNext = (...nextArgs: any[]): T | Promise<T> => {
|
|
58
58
|
// 如果拦截器传递了新参数,传递给 next;否则传递 undefined(使用当前参数)
|
|
59
59
|
// next 是异步函数,总是返回 Promise<T>,但类型签名允许 T | Promise<T>
|
|
60
60
|
const result = next(nextArgs.length > 0 ? nextArgs : undefined);
|
|
@@ -70,10 +70,9 @@ export class InterceptorChain {
|
|
|
70
70
|
currentArgs, // Pass current args to the interceptor
|
|
71
71
|
container,
|
|
72
72
|
context,
|
|
73
|
-
);
|
|
73
|
+
) as T;
|
|
74
74
|
};
|
|
75
75
|
|
|
76
76
|
return await next();
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
|
-
|
|
@@ -22,8 +22,11 @@ export class InterceptorRegistry {
|
|
|
22
22
|
public register(
|
|
23
23
|
metadataKey: symbol,
|
|
24
24
|
interceptor: Interceptor,
|
|
25
|
-
priority: number = 100,
|
|
25
|
+
priority: number | { priority?: number } = 100,
|
|
26
26
|
): void {
|
|
27
|
+
const resolvedPriority = typeof priority === 'number'
|
|
28
|
+
? priority
|
|
29
|
+
: priority.priority ?? 100;
|
|
27
30
|
if (!this.interceptors.has(metadataKey)) {
|
|
28
31
|
this.interceptors.set(metadataKey, []);
|
|
29
32
|
}
|
|
@@ -39,7 +42,7 @@ export class InterceptorRegistry {
|
|
|
39
42
|
metadataList.push({
|
|
40
43
|
metadataKey,
|
|
41
44
|
interceptor,
|
|
42
|
-
priority,
|
|
45
|
+
priority: resolvedPriority,
|
|
43
46
|
});
|
|
44
47
|
|
|
45
48
|
// 按优先级排序(数字越小优先级越高)
|
|
@@ -129,4 +132,3 @@ export class InterceptorRegistry {
|
|
|
129
132
|
return total;
|
|
130
133
|
}
|
|
131
134
|
}
|
|
132
|
-
|
package/src/interceptor/types.ts
CHANGED
|
@@ -6,6 +6,12 @@ import type { Context } from '../core/context';
|
|
|
6
6
|
* 定义拦截器的核心执行方法
|
|
7
7
|
*/
|
|
8
8
|
export interface Interceptor {
|
|
9
|
+
/**
|
|
10
|
+
* Optional priority used by registries/tests that carry interceptor metadata
|
|
11
|
+
* alongside the executable interceptor instance.
|
|
12
|
+
*/
|
|
13
|
+
priority?: number;
|
|
14
|
+
|
|
9
15
|
/**
|
|
10
16
|
* 执行拦截器逻辑
|
|
11
17
|
* @param target - 目标对象(控制器实例的原型)
|
|
@@ -19,11 +25,11 @@ export interface Interceptor {
|
|
|
19
25
|
execute<T>(
|
|
20
26
|
target: unknown,
|
|
21
27
|
propertyKey: string | symbol,
|
|
22
|
-
originalMethod: (...args:
|
|
23
|
-
args:
|
|
28
|
+
originalMethod: (...args: any[]) => T | Promise<T>,
|
|
29
|
+
args: any[],
|
|
24
30
|
container: Container,
|
|
25
31
|
context?: Context,
|
|
26
|
-
): Promise<
|
|
32
|
+
): Promise<any>;
|
|
27
33
|
}
|
|
28
34
|
|
|
29
35
|
/**
|
|
@@ -49,4 +55,3 @@ export interface InterceptorMetadata {
|
|
|
49
55
|
* 拦截器注册表 Token
|
|
50
56
|
*/
|
|
51
57
|
export const INTERCEPTOR_REGISTRY_TOKEN = Symbol('@dangao/bun-server:interceptor-registry');
|
|
52
|
-
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ServiceInstance } from '../service-registry/types';
|
|
2
|
+
export type { ServiceInstance } from '../service-registry/types';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* 负载均衡策略类型
|
|
@@ -183,4 +184,3 @@ export class ServiceCallError extends Error {
|
|
|
183
184
|
this.name = 'ServiceCallError';
|
|
184
185
|
}
|
|
185
186
|
}
|
|
186
|
-
|
|
@@ -172,14 +172,27 @@ export class Tracer {
|
|
|
172
172
|
/**
|
|
173
173
|
* 添加 Span 事件
|
|
174
174
|
*/
|
|
175
|
-
public addSpanEvent(
|
|
175
|
+
public addSpanEvent(
|
|
176
|
+
spanId: SpanId,
|
|
177
|
+
event: string,
|
|
178
|
+
attributes?: Record<string, string | number | boolean>,
|
|
179
|
+
): void;
|
|
180
|
+
public addSpanEvent(spanId: SpanId, event: Omit<SpanEvent, 'timestamp'>): void;
|
|
181
|
+
public addSpanEvent(
|
|
182
|
+
spanId: SpanId,
|
|
183
|
+
event: string | Omit<SpanEvent, 'timestamp'>,
|
|
184
|
+
attributes?: Record<string, string | number | boolean>,
|
|
185
|
+
): void {
|
|
176
186
|
const span = this.activeSpans.get(spanId);
|
|
177
187
|
if (span) {
|
|
178
188
|
if (!span.events) {
|
|
179
189
|
span.events = [];
|
|
180
190
|
}
|
|
191
|
+
const spanEvent = typeof event === 'string'
|
|
192
|
+
? { name: event, attributes }
|
|
193
|
+
: event;
|
|
181
194
|
span.events.push({
|
|
182
|
-
...
|
|
195
|
+
...spanEvent,
|
|
183
196
|
timestamp: Date.now(),
|
|
184
197
|
});
|
|
185
198
|
}
|
|
@@ -290,4 +303,3 @@ export class Tracer {
|
|
|
290
303
|
this.activeSpans.clear();
|
|
291
304
|
}
|
|
292
305
|
}
|
|
293
|
-
|
|
@@ -3,13 +3,15 @@ import { FileHandler } from '../../request/file-handler';
|
|
|
3
3
|
|
|
4
4
|
export interface FileUploadOptions {
|
|
5
5
|
maxSize?: number;
|
|
6
|
+
maxFileSize?: number;
|
|
7
|
+
uploadDir?: string;
|
|
6
8
|
}
|
|
7
9
|
|
|
8
10
|
/**
|
|
9
11
|
* 简单的文件上传中间件:解析 multipart/form-data 并将文件附加到 context.body
|
|
10
12
|
*/
|
|
11
13
|
export function createFileUploadMiddleware(options: FileUploadOptions = {}): Middleware {
|
|
12
|
-
const maxSize = options.maxSize ?? 10 * 1024 * 1024;
|
|
14
|
+
const maxSize = options.maxSize ?? options.maxFileSize ?? 10 * 1024 * 1024;
|
|
13
15
|
|
|
14
16
|
return async (context, next) => {
|
|
15
17
|
const contentType = context.getHeader('Content-Type');
|
|
@@ -39,4 +41,3 @@ export function createFileUploadMiddleware(options: FileUploadOptions = {}): Mid
|
|
|
39
41
|
};
|
|
40
42
|
}
|
|
41
43
|
|
|
42
|
-
|
|
@@ -88,7 +88,12 @@ export interface RateLimitOptions {
|
|
|
88
88
|
/**
|
|
89
89
|
* 时间窗口内的最大请求数
|
|
90
90
|
*/
|
|
91
|
-
max
|
|
91
|
+
max?: number;
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* 时间窗口内的最大请求数别名,兼容装饰器早期写法。
|
|
95
|
+
*/
|
|
96
|
+
limit?: number;
|
|
92
97
|
|
|
93
98
|
/**
|
|
94
99
|
* 时间窗口(毫秒)
|
|
@@ -108,6 +113,11 @@ export interface RateLimitOptions {
|
|
|
108
113
|
*/
|
|
109
114
|
keyGenerator?: (context: Context) => string | Promise<string>;
|
|
110
115
|
|
|
116
|
+
/**
|
|
117
|
+
* 返回 true 时跳过限流。
|
|
118
|
+
*/
|
|
119
|
+
skip?: (context: Context) => boolean | Promise<boolean>;
|
|
120
|
+
|
|
111
121
|
/**
|
|
112
122
|
* 是否跳过成功响应(只对错误响应计数)
|
|
113
123
|
* @default false
|
|
@@ -157,9 +167,11 @@ function defaultKeyGenerator(context: Context): string {
|
|
|
157
167
|
export function createRateLimitMiddleware(options: RateLimitOptions): Middleware {
|
|
158
168
|
const {
|
|
159
169
|
max,
|
|
170
|
+
limit,
|
|
160
171
|
windowMs = 60000, // 默认 1 分钟
|
|
161
172
|
store = new MemoryRateLimitStore(),
|
|
162
173
|
keyGenerator = defaultKeyGenerator,
|
|
174
|
+
skip,
|
|
163
175
|
skipSuccessfulRequests = false,
|
|
164
176
|
skipFailedRequests = false,
|
|
165
177
|
message = 'Too Many Requests',
|
|
@@ -167,31 +179,36 @@ export function createRateLimitMiddleware(options: RateLimitOptions): Middleware
|
|
|
167
179
|
standardHeaders = true,
|
|
168
180
|
legacyHeaders = true,
|
|
169
181
|
} = options;
|
|
182
|
+
const requestLimit = limit ?? max ?? 100;
|
|
170
183
|
|
|
171
184
|
return async (context: Context, next) => {
|
|
185
|
+
if (skip && await skip(context)) {
|
|
186
|
+
return await next();
|
|
187
|
+
}
|
|
188
|
+
|
|
172
189
|
// 生成限流键
|
|
173
190
|
const key = await keyGenerator(context);
|
|
174
191
|
const currentCount = await store.increment(key, windowMs);
|
|
175
192
|
|
|
176
193
|
// 计算剩余请求数和重置时间
|
|
177
|
-
const remaining = Math.max(0,
|
|
194
|
+
const remaining = Math.max(0, requestLimit - currentCount);
|
|
178
195
|
const resetTime = Date.now() + windowMs;
|
|
179
196
|
|
|
180
197
|
// 设置响应头
|
|
181
198
|
if (standardHeaders) {
|
|
182
|
-
context.setHeader('RateLimit-Limit',
|
|
199
|
+
context.setHeader('RateLimit-Limit', requestLimit.toString());
|
|
183
200
|
context.setHeader('RateLimit-Remaining', remaining.toString());
|
|
184
201
|
context.setHeader('RateLimit-Reset', Math.ceil(resetTime / 1000).toString());
|
|
185
202
|
}
|
|
186
203
|
|
|
187
204
|
if (legacyHeaders) {
|
|
188
|
-
context.setHeader('X-RateLimit-Limit',
|
|
205
|
+
context.setHeader('X-RateLimit-Limit', requestLimit.toString());
|
|
189
206
|
context.setHeader('X-RateLimit-Remaining', remaining.toString());
|
|
190
207
|
context.setHeader('X-RateLimit-Reset', Math.ceil(resetTime / 1000).toString());
|
|
191
208
|
}
|
|
192
209
|
|
|
193
210
|
// 检查是否超过限制
|
|
194
|
-
if (currentCount >
|
|
211
|
+
if (currentCount > requestLimit) {
|
|
195
212
|
context.setStatus(statusCode);
|
|
196
213
|
return context.createErrorResponse({
|
|
197
214
|
error: message,
|
package/src/queue/service.ts
CHANGED
|
@@ -17,7 +17,7 @@ import { getRuntime } from '../platform/runtime';
|
|
|
17
17
|
*/
|
|
18
18
|
@Injectable()
|
|
19
19
|
export class QueueService {
|
|
20
|
-
|
|
20
|
+
public readonly store: QueueStore;
|
|
21
21
|
private defaultQueue: string;
|
|
22
22
|
private enableWorker: boolean;
|
|
23
23
|
private concurrency: number;
|
package/src/queue/types.ts
CHANGED
|
@@ -24,6 +24,11 @@ export interface JobOptions {
|
|
|
24
24
|
*/
|
|
25
25
|
attempts?: number;
|
|
26
26
|
|
|
27
|
+
/**
|
|
28
|
+
* 任务重试次数别名,兼容早期 API。
|
|
29
|
+
*/
|
|
30
|
+
retries?: number;
|
|
31
|
+
|
|
27
32
|
/**
|
|
28
33
|
* 任务重试延迟(毫秒)
|
|
29
34
|
*/
|
|
@@ -90,7 +95,22 @@ export interface Job<T = JobData> {
|
|
|
90
95
|
/**
|
|
91
96
|
* 更新时间
|
|
92
97
|
*/
|
|
93
|
-
updatedAt
|
|
98
|
+
updatedAt?: number;
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* 完成结果
|
|
102
|
+
*/
|
|
103
|
+
result?: unknown;
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* 完成时间
|
|
107
|
+
*/
|
|
108
|
+
completedAt?: number;
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* 失败错误信息
|
|
112
|
+
*/
|
|
113
|
+
error?: string;
|
|
94
114
|
}
|
|
95
115
|
|
|
96
116
|
/**
|
|
@@ -141,6 +161,25 @@ export interface QueueStore {
|
|
|
141
161
|
status: Job['status'],
|
|
142
162
|
): Promise<boolean>;
|
|
143
163
|
|
|
164
|
+
complete?(
|
|
165
|
+
queueName: string,
|
|
166
|
+
jobId: string,
|
|
167
|
+
result?: unknown,
|
|
168
|
+
): Promise<boolean>;
|
|
169
|
+
|
|
170
|
+
fail?(
|
|
171
|
+
queueName: string,
|
|
172
|
+
jobId: string,
|
|
173
|
+
error: Error,
|
|
174
|
+
): Promise<boolean>;
|
|
175
|
+
|
|
176
|
+
getStats?(queueName: string): Promise<{
|
|
177
|
+
waiting: number;
|
|
178
|
+
active: number;
|
|
179
|
+
completed: number;
|
|
180
|
+
failed: number;
|
|
181
|
+
}>;
|
|
182
|
+
|
|
144
183
|
/**
|
|
145
184
|
* 删除任务
|
|
146
185
|
* @param queueName - 队列名称
|
package/src/router/decorators.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import 'reflect-metadata';
|
|
2
|
-
import type { RouteHandler } from './types';
|
|
3
2
|
import { RouteRegistry } from './registry';
|
|
4
3
|
import type { HttpMethod } from './types';
|
|
5
4
|
import { CONTROLLER_METADATA_KEY } from '../controller/controller';
|
|
@@ -15,7 +14,7 @@ export const ROUTE_METADATA_KEY = Symbol('route');
|
|
|
15
14
|
export interface RouteMetadata {
|
|
16
15
|
method: HttpMethod;
|
|
17
16
|
path: string;
|
|
18
|
-
handler:
|
|
17
|
+
handler: (...args: any[]) => unknown;
|
|
19
18
|
/**
|
|
20
19
|
* 属性键(用于控制器方法)
|
|
21
20
|
*/
|
|
@@ -75,7 +74,7 @@ function createRouteDecorator(method: HttpMethod, path: string = '') {
|
|
|
75
74
|
existingRoutes.push({
|
|
76
75
|
method,
|
|
77
76
|
path: path ?? '',
|
|
78
|
-
handler: descriptor.value as
|
|
77
|
+
handler: descriptor.value as (...args: any[]) => unknown,
|
|
79
78
|
propertyKey: propertyKeyStr || undefined,
|
|
80
79
|
});
|
|
81
80
|
Reflect.defineMetadata(ROUTE_METADATA_KEY, existingRoutes, target);
|
|
@@ -16,6 +16,8 @@ export interface CanActivate {
|
|
|
16
16
|
canActivate(context: ExecutionContext): boolean | Promise<boolean>;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
export type Guard = CanActivate;
|
|
20
|
+
|
|
19
21
|
/**
|
|
20
22
|
* HTTP 参数主机接口
|
|
21
23
|
* 提供 HTTP 请求相关的上下文信息
|
|
@@ -141,4 +143,3 @@ export const GUARD_REGISTRY_TOKEN = Symbol('@dangao/bun-server:guard-registry');
|
|
|
141
143
|
* Roles 元数据键
|
|
142
144
|
*/
|
|
143
145
|
export const ROLES_METADATA_KEY = Symbol('@dangao/bun-server:roles');
|
|
144
|
-
|
package/src/security/types.ts
CHANGED
package/src/session/service.ts
CHANGED
|
@@ -25,7 +25,7 @@ export class SessionService {
|
|
|
25
25
|
) {
|
|
26
26
|
this.store = options.store!;
|
|
27
27
|
this.name = options.name ?? 'sessionId';
|
|
28
|
-
this.maxAge = options.maxAge ?? 86400000; // 24 小时
|
|
28
|
+
this.maxAge = options.maxAge ?? options.ttl ?? 86400000; // 24 小时
|
|
29
29
|
this.rolling = options.rolling ?? true;
|
|
30
30
|
this.cookieOptions = {
|
|
31
31
|
secure: options.cookie?.secure ?? false,
|
package/src/session/types.ts
CHANGED
|
@@ -317,6 +317,11 @@ export interface SessionModuleOptions {
|
|
|
317
317
|
*/
|
|
318
318
|
maxAge?: number;
|
|
319
319
|
|
|
320
|
+
/**
|
|
321
|
+
* Session 最大存活时间别名。
|
|
322
|
+
*/
|
|
323
|
+
ttl?: number;
|
|
324
|
+
|
|
320
325
|
/**
|
|
321
326
|
* 是否在每次访问时更新过期时间
|
|
322
327
|
* @default true
|
|
@@ -327,6 +332,11 @@ export interface SessionModuleOptions {
|
|
|
327
332
|
* Cookie 选项
|
|
328
333
|
*/
|
|
329
334
|
cookie?: {
|
|
335
|
+
/**
|
|
336
|
+
* Cookie 名称别名,优先使用顶层 name。
|
|
337
|
+
*/
|
|
338
|
+
name?: string;
|
|
339
|
+
|
|
330
340
|
/**
|
|
331
341
|
* 是否只在 HTTPS 下发送
|
|
332
342
|
* @default false
|
|
@@ -50,13 +50,25 @@ export function ApiOperation(metadata: ApiOperationMetadata): MethodDecorator {
|
|
|
50
50
|
* 用于描述 API 参数
|
|
51
51
|
* @param metadata - 参数元数据
|
|
52
52
|
*/
|
|
53
|
-
export function ApiParam(metadata: ApiParamMetadata): ParameterDecorator {
|
|
54
|
-
|
|
53
|
+
export function ApiParam(metadata: ApiParamMetadata): ParameterDecorator & MethodDecorator {
|
|
54
|
+
const decorator = (
|
|
55
|
+
target: Object,
|
|
56
|
+
propertyKey: string | symbol | undefined,
|
|
57
|
+
descriptorOrParameterIndex: TypedPropertyDescriptor<any> | number,
|
|
58
|
+
): void => {
|
|
59
|
+
const paramMetadata: ApiParamMetadata = {
|
|
60
|
+
in: 'path',
|
|
61
|
+
...metadata,
|
|
62
|
+
};
|
|
55
63
|
const existingParams: Array<{ index: number; metadata: ApiParamMetadata }> =
|
|
56
64
|
Reflect.getMetadata(API_PARAM_METADATA_KEY, target as Object, propertyKey!) || [];
|
|
57
|
-
|
|
65
|
+
const index = typeof descriptorOrParameterIndex === 'number'
|
|
66
|
+
? descriptorOrParameterIndex
|
|
67
|
+
: -1;
|
|
68
|
+
existingParams.push({ index, metadata: paramMetadata });
|
|
58
69
|
Reflect.defineMetadata(API_PARAM_METADATA_KEY, existingParams, target as Object, propertyKey!);
|
|
59
70
|
};
|
|
71
|
+
return decorator as ParameterDecorator & MethodDecorator;
|
|
60
72
|
}
|
|
61
73
|
|
|
62
74
|
/**
|
|
@@ -130,4 +142,3 @@ export function getApiResponses(
|
|
|
130
142
|
): ApiResponseMetadata[] {
|
|
131
143
|
return Reflect.getMetadata(API_RESPONSE_METADATA_KEY, target as Object, propertyKey) || [];
|
|
132
144
|
}
|
|
133
|
-
|
package/src/swagger/generator.ts
CHANGED
|
@@ -139,7 +139,7 @@ export class SwaggerGenerator {
|
|
|
139
139
|
for (const match of pathParamMatches) {
|
|
140
140
|
const paramName = match[1];
|
|
141
141
|
// 检查是否已经有手动定义的参数
|
|
142
|
-
const existingParam = params.find((p) => p.metadata.name === paramName && p.metadata.in === 'path');
|
|
142
|
+
const existingParam = params.find((p) => p.metadata.name === paramName && (p.metadata.in ?? 'path') === 'path');
|
|
143
143
|
if (!existingParam) {
|
|
144
144
|
// 自动添加路径参数
|
|
145
145
|
pathParams.push({
|
|
@@ -155,7 +155,7 @@ export class SwaggerGenerator {
|
|
|
155
155
|
for (const param of params) {
|
|
156
156
|
pathParams.push({
|
|
157
157
|
name: param.metadata.name,
|
|
158
|
-
in: param.metadata.in,
|
|
158
|
+
in: param.metadata.in ?? 'path',
|
|
159
159
|
description: param.metadata.description,
|
|
160
160
|
required: param.metadata.required,
|
|
161
161
|
schema: param.metadata.schema,
|
|
@@ -332,4 +332,3 @@ export class SwaggerGenerator {
|
|
|
332
332
|
return path;
|
|
333
333
|
}
|
|
334
334
|
}
|
|
335
|
-
|
package/src/swagger/types.ts
CHANGED
|
@@ -60,7 +60,7 @@ export interface ApiParamMetadata {
|
|
|
60
60
|
enum?: unknown[];
|
|
61
61
|
default?: unknown;
|
|
62
62
|
};
|
|
63
|
-
in
|
|
63
|
+
in?: 'query' | 'path' | 'header' | 'cookie';
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
/**
|
|
@@ -185,4 +185,3 @@ export interface SwaggerDocument {
|
|
|
185
185
|
patch?: SwaggerPathItem;
|
|
186
186
|
}>;
|
|
187
187
|
}
|
|
188
|
-
|
package/src/testing/harness.ts
CHANGED
|
@@ -22,7 +22,7 @@ export class PerformanceHarness {
|
|
|
22
22
|
public static async benchmark(
|
|
23
23
|
name: string,
|
|
24
24
|
iterations: number,
|
|
25
|
-
runner: (iteration: number) =>
|
|
25
|
+
runner: (iteration: number) => unknown | Promise<unknown>,
|
|
26
26
|
): Promise<BenchmarkResult> {
|
|
27
27
|
if (iterations <= 0) {
|
|
28
28
|
throw new Error('iterations must be greater than 0');
|
|
@@ -93,4 +93,3 @@ export class StressTester {
|
|
|
93
93
|
};
|
|
94
94
|
}
|
|
95
95
|
}
|
|
96
|
-
|
|
@@ -9,7 +9,7 @@ interface RequestOptions {
|
|
|
9
9
|
interface TestResponse {
|
|
10
10
|
status: number;
|
|
11
11
|
headers: Headers;
|
|
12
|
-
body:
|
|
12
|
+
body: any;
|
|
13
13
|
text: string;
|
|
14
14
|
ok: boolean;
|
|
15
15
|
}
|
|
@@ -94,7 +94,7 @@ export class TestHttpClient {
|
|
|
94
94
|
const response = await fetch(url, fetchOptions);
|
|
95
95
|
const text = await response.text();
|
|
96
96
|
|
|
97
|
-
let body:
|
|
97
|
+
let body: any;
|
|
98
98
|
try {
|
|
99
99
|
body = JSON.parse(text);
|
|
100
100
|
} catch (_error) {
|
|
@@ -5,7 +5,7 @@ import { Container } from '../di/container';
|
|
|
5
5
|
import { ModuleRegistry } from '../di/module-registry';
|
|
6
6
|
import { ControllerRegistry } from '../controller/controller';
|
|
7
7
|
import { RouteRegistry } from '../router/registry';
|
|
8
|
-
import { MODULE_METADATA_KEY, type ModuleMetadata, type ModuleClass, type ModuleProvider, type ProviderToken } from '../di/module';
|
|
8
|
+
import { MODULE_METADATA_KEY, invokeFactoryProvider, type ModuleMetadata, type ModuleClass, type ModuleProvider, type ProviderToken } from '../di/module';
|
|
9
9
|
import type { Constructor } from '../core/types';
|
|
10
10
|
import { TestHttpClient } from './test-client';
|
|
11
11
|
|
|
@@ -13,7 +13,7 @@ interface ProviderOverride {
|
|
|
13
13
|
token: ProviderToken;
|
|
14
14
|
useValue?: unknown;
|
|
15
15
|
useClass?: Constructor<unknown>;
|
|
16
|
-
useFactory?: () => unknown;
|
|
16
|
+
useFactory?: (...args: any[]) => unknown;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
/**
|
|
@@ -75,7 +75,7 @@ export class TestingModuleBuilder {
|
|
|
75
75
|
} else if (override.useClass) {
|
|
76
76
|
result.push({ provide: override.token, useClass: override.useClass });
|
|
77
77
|
} else if (override.useFactory) {
|
|
78
|
-
result.push({ provide: override.token, useFactory: override.useFactory
|
|
78
|
+
result.push({ provide: override.token, useFactory: override.useFactory });
|
|
79
79
|
}
|
|
80
80
|
overrideMap.delete(key as string | symbol);
|
|
81
81
|
} else {
|
|
@@ -121,7 +121,7 @@ class ProviderOverrideBuilder {
|
|
|
121
121
|
/**
|
|
122
122
|
* 使用工厂函数覆盖
|
|
123
123
|
*/
|
|
124
|
-
public useFactory(factory: () => unknown): TestingModuleBuilder {
|
|
124
|
+
public useFactory(factory: (...args: any[]) => unknown): TestingModuleBuilder {
|
|
125
125
|
this.builder.addOverride({ token: this.token, useFactory: factory });
|
|
126
126
|
return this.builder;
|
|
127
127
|
}
|
|
@@ -177,7 +177,7 @@ export class TestingModule {
|
|
|
177
177
|
container.registerInstance(provider.provide, provider.useValue);
|
|
178
178
|
} else if ('useFactory' in provider && provider.provide) {
|
|
179
179
|
container.register(provider.provide as Constructor<unknown>, {
|
|
180
|
-
factory: () => (provider
|
|
180
|
+
factory: () => invokeFactoryProvider(provider, container),
|
|
181
181
|
});
|
|
182
182
|
} else if ('useClass' in provider) {
|
|
183
183
|
const token = (provider as { provide?: ProviderToken }).provide ?? (provider as { useClass: Constructor<unknown> }).useClass;
|
package/src/validation/errors.ts
CHANGED
|
@@ -9,10 +9,15 @@ export interface ValidationIssue {
|
|
|
9
9
|
*/
|
|
10
10
|
property?: string;
|
|
11
11
|
|
|
12
|
+
/**
|
|
13
|
+
* 属性路径别名,兼容早期错误对象形状。
|
|
14
|
+
*/
|
|
15
|
+
path?: string;
|
|
16
|
+
|
|
12
17
|
/**
|
|
13
18
|
* 失败的规则名称
|
|
14
19
|
*/
|
|
15
|
-
rule
|
|
20
|
+
rule?: string;
|
|
16
21
|
|
|
17
22
|
/**
|
|
18
23
|
* 错误信息
|
|
@@ -73,4 +78,3 @@ export class ValidationError extends Error {
|
|
|
73
78
|
}
|
|
74
79
|
}
|
|
75
80
|
|
|
76
|
-
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
declare module 'bun:test' {
|
|
2
|
+
export const describe: any;
|
|
3
|
+
export const test: any;
|
|
4
|
+
export const it: any;
|
|
5
|
+
export const expect: any;
|
|
6
|
+
export const beforeEach: any;
|
|
7
|
+
export const afterEach: any;
|
|
8
|
+
export const beforeAll: any;
|
|
9
|
+
export const afterAll: any;
|
|
10
|
+
export const mock: any;
|
|
11
|
+
}
|