@dangao/bun-server 0.1.5 → 0.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.
Files changed (45) hide show
  1. package/dist/controller/controller.d.ts.map +1 -1
  2. package/dist/core/context.d.ts +6 -0
  3. package/dist/core/context.d.ts.map +1 -1
  4. package/dist/error/error-codes.d.ts +33 -0
  5. package/dist/error/error-codes.d.ts.map +1 -0
  6. package/dist/error/handler.d.ts.map +1 -1
  7. package/dist/error/http-exception.d.ts +12 -6
  8. package/dist/error/http-exception.d.ts.map +1 -1
  9. package/dist/error/i18n.d.ts +28 -0
  10. package/dist/error/i18n.d.ts.map +1 -0
  11. package/dist/error/index.d.ts +3 -0
  12. package/dist/error/index.d.ts.map +1 -1
  13. package/dist/index.d.ts +3 -2
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +904 -329
  16. package/dist/metrics/collector.d.ts +47 -0
  17. package/dist/metrics/collector.d.ts.map +1 -0
  18. package/dist/metrics/controller.d.ts +17 -0
  19. package/dist/metrics/controller.d.ts.map +1 -0
  20. package/dist/metrics/index.d.ts +7 -0
  21. package/dist/metrics/index.d.ts.map +1 -0
  22. package/dist/metrics/metrics-module.d.ts +9 -0
  23. package/dist/metrics/metrics-module.d.ts.map +1 -0
  24. package/dist/metrics/middleware.d.ts +7 -0
  25. package/dist/metrics/middleware.d.ts.map +1 -0
  26. package/dist/metrics/prometheus.d.ts +23 -0
  27. package/dist/metrics/prometheus.d.ts.map +1 -0
  28. package/dist/metrics/types.d.ts +88 -0
  29. package/dist/metrics/types.d.ts.map +1 -0
  30. package/dist/middleware/builtin/error-handler.d.ts.map +1 -1
  31. package/dist/middleware/builtin/index.d.ts +1 -0
  32. package/dist/middleware/builtin/index.d.ts.map +1 -1
  33. package/dist/middleware/builtin/rate-limit.d.ts +104 -0
  34. package/dist/middleware/builtin/rate-limit.d.ts.map +1 -0
  35. package/dist/middleware/decorators.d.ts +7 -0
  36. package/dist/middleware/decorators.d.ts.map +1 -1
  37. package/dist/middleware/index.d.ts +1 -1
  38. package/dist/middleware/index.d.ts.map +1 -1
  39. package/dist/security/context.d.ts.map +1 -1
  40. package/dist/security/filter.d.ts.map +1 -1
  41. package/dist/security/providers/oauth2-provider.d.ts +3 -1
  42. package/dist/security/providers/oauth2-provider.d.ts.map +1 -1
  43. package/dist/security/security-module.d.ts.map +1 -1
  44. package/package.json +3 -3
  45. package/readme.md +1 -1
package/dist/index.js CHANGED
@@ -1,4 +1,14 @@
1
1
  // @bun
2
+ var __defProp = Object.defineProperty;
3
+ var __export = (target, all) => {
4
+ for (var name in all)
5
+ __defProp(target, name, {
6
+ get: all[name],
7
+ enumerable: true,
8
+ configurable: true,
9
+ set: (newValue) => all[name] = () => newValue
10
+ });
11
+ };
2
12
  var __legacyDecorateClassTS = function(decorators, target, key, desc) {
3
13
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
14
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
@@ -14,6 +24,371 @@ var __legacyMetadataTS = (k, v) => {
14
24
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function")
15
25
  return Reflect.metadata(k, v);
16
26
  };
27
+ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
28
+
29
+ // src/validation/decorators.ts
30
+ import"reflect-metadata";
31
+ function getOrCreateMetadata(target, propertyKey) {
32
+ const existing = Reflect.getMetadata(VALIDATION_METADATA_KEY, target, propertyKey);
33
+ if (existing) {
34
+ return existing;
35
+ }
36
+ const metadata = [];
37
+ Reflect.defineMetadata(VALIDATION_METADATA_KEY, metadata, target, propertyKey);
38
+ return metadata;
39
+ }
40
+ function Validate(...rules) {
41
+ return (target, propertyKey, parameterIndex) => {
42
+ if (!propertyKey) {
43
+ throw new Error("@Validate decorator can only be used on methods");
44
+ }
45
+ if (!rules.length) {
46
+ throw new Error("@Validate requires at least one validation rule");
47
+ }
48
+ const metadata = getOrCreateMetadata(target, propertyKey);
49
+ let entry = metadata.find((item) => item.index === parameterIndex);
50
+ if (!entry) {
51
+ entry = { index: parameterIndex, rules: [] };
52
+ metadata.push(entry);
53
+ }
54
+ entry.rules.push(...rules);
55
+ };
56
+ }
57
+ function IsString(options = {}) {
58
+ return {
59
+ name: "isString",
60
+ message: options.message ?? "\u5FC5\u987B\u662F\u5B57\u7B26\u4E32",
61
+ validate: (value) => typeof value === "string"
62
+ };
63
+ }
64
+ function IsNumber(options = {}) {
65
+ return {
66
+ name: "isNumber",
67
+ message: options.message ?? "\u5FC5\u987B\u662F\u6570\u5B57",
68
+ validate: (value) => typeof value === "number" && !Number.isNaN(value)
69
+ };
70
+ }
71
+ function IsEmail(options = {}) {
72
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
73
+ return {
74
+ name: "isEmail",
75
+ message: options.message ?? "\u5FC5\u987B\u662F\u5408\u6CD5\u7684\u90AE\u7BB1\u5730\u5740",
76
+ validate: (value) => typeof value === "string" && emailRegex.test(value)
77
+ };
78
+ }
79
+ function IsOptional() {
80
+ return {
81
+ name: "isOptional",
82
+ message: "",
83
+ optional: true,
84
+ validate: () => true
85
+ };
86
+ }
87
+ function MinLength(min, options = {}) {
88
+ return {
89
+ name: "minLength",
90
+ message: options.message ?? `\u957F\u5EA6\u4E0D\u80FD\u5C0F\u4E8E ${min}`,
91
+ validate: (value) => typeof value === "string" && value.length >= min
92
+ };
93
+ }
94
+ function getValidationMetadata(target, propertyKey) {
95
+ return Reflect.getMetadata(VALIDATION_METADATA_KEY, target, propertyKey) || [];
96
+ }
97
+ var VALIDATION_METADATA_KEY;
98
+ var init_decorators = __esm(() => {
99
+ VALIDATION_METADATA_KEY = Symbol("validation:param");
100
+ });
101
+
102
+ // src/validation/errors.ts
103
+ var ValidationError;
104
+ var init_errors = __esm(() => {
105
+ ValidationError = class ValidationError extends Error {
106
+ issues;
107
+ constructor(message, issues) {
108
+ super(message);
109
+ this.name = "ValidationError";
110
+ this.issues = issues;
111
+ }
112
+ };
113
+ });
114
+
115
+ // src/validation/validator.ts
116
+ function shouldSkip(rule, value) {
117
+ return rule.optional === true && (value === undefined || value === null);
118
+ }
119
+ function validateRule(rule, value, index) {
120
+ if (shouldSkip(rule, value)) {
121
+ return null;
122
+ }
123
+ const passed = rule.validate(value);
124
+ if (passed) {
125
+ return null;
126
+ }
127
+ return {
128
+ index,
129
+ rule: rule.name,
130
+ message: rule.message
131
+ };
132
+ }
133
+ function validateParameters(params, metadata) {
134
+ if (!metadata.length) {
135
+ return;
136
+ }
137
+ const issues = [];
138
+ for (const item of metadata) {
139
+ const value = params[item.index];
140
+ let skipped = false;
141
+ for (const rule of item.rules) {
142
+ if (rule.optional && (value === undefined || value === null)) {
143
+ skipped = true;
144
+ break;
145
+ }
146
+ const issue = validateRule(rule, value, item.index);
147
+ if (issue) {
148
+ issues.push(issue);
149
+ }
150
+ }
151
+ if (skipped) {
152
+ continue;
153
+ }
154
+ }
155
+ if (issues.length > 0) {
156
+ throw new ValidationError("Validation failed", issues);
157
+ }
158
+ }
159
+ var init_validator = __esm(() => {
160
+ init_errors();
161
+ });
162
+
163
+ // src/validation/index.ts
164
+ var init_validation = __esm(() => {
165
+ init_decorators();
166
+ init_validator();
167
+ init_errors();
168
+ });
169
+
170
+ // src/error/error-codes.ts
171
+ var ERROR_CODE_TO_STATUS, ERROR_CODE_MESSAGES;
172
+ var init_error_codes = __esm(() => {
173
+ ERROR_CODE_TO_STATUS = {
174
+ ["INTERNAL_ERROR" /* INTERNAL_ERROR */]: 500,
175
+ ["INVALID_REQUEST" /* INVALID_REQUEST */]: 400,
176
+ ["RESOURCE_NOT_FOUND" /* RESOURCE_NOT_FOUND */]: 404,
177
+ ["METHOD_NOT_ALLOWED" /* METHOD_NOT_ALLOWED */]: 405,
178
+ ["AUTH_REQUIRED" /* AUTH_REQUIRED */]: 401,
179
+ ["AUTH_INVALID_TOKEN" /* AUTH_INVALID_TOKEN */]: 401,
180
+ ["AUTH_TOKEN_EXPIRED" /* AUTH_TOKEN_EXPIRED */]: 401,
181
+ ["AUTH_INSUFFICIENT_PERMISSIONS" /* AUTH_INSUFFICIENT_PERMISSIONS */]: 403,
182
+ ["VALIDATION_FAILED" /* VALIDATION_FAILED */]: 400,
183
+ ["VALIDATION_REQUIRED_FIELD" /* VALIDATION_REQUIRED_FIELD */]: 400,
184
+ ["VALIDATION_INVALID_FORMAT" /* VALIDATION_INVALID_FORMAT */]: 400,
185
+ ["VALIDATION_OUT_OF_RANGE" /* VALIDATION_OUT_OF_RANGE */]: 400,
186
+ ["OAUTH2_INVALID_CLIENT" /* OAUTH2_INVALID_CLIENT */]: 400,
187
+ ["OAUTH2_INVALID_GRANT" /* OAUTH2_INVALID_GRANT */]: 400,
188
+ ["OAUTH2_INVALID_SCOPE" /* OAUTH2_INVALID_SCOPE */]: 400,
189
+ ["OAUTH2_INVALID_REDIRECT_URI" /* OAUTH2_INVALID_REDIRECT_URI */]: 400,
190
+ ["OAUTH2_UNSUPPORTED_GRANT_TYPE" /* OAUTH2_UNSUPPORTED_GRANT_TYPE */]: 400
191
+ };
192
+ ERROR_CODE_MESSAGES = {
193
+ ["INTERNAL_ERROR" /* INTERNAL_ERROR */]: "Internal Server Error",
194
+ ["INVALID_REQUEST" /* INVALID_REQUEST */]: "Invalid Request",
195
+ ["RESOURCE_NOT_FOUND" /* RESOURCE_NOT_FOUND */]: "Resource Not Found",
196
+ ["METHOD_NOT_ALLOWED" /* METHOD_NOT_ALLOWED */]: "Method Not Allowed",
197
+ ["AUTH_REQUIRED" /* AUTH_REQUIRED */]: "Authentication Required",
198
+ ["AUTH_INVALID_TOKEN" /* AUTH_INVALID_TOKEN */]: "Invalid Authentication Token",
199
+ ["AUTH_TOKEN_EXPIRED" /* AUTH_TOKEN_EXPIRED */]: "Authentication Token Expired",
200
+ ["AUTH_INSUFFICIENT_PERMISSIONS" /* AUTH_INSUFFICIENT_PERMISSIONS */]: "Insufficient Permissions",
201
+ ["VALIDATION_FAILED" /* VALIDATION_FAILED */]: "Validation Failed",
202
+ ["VALIDATION_REQUIRED_FIELD" /* VALIDATION_REQUIRED_FIELD */]: "Required Field Missing",
203
+ ["VALIDATION_INVALID_FORMAT" /* VALIDATION_INVALID_FORMAT */]: "Invalid Format",
204
+ ["VALIDATION_OUT_OF_RANGE" /* VALIDATION_OUT_OF_RANGE */]: "Value Out of Range",
205
+ ["OAUTH2_INVALID_CLIENT" /* OAUTH2_INVALID_CLIENT */]: "Invalid Client",
206
+ ["OAUTH2_INVALID_GRANT" /* OAUTH2_INVALID_GRANT */]: "Invalid Grant",
207
+ ["OAUTH2_INVALID_SCOPE" /* OAUTH2_INVALID_SCOPE */]: "Invalid Scope",
208
+ ["OAUTH2_INVALID_REDIRECT_URI" /* OAUTH2_INVALID_REDIRECT_URI */]: "Invalid Redirect URI",
209
+ ["OAUTH2_UNSUPPORTED_GRANT_TYPE" /* OAUTH2_UNSUPPORTED_GRANT_TYPE */]: "Unsupported Grant Type"
210
+ };
211
+ });
212
+
213
+ // src/error/http-exception.ts
214
+ var HttpException, BadRequestException, UnauthorizedException, ForbiddenException, NotFoundException, InternalServerErrorException;
215
+ var init_http_exception = __esm(() => {
216
+ init_error_codes();
217
+ HttpException = class HttpException extends Error {
218
+ status;
219
+ code;
220
+ details;
221
+ constructor(status, message, details, code) {
222
+ super(message);
223
+ this.name = this.constructor.name;
224
+ this.status = status;
225
+ this.code = code;
226
+ this.details = details;
227
+ }
228
+ static withCode(code, message, details) {
229
+ const status = ERROR_CODE_TO_STATUS[code] || 500;
230
+ const finalMessage = message || ERROR_CODE_MESSAGES[code] || "Internal Server Error";
231
+ return new HttpException(status, finalMessage, details, code);
232
+ }
233
+ };
234
+ BadRequestException = class BadRequestException extends HttpException {
235
+ constructor(message = "Bad Request", details, code) {
236
+ super(400, message, details, code);
237
+ }
238
+ };
239
+ UnauthorizedException = class UnauthorizedException extends HttpException {
240
+ constructor(message = "Unauthorized", details, code) {
241
+ super(401, message, details, code);
242
+ }
243
+ };
244
+ ForbiddenException = class ForbiddenException extends HttpException {
245
+ constructor(message = "Forbidden", details, code) {
246
+ super(403, message, details, code);
247
+ }
248
+ };
249
+ NotFoundException = class NotFoundException extends HttpException {
250
+ constructor(message = "Not Found", details, code) {
251
+ super(404, message, details, code);
252
+ }
253
+ };
254
+ InternalServerErrorException = class InternalServerErrorException extends HttpException {
255
+ constructor(message = "Internal Server Error", details, code) {
256
+ super(500, message, details, code);
257
+ }
258
+ };
259
+ });
260
+
261
+ // src/error/filter.ts
262
+ class ExceptionFilterRegistry {
263
+ static instance;
264
+ filters = [];
265
+ constructor() {}
266
+ static getInstance() {
267
+ if (!ExceptionFilterRegistry.instance) {
268
+ ExceptionFilterRegistry.instance = new ExceptionFilterRegistry;
269
+ }
270
+ return ExceptionFilterRegistry.instance;
271
+ }
272
+ register(filter) {
273
+ this.filters.push(filter);
274
+ }
275
+ clear() {
276
+ this.filters.length = 0;
277
+ }
278
+ async execute(error, context) {
279
+ for (const filter of this.filters) {
280
+ const result = await filter.catch(error, context);
281
+ if (result) {
282
+ return result;
283
+ }
284
+ }
285
+ return;
286
+ }
287
+ }
288
+
289
+ // src/error/i18n.ts
290
+ class ErrorMessageI18n {
291
+ static currentLanguage = "en";
292
+ static setLanguage(language) {
293
+ this.currentLanguage = language;
294
+ }
295
+ static getLanguage() {
296
+ return this.currentLanguage;
297
+ }
298
+ static getMessage(code, language) {
299
+ const lang = language || this.currentLanguage;
300
+ const messages = ERROR_MESSAGES_I18N[lang];
301
+ return messages?.[code] || ERROR_CODE_MESSAGES[code] || "Internal Server Error";
302
+ }
303
+ static parseLanguageFromHeader(acceptLanguage) {
304
+ if (!acceptLanguage) {
305
+ return "en";
306
+ }
307
+ if (acceptLanguage.includes("zh-CN") || acceptLanguage.includes("zh")) {
308
+ return "zh-CN";
309
+ }
310
+ return "en";
311
+ }
312
+ }
313
+ var ERROR_MESSAGES_I18N;
314
+ var init_i18n = __esm(() => {
315
+ init_error_codes();
316
+ ERROR_MESSAGES_I18N = {
317
+ en: {
318
+ ...ERROR_CODE_MESSAGES
319
+ },
320
+ "zh-CN": {
321
+ ["INTERNAL_ERROR" /* INTERNAL_ERROR */]: "\u670D\u52A1\u5668\u5185\u90E8\u9519\u8BEF",
322
+ ["INVALID_REQUEST" /* INVALID_REQUEST */]: "\u65E0\u6548\u7684\u8BF7\u6C42",
323
+ ["RESOURCE_NOT_FOUND" /* RESOURCE_NOT_FOUND */]: "\u8D44\u6E90\u672A\u627E\u5230",
324
+ ["METHOD_NOT_ALLOWED" /* METHOD_NOT_ALLOWED */]: "\u65B9\u6CD5\u4E0D\u5141\u8BB8",
325
+ ["AUTH_REQUIRED" /* AUTH_REQUIRED */]: "\u9700\u8981\u8BA4\u8BC1",
326
+ ["AUTH_INVALID_TOKEN" /* AUTH_INVALID_TOKEN */]: "\u65E0\u6548\u7684\u8BA4\u8BC1\u4EE4\u724C",
327
+ ["AUTH_TOKEN_EXPIRED" /* AUTH_TOKEN_EXPIRED */]: "\u8BA4\u8BC1\u4EE4\u724C\u5DF2\u8FC7\u671F",
328
+ ["AUTH_INSUFFICIENT_PERMISSIONS" /* AUTH_INSUFFICIENT_PERMISSIONS */]: "\u6743\u9650\u4E0D\u8DB3",
329
+ ["VALIDATION_FAILED" /* VALIDATION_FAILED */]: "\u9A8C\u8BC1\u5931\u8D25",
330
+ ["VALIDATION_REQUIRED_FIELD" /* VALIDATION_REQUIRED_FIELD */]: "\u5FC5\u586B\u5B57\u6BB5\u7F3A\u5931",
331
+ ["VALIDATION_INVALID_FORMAT" /* VALIDATION_INVALID_FORMAT */]: "\u683C\u5F0F\u65E0\u6548",
332
+ ["VALIDATION_OUT_OF_RANGE" /* VALIDATION_OUT_OF_RANGE */]: "\u503C\u8D85\u51FA\u8303\u56F4",
333
+ ["OAUTH2_INVALID_CLIENT" /* OAUTH2_INVALID_CLIENT */]: "\u65E0\u6548\u7684\u5BA2\u6237\u7AEF",
334
+ ["OAUTH2_INVALID_GRANT" /* OAUTH2_INVALID_GRANT */]: "\u65E0\u6548\u7684\u6388\u6743",
335
+ ["OAUTH2_INVALID_SCOPE" /* OAUTH2_INVALID_SCOPE */]: "\u65E0\u6548\u7684\u4F5C\u7528\u57DF",
336
+ ["OAUTH2_INVALID_REDIRECT_URI" /* OAUTH2_INVALID_REDIRECT_URI */]: "\u65E0\u6548\u7684\u91CD\u5B9A\u5411 URI",
337
+ ["OAUTH2_UNSUPPORTED_GRANT_TYPE" /* OAUTH2_UNSUPPORTED_GRANT_TYPE */]: "\u4E0D\u652F\u6301\u7684\u6388\u6743\u7C7B\u578B"
338
+ }
339
+ };
340
+ });
341
+
342
+ // src/error/handler.ts
343
+ var exports_handler = {};
344
+ __export(exports_handler, {
345
+ handleError: () => handleError
346
+ });
347
+ async function handleError(error, context) {
348
+ const registry = ExceptionFilterRegistry.getInstance();
349
+ const filterResponse = await registry.execute(error, context);
350
+ if (filterResponse) {
351
+ return filterResponse;
352
+ }
353
+ if (error instanceof HttpException) {
354
+ context.setStatus(error.status);
355
+ let errorMessage = error.message;
356
+ if (error.code) {
357
+ const acceptLanguage = context.getHeader("accept-language");
358
+ const language = ErrorMessageI18n.parseLanguageFromHeader(acceptLanguage);
359
+ errorMessage = ErrorMessageI18n.getMessage(error.code, language);
360
+ }
361
+ const responseBody = {
362
+ error: errorMessage
363
+ };
364
+ if (error.code) {
365
+ responseBody.code = error.code;
366
+ }
367
+ if (error.details !== undefined) {
368
+ responseBody.details = error.details;
369
+ }
370
+ return context.createResponse(responseBody);
371
+ }
372
+ if (error instanceof ValidationError) {
373
+ context.setStatus(400);
374
+ return context.createResponse({
375
+ error: error.message,
376
+ code: "VALIDATION_FAILED",
377
+ issues: error.issues
378
+ });
379
+ }
380
+ const message = error instanceof Error ? error.message : String(error);
381
+ context.setStatus(500);
382
+ return context.createResponse({
383
+ error: "Internal Server Error",
384
+ details: message
385
+ });
386
+ }
387
+ var init_handler = __esm(() => {
388
+ init_http_exception();
389
+ init_validation();
390
+ init_i18n();
391
+ });
17
392
 
18
393
  // src/request/body-parser.ts
19
394
  class BodyParser {
@@ -150,6 +525,17 @@ class Context {
150
525
  getHeader(key) {
151
526
  return this.headers.get(key);
152
527
  }
528
+ getClientIp() {
529
+ const forwardedFor = this.getHeader("X-Forwarded-For");
530
+ if (forwardedFor) {
531
+ return forwardedFor.split(",")[0].trim();
532
+ }
533
+ const realIp = this.getHeader("X-Real-IP");
534
+ if (realIp) {
535
+ return realIp.trim();
536
+ }
537
+ return "unknown";
538
+ }
153
539
  setHeader(key, value) {
154
540
  this.responseHeaders.set(key, value);
155
541
  }
@@ -941,310 +1327,211 @@ function PATCH(path) {
941
1327
  return createRouteDecorator("PATCH", path);
942
1328
  }
943
1329
 
944
- // src/controller/metadata.ts
945
- function getControllerMetadata(target) {
946
- return Reflect.getMetadata(CONTROLLER_METADATA_KEY, target);
947
- }
948
- function getRouteMetadata(target) {
949
- return Reflect.getMetadata(ROUTE_METADATA_KEY, target) || [];
950
- }
951
- // src/middleware/decorators.ts
952
- import"reflect-metadata";
953
- var CLASS_MIDDLEWARE_METADATA_KEY = Symbol("middleware:class");
954
- var METHOD_MIDDLEWARE_METADATA_KEY = Symbol("middleware:method");
955
- function appendMiddlewareMetadata(target, propertyKey, middlewares) {
956
- if (!middlewares.length) {
957
- return;
958
- }
959
- if (propertyKey === undefined) {
960
- const existing2 = Reflect.getMetadata(CLASS_MIDDLEWARE_METADATA_KEY, target) || [];
961
- Reflect.defineMetadata(CLASS_MIDDLEWARE_METADATA_KEY, existing2.concat(middlewares), target);
962
- return;
963
- }
964
- const existing = Reflect.getMetadata(METHOD_MIDDLEWARE_METADATA_KEY, target, propertyKey) || [];
965
- Reflect.defineMetadata(METHOD_MIDDLEWARE_METADATA_KEY, existing.concat(middlewares), target, propertyKey);
966
- }
967
- function UseMiddleware(...middlewares) {
968
- return function(target, propertyKey) {
969
- appendMiddlewareMetadata(propertyKey === undefined ? target : target, propertyKey, middlewares);
970
- };
971
- }
972
- function getClassMiddlewares(constructor) {
973
- return Reflect.getMetadata(CLASS_MIDDLEWARE_METADATA_KEY, constructor) || [];
974
- }
975
- function getMethodMiddlewares(target, propertyKey) {
976
- return Reflect.getMetadata(METHOD_MIDDLEWARE_METADATA_KEY, target, propertyKey) || [];
977
- }
978
- // src/middleware/builtin/logger.ts
979
- import { LoggerManager as LoggerManager4 } from "@dangao/logsmith";
980
- function createLoggerMiddleware(options = {}) {
981
- const log = options.logger ?? ((message, details) => {
982
- const logger = LoggerManager4.getLogger();
983
- if (details) {
984
- logger.info(message, details);
985
- } else {
986
- logger.info(message);
987
- }
988
- });
989
- const prefix = options.prefix ?? "[Logger]";
990
- return async (context, next) => {
991
- let response;
992
- try {
993
- response = await next();
994
- return response;
995
- } finally {
996
- const status = response?.status ?? context.statusCode ?? 200;
997
- log(`${prefix} ${context.method} ${context.path} ${status}`);
998
- }
999
- };
1000
- }
1001
- function createRequestLoggingMiddleware(options = {}) {
1002
- const log = options.logger ?? ((message, details) => {
1003
- const logger = LoggerManager4.getLogger();
1004
- logger.info(message, details);
1005
- });
1006
- const prefix = options.prefix ?? "[Request]";
1007
- const setHeader = options.setHeader ?? true;
1008
- return async (context, next) => {
1009
- const start = performance.now();
1010
- try {
1011
- const response = await next();
1012
- const duration = performance.now() - start;
1013
- log(`${prefix} ${context.method} ${context.path} ${response.status} ${duration.toFixed(2)}ms`);
1014
- if (setHeader) {
1015
- context.setHeader("x-request-duration", duration.toFixed(2));
1016
- }
1017
- return response;
1018
- } catch (error) {
1019
- const duration = performance.now() - start;
1020
- log(`${prefix} ${context.method} ${context.path} error ${duration.toFixed(2)}ms`, error instanceof Error ? { error: error.message } : undefined);
1021
- throw error;
1022
- }
1023
- };
1024
- }
1025
- // src/validation/decorators.ts
1026
- import"reflect-metadata";
1027
- var VALIDATION_METADATA_KEY = Symbol("validation:param");
1028
- function getOrCreateMetadata(target, propertyKey) {
1029
- const existing = Reflect.getMetadata(VALIDATION_METADATA_KEY, target, propertyKey);
1030
- if (existing) {
1031
- return existing;
1032
- }
1033
- const metadata = [];
1034
- Reflect.defineMetadata(VALIDATION_METADATA_KEY, metadata, target, propertyKey);
1035
- return metadata;
1036
- }
1037
- function Validate(...rules) {
1038
- return (target, propertyKey, parameterIndex) => {
1039
- if (!propertyKey) {
1040
- throw new Error("@Validate decorator can only be used on methods");
1041
- }
1042
- if (!rules.length) {
1043
- throw new Error("@Validate requires at least one validation rule");
1044
- }
1045
- const metadata = getOrCreateMetadata(target, propertyKey);
1046
- let entry = metadata.find((item) => item.index === parameterIndex);
1047
- if (!entry) {
1048
- entry = { index: parameterIndex, rules: [] };
1049
- metadata.push(entry);
1050
- }
1051
- entry.rules.push(...rules);
1052
- };
1053
- }
1054
- function IsString(options = {}) {
1055
- return {
1056
- name: "isString",
1057
- message: options.message ?? "\u5FC5\u987B\u662F\u5B57\u7B26\u4E32",
1058
- validate: (value) => typeof value === "string"
1059
- };
1060
- }
1061
- function IsNumber(options = {}) {
1062
- return {
1063
- name: "isNumber",
1064
- message: options.message ?? "\u5FC5\u987B\u662F\u6570\u5B57",
1065
- validate: (value) => typeof value === "number" && !Number.isNaN(value)
1066
- };
1067
- }
1068
- function IsEmail(options = {}) {
1069
- const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
1070
- return {
1071
- name: "isEmail",
1072
- message: options.message ?? "\u5FC5\u987B\u662F\u5408\u6CD5\u7684\u90AE\u7BB1\u5730\u5740",
1073
- validate: (value) => typeof value === "string" && emailRegex.test(value)
1074
- };
1075
- }
1076
- function IsOptional() {
1077
- return {
1078
- name: "isOptional",
1079
- message: "",
1080
- optional: true,
1081
- validate: () => true
1082
- };
1083
- }
1084
- function MinLength(min, options = {}) {
1085
- return {
1086
- name: "minLength",
1087
- message: options.message ?? `\u957F\u5EA6\u4E0D\u80FD\u5C0F\u4E8E ${min}`,
1088
- validate: (value) => typeof value === "string" && value.length >= min
1089
- };
1090
- }
1091
- function getValidationMetadata(target, propertyKey) {
1092
- return Reflect.getMetadata(VALIDATION_METADATA_KEY, target, propertyKey) || [];
1093
- }
1094
- // src/validation/errors.ts
1095
- class ValidationError extends Error {
1096
- issues;
1097
- constructor(message, issues) {
1098
- super(message);
1099
- this.name = "ValidationError";
1100
- this.issues = issues;
1101
- }
1102
- }
1103
-
1104
- // src/validation/validator.ts
1105
- function shouldSkip(rule, value) {
1106
- return rule.optional === true && (value === undefined || value === null);
1107
- }
1108
- function validateRule(rule, value, index) {
1109
- if (shouldSkip(rule, value)) {
1110
- return null;
1111
- }
1112
- const passed = rule.validate(value);
1113
- if (passed) {
1114
- return null;
1115
- }
1116
- return {
1117
- index,
1118
- rule: rule.name,
1119
- message: rule.message
1120
- };
1121
- }
1122
- function validateParameters(params, metadata) {
1123
- if (!metadata.length) {
1124
- return;
1125
- }
1126
- const issues = [];
1127
- for (const item of metadata) {
1128
- const value = params[item.index];
1129
- let skipped = false;
1130
- for (const rule of item.rules) {
1131
- if (rule.optional && (value === undefined || value === null)) {
1132
- skipped = true;
1133
- break;
1134
- }
1135
- const issue = validateRule(rule, value, item.index);
1136
- if (issue) {
1137
- issues.push(issue);
1138
- }
1139
- }
1140
- if (skipped) {
1141
- continue;
1142
- }
1143
- }
1144
- if (issues.length > 0) {
1145
- throw new ValidationError("Validation failed", issues);
1146
- }
1147
- }
1148
- // src/middleware/builtin/error-handler.ts
1149
- import { LoggerManager as LoggerManager5 } from "@dangao/logsmith";
1150
-
1151
- // src/error/http-exception.ts
1152
- class HttpException extends Error {
1153
- status;
1154
- details;
1155
- constructor(status, message, details) {
1156
- super(message);
1157
- this.name = this.constructor.name;
1158
- this.status = status;
1159
- this.details = details;
1160
- }
1161
- }
1162
-
1163
- class BadRequestException extends HttpException {
1164
- constructor(message = "Bad Request", details) {
1165
- super(400, message, details);
1166
- }
1167
- }
1168
-
1169
- class UnauthorizedException extends HttpException {
1170
- constructor(message = "Unauthorized", details) {
1171
- super(401, message, details);
1172
- }
1173
- }
1174
-
1175
- class ForbiddenException extends HttpException {
1176
- constructor(message = "Forbidden", details) {
1177
- super(403, message, details);
1178
- }
1179
- }
1180
-
1181
- class NotFoundException extends HttpException {
1182
- constructor(message = "Not Found", details) {
1183
- super(404, message, details);
1184
- }
1330
+ // src/controller/metadata.ts
1331
+ function getControllerMetadata(target) {
1332
+ return Reflect.getMetadata(CONTROLLER_METADATA_KEY, target);
1185
1333
  }
1186
-
1187
- class InternalServerErrorException extends HttpException {
1188
- constructor(message = "Internal Server Error", details) {
1189
- super(500, message, details);
1190
- }
1334
+ function getRouteMetadata(target) {
1335
+ return Reflect.getMetadata(ROUTE_METADATA_KEY, target) || [];
1191
1336
  }
1192
- // src/error/filter.ts
1193
- class ExceptionFilterRegistry {
1194
- static instance;
1195
- filters = [];
1196
- constructor() {}
1197
- static getInstance() {
1198
- if (!ExceptionFilterRegistry.instance) {
1199
- ExceptionFilterRegistry.instance = new ExceptionFilterRegistry;
1337
+ // src/middleware/decorators.ts
1338
+ import"reflect-metadata";
1339
+
1340
+ // src/middleware/builtin/rate-limit.ts
1341
+ class MemoryRateLimitStore {
1342
+ store = new Map;
1343
+ async get(key) {
1344
+ const entry = this.store.get(key);
1345
+ if (!entry) {
1346
+ return 0;
1200
1347
  }
1201
- return ExceptionFilterRegistry.instance;
1348
+ if (Date.now() > entry.resetTime) {
1349
+ this.store.delete(key);
1350
+ return 0;
1351
+ }
1352
+ return entry.count;
1202
1353
  }
1203
- register(filter) {
1204
- this.filters.push(filter);
1354
+ async increment(key, windowMs) {
1355
+ const now = Date.now();
1356
+ const entry = this.store.get(key);
1357
+ if (!entry || now > entry.resetTime) {
1358
+ const resetTime = now + windowMs;
1359
+ this.store.set(key, { count: 1, resetTime });
1360
+ return 1;
1361
+ }
1362
+ entry.count++;
1363
+ return entry.count;
1205
1364
  }
1206
- clear() {
1207
- this.filters.length = 0;
1365
+ async reset(key) {
1366
+ this.store.delete(key);
1208
1367
  }
1209
- async execute(error, context) {
1210
- for (const filter of this.filters) {
1211
- const result = await filter.catch(error, context);
1212
- if (result) {
1213
- return result;
1368
+ cleanup() {
1369
+ const now = Date.now();
1370
+ for (const [key, entry] of this.store.entries()) {
1371
+ if (now > entry.resetTime) {
1372
+ this.store.delete(key);
1214
1373
  }
1215
1374
  }
1216
- return;
1217
1375
  }
1218
1376
  }
1219
- // src/error/handler.ts
1220
- async function handleError(error, context) {
1221
- const registry = ExceptionFilterRegistry.getInstance();
1222
- const filterResponse = await registry.execute(error, context);
1223
- if (filterResponse) {
1224
- return filterResponse;
1225
- }
1226
- if (error instanceof HttpException) {
1227
- context.setStatus(error.status);
1228
- return context.createResponse({
1229
- error: error.message,
1230
- details: error.details
1231
- });
1377
+ function defaultKeyGenerator(context) {
1378
+ return `rate-limit:${context.getClientIp()}`;
1379
+ }
1380
+ function createRateLimitMiddleware(options) {
1381
+ const {
1382
+ max,
1383
+ windowMs = 60000,
1384
+ store = new MemoryRateLimitStore,
1385
+ keyGenerator = defaultKeyGenerator,
1386
+ skipSuccessfulRequests = false,
1387
+ skipFailedRequests = false,
1388
+ message = "Too Many Requests",
1389
+ statusCode = 429,
1390
+ standardHeaders = true,
1391
+ legacyHeaders = true
1392
+ } = options;
1393
+ return async (context, next) => {
1394
+ const key = await keyGenerator(context);
1395
+ const currentCount = await store.increment(key, windowMs);
1396
+ const remaining = Math.max(0, max - currentCount);
1397
+ const resetTime = Date.now() + windowMs;
1398
+ if (standardHeaders) {
1399
+ context.setHeader("RateLimit-Limit", max.toString());
1400
+ context.setHeader("RateLimit-Remaining", remaining.toString());
1401
+ context.setHeader("RateLimit-Reset", Math.ceil(resetTime / 1000).toString());
1402
+ }
1403
+ if (legacyHeaders) {
1404
+ context.setHeader("X-RateLimit-Limit", max.toString());
1405
+ context.setHeader("X-RateLimit-Remaining", remaining.toString());
1406
+ context.setHeader("X-RateLimit-Reset", Math.ceil(resetTime / 1000).toString());
1407
+ }
1408
+ if (currentCount > max) {
1409
+ context.setStatus(statusCode);
1410
+ return context.createResponse({
1411
+ error: message,
1412
+ retryAfter: Math.ceil(windowMs / 1000)
1413
+ });
1414
+ }
1415
+ const response = await next();
1416
+ const shouldSkip = skipSuccessfulRequests && response.status >= 200 && response.status < 300 || skipFailedRequests && response.status >= 400;
1417
+ if (shouldSkip) {
1418
+ const current = await store.get(key);
1419
+ if (current > 0) {}
1420
+ }
1421
+ return response;
1422
+ };
1423
+ }
1424
+ function createTokenKeyGenerator(tokenHeader = "Authorization") {
1425
+ return (context) => {
1426
+ const token = context.getHeader(tokenHeader);
1427
+ if (token) {
1428
+ const tokenValue = token.startsWith("Bearer ") ? token.substring(7) : token;
1429
+ return `rate-limit:token:${tokenValue}`;
1430
+ }
1431
+ return defaultKeyGenerator(context);
1432
+ };
1433
+ }
1434
+ function createUserKeyGenerator(getUserId) {
1435
+ return (context) => {
1436
+ const userId = getUserId(context);
1437
+ if (userId) {
1438
+ return `rate-limit:user:${userId}`;
1439
+ }
1440
+ return defaultKeyGenerator(context);
1441
+ };
1442
+ }
1443
+
1444
+ // src/middleware/decorators.ts
1445
+ var CLASS_MIDDLEWARE_METADATA_KEY = Symbol("middleware:class");
1446
+ var METHOD_MIDDLEWARE_METADATA_KEY = Symbol("middleware:method");
1447
+ function appendMiddlewareMetadata(target, propertyKey, middlewares) {
1448
+ if (!middlewares.length) {
1449
+ return;
1232
1450
  }
1233
- if (error instanceof ValidationError) {
1234
- context.setStatus(400);
1235
- return context.createResponse({
1236
- error: error.message,
1237
- issues: error.issues
1238
- });
1451
+ if (propertyKey === undefined) {
1452
+ const existing2 = Reflect.getMetadata(CLASS_MIDDLEWARE_METADATA_KEY, target) || [];
1453
+ Reflect.defineMetadata(CLASS_MIDDLEWARE_METADATA_KEY, existing2.concat(middlewares), target);
1454
+ return;
1239
1455
  }
1240
- const message = error instanceof Error ? error.message : String(error);
1241
- context.setStatus(500);
1242
- return context.createResponse({
1243
- error: "Internal Server Error",
1244
- details: message
1456
+ const existing = Reflect.getMetadata(METHOD_MIDDLEWARE_METADATA_KEY, target, propertyKey) || [];
1457
+ Reflect.defineMetadata(METHOD_MIDDLEWARE_METADATA_KEY, existing.concat(middlewares), target, propertyKey);
1458
+ }
1459
+ function UseMiddleware(...middlewares) {
1460
+ return function(target, propertyKey) {
1461
+ appendMiddlewareMetadata(propertyKey === undefined ? target : target, propertyKey, middlewares);
1462
+ };
1463
+ }
1464
+ function RateLimit(options) {
1465
+ return function(target, propertyKey) {
1466
+ const middleware = createRateLimitMiddleware(options);
1467
+ appendMiddlewareMetadata(target, propertyKey, [middleware]);
1468
+ };
1469
+ }
1470
+ function getClassMiddlewares(constructor) {
1471
+ return Reflect.getMetadata(CLASS_MIDDLEWARE_METADATA_KEY, constructor) || [];
1472
+ }
1473
+ function getMethodMiddlewares(target, propertyKey) {
1474
+ return Reflect.getMetadata(METHOD_MIDDLEWARE_METADATA_KEY, target, propertyKey) || [];
1475
+ }
1476
+ // src/middleware/builtin/logger.ts
1477
+ import { LoggerManager as LoggerManager4 } from "@dangao/logsmith";
1478
+ function createLoggerMiddleware(options = {}) {
1479
+ const log = options.logger ?? ((message, details) => {
1480
+ const logger = LoggerManager4.getLogger();
1481
+ if (details) {
1482
+ logger.info(message, details);
1483
+ } else {
1484
+ logger.info(message);
1485
+ }
1486
+ });
1487
+ const prefix = options.prefix ?? "[Logger]";
1488
+ return async (context, next) => {
1489
+ let response;
1490
+ try {
1491
+ response = await next();
1492
+ return response;
1493
+ } finally {
1494
+ const status = response?.status ?? context.statusCode ?? 200;
1495
+ log(`${prefix} ${context.method} ${context.path} ${status}`);
1496
+ }
1497
+ };
1498
+ }
1499
+ function createRequestLoggingMiddleware(options = {}) {
1500
+ const log = options.logger ?? ((message, details) => {
1501
+ const logger = LoggerManager4.getLogger();
1502
+ logger.info(message, details);
1245
1503
  });
1504
+ const prefix = options.prefix ?? "[Request]";
1505
+ const setHeader = options.setHeader ?? true;
1506
+ return async (context, next) => {
1507
+ const start = performance.now();
1508
+ try {
1509
+ const response = await next();
1510
+ const duration = performance.now() - start;
1511
+ log(`${prefix} ${context.method} ${context.path} ${response.status} ${duration.toFixed(2)}ms`);
1512
+ if (setHeader) {
1513
+ context.setHeader("x-request-duration", duration.toFixed(2));
1514
+ }
1515
+ return response;
1516
+ } catch (error) {
1517
+ const duration = performance.now() - start;
1518
+ log(`${prefix} ${context.method} ${context.path} error ${duration.toFixed(2)}ms`, error instanceof Error ? { error: error.message } : undefined);
1519
+ throw error;
1520
+ }
1521
+ };
1246
1522
  }
1247
1523
  // src/middleware/builtin/error-handler.ts
1524
+ init_validation();
1525
+ import { LoggerManager as LoggerManager5 } from "@dangao/logsmith";
1526
+
1527
+ // src/error/index.ts
1528
+ init_http_exception();
1529
+ init_handler();
1530
+ init_error_codes();
1531
+ init_i18n();
1532
+
1533
+ // src/middleware/builtin/error-handler.ts
1534
+ init_handler();
1248
1535
  function createErrorHandlingMiddleware(options = {}) {
1249
1536
  const log = options.logger ?? ((error, context) => {
1250
1537
  LoggerManager5.getLogger().error("[Error]", { ...context, error });
@@ -1274,13 +1561,7 @@ function createErrorHandlingMiddleware(options = {}) {
1274
1561
  });
1275
1562
  }
1276
1563
  if (error instanceof HttpException) {
1277
- if (!expose) {
1278
- return await handleError(error, context);
1279
- }
1280
- return context.createResponse({
1281
- error: error.message,
1282
- details: error.details
1283
- }, { status: error.status });
1564
+ return await handleError(error, context);
1284
1565
  }
1285
1566
  if (error instanceof Error && !expose) {
1286
1567
  return await handleError(error, context);
@@ -1455,6 +1736,7 @@ function createStaticFileMiddleware(options) {
1455
1736
  };
1456
1737
  }
1457
1738
  // src/controller/controller.ts
1739
+ init_validation();
1458
1740
  var CONTROLLER_METADATA_KEY = Symbol("controller");
1459
1741
  function Controller(path = "") {
1460
1742
  return function(target) {
@@ -1538,23 +1820,8 @@ class ControllerRegistry {
1538
1820
  }
1539
1821
  return context.createResponse(responseData);
1540
1822
  } catch (error) {
1541
- if (error instanceof ValidationError) {
1542
- context.setStatus(400);
1543
- return context.createResponse({
1544
- error: error.message,
1545
- issues: error.issues
1546
- });
1547
- }
1548
- if (error instanceof HttpException) {
1549
- context.setStatus(error.status);
1550
- return context.createResponse({
1551
- error: error.message,
1552
- details: error.details
1553
- });
1554
- }
1555
- context.setStatus(500);
1556
- const errorMessage = error instanceof Error ? error.message : String(error);
1557
- return context.createResponse({ error: errorMessage });
1823
+ const { handleError: handleError2 } = await Promise.resolve().then(() => (init_handler(), exports_handler));
1824
+ return await handleError2(error, context);
1558
1825
  }
1559
1826
  };
1560
1827
  const middlewares = [...classMiddlewares];
@@ -2079,6 +2346,8 @@ class ResponseBuilder {
2079
2346
  });
2080
2347
  }
2081
2348
  }
2349
+ // src/index.ts
2350
+ init_validation();
2082
2351
  // src/extensions/logger-module.ts
2083
2352
  class LoggerModule {
2084
2353
  static forRoot(options = {}) {
@@ -2490,8 +2759,8 @@ class SecurityContextHolder {
2490
2759
  return context;
2491
2760
  }
2492
2761
  static runWithContext(callback) {
2493
- const existing = this.storage.getStore() ?? new SecurityContextImpl;
2494
- return this.storage.run(existing, callback);
2762
+ const context = new SecurityContextImpl;
2763
+ return this.storage.run(context, callback);
2495
2764
  }
2496
2765
  static clearContext() {
2497
2766
  const context = this.storage.getStore();
@@ -2531,6 +2800,10 @@ class RoleBasedAccessDecisionManager {
2531
2800
  return requiredAuthorities.some((required) => userAuthorities.includes(required));
2532
2801
  }
2533
2802
  }
2803
+ // src/security/filter.ts
2804
+ init_http_exception();
2805
+ init_error_codes();
2806
+
2534
2807
  // src/auth/decorators.ts
2535
2808
  import"reflect-metadata";
2536
2809
  var AUTH_METADATA_KEY = Symbol("@dangao/bun-server:auth");
@@ -2601,19 +2874,19 @@ function createSecurityFilter(config) {
2601
2874
  if (requiresAuth(controllerTarget, method)) {
2602
2875
  const authentication = securityContext.authentication;
2603
2876
  if (!authentication || !authentication.authenticated) {
2604
- throw new UnauthorizedException("Authentication required");
2877
+ throw new UnauthorizedException("Authentication required", undefined, "AUTH_REQUIRED" /* AUTH_REQUIRED */);
2605
2878
  }
2606
2879
  const requiredRoles = getRequiredRoles(controllerTarget, method);
2607
2880
  if (requiredRoles.length > 0) {
2608
2881
  const hasAccess = accessDecisionManager.decide(authentication, requiredRoles);
2609
2882
  if (!hasAccess) {
2610
2883
  const userRoles = authentication.authorities || [];
2611
- throw new ForbiddenException(`Insufficient permissions. Required roles: ${requiredRoles.join(", ")}, User roles: ${userRoles.join(", ")}`);
2884
+ throw new ForbiddenException(`Insufficient permissions. Required roles: ${requiredRoles.join(", ")}, User roles: ${userRoles.join(", ")}`, { requiredRoles, userRoles }, "AUTH_INSUFFICIENT_PERMISSIONS" /* AUTH_INSUFFICIENT_PERMISSIONS */);
2612
2885
  }
2613
2886
  }
2614
2887
  }
2615
2888
  } else if (defaultAuthRequired && !securityContext.isAuthenticated()) {
2616
- throw new UnauthorizedException("Authentication required");
2889
+ throw new UnauthorizedException("Authentication required", undefined, "AUTH_REQUIRED" /* AUTH_REQUIRED */);
2617
2890
  }
2618
2891
  ctx.security = securityContext;
2619
2892
  ctx.auth = {
@@ -2634,7 +2907,7 @@ function extractTokenFromHeader(ctx) {
2634
2907
  return null;
2635
2908
  }
2636
2909
  const parts = authHeader.split(" ");
2637
- if (parts.length !== 2 || parts[0] !== "Bearer") {
2910
+ if (parts.length !== 2 || parts[0].toLowerCase() !== "bearer") {
2638
2911
  return null;
2639
2912
  }
2640
2913
  return parts[1];
@@ -2680,9 +2953,11 @@ class JwtAuthenticationProvider {
2680
2953
  // src/security/providers/oauth2-provider.ts
2681
2954
  class OAuth2AuthenticationProvider {
2682
2955
  oauth2Service;
2956
+ jwtUtil;
2683
2957
  supportedTypes = ["oauth2", "authorization_code"];
2684
- constructor(oauth2Service) {
2958
+ constructor(oauth2Service, jwtUtil) {
2685
2959
  this.oauth2Service = oauth2Service;
2960
+ this.jwtUtil = jwtUtil;
2686
2961
  }
2687
2962
  supports(type) {
2688
2963
  return this.supportedTypes.includes(type.toLowerCase());
@@ -2696,15 +2971,14 @@ class OAuth2AuthenticationProvider {
2696
2971
  if (!tokenResponse) {
2697
2972
  return null;
2698
2973
  }
2699
- const userInfo = {
2700
- id: "user-1",
2701
- username: "user",
2702
- roles: ["user"]
2703
- };
2974
+ const payload = this.jwtUtil.verify(tokenResponse.accessToken);
2975
+ if (!payload || !payload.sub) {
2976
+ return null;
2977
+ }
2704
2978
  const principal = {
2705
- id: userInfo.id,
2706
- username: userInfo.username,
2707
- roles: userInfo.roles
2979
+ id: payload.sub,
2980
+ username: payload.username || payload.sub,
2981
+ roles: payload.roles || []
2708
2982
  };
2709
2983
  return {
2710
2984
  authenticated: true,
@@ -3088,7 +3362,7 @@ class SecurityModule {
3088
3362
  const oauth2Service = new OAuth2Service(jwtUtil, config.oauth2Clients || [], {}, userProvider);
3089
3363
  const authenticationManager = new AuthenticationManager;
3090
3364
  authenticationManager.registerProvider(new JwtAuthenticationProvider(jwtUtil));
3091
- authenticationManager.registerProvider(new OAuth2AuthenticationProvider(oauth2Service));
3365
+ authenticationManager.registerProvider(new OAuth2AuthenticationProvider(oauth2Service, jwtUtil));
3092
3366
  const securityFilter = createSecurityFilter({
3093
3367
  authenticationManager,
3094
3368
  excludePaths: [
@@ -3338,6 +3612,297 @@ HealthModule = __legacyDecorateClassTS([
3338
3612
  providers: []
3339
3613
  })
3340
3614
  ], HealthModule);
3615
+ // src/metrics/collector.ts
3616
+ class MetricsCollector {
3617
+ counters = new Map;
3618
+ gauges = new Map;
3619
+ histograms = new Map;
3620
+ customMetrics = [];
3621
+ registerCustomMetric(metric) {
3622
+ this.customMetrics.push(metric);
3623
+ }
3624
+ incrementCounter(name, labels, value = 1) {
3625
+ const key = this.getKey(name, labels);
3626
+ const counterMap = this.counters.get(name) || new Map;
3627
+ const current = counterMap.get(key) || 0;
3628
+ counterMap.set(key, current + value);
3629
+ this.counters.set(name, counterMap);
3630
+ }
3631
+ setGauge(name, labels, value) {
3632
+ const key = this.getKey(name, labels);
3633
+ const gaugeMap = this.gauges.get(name) || new Map;
3634
+ gaugeMap.set(key, value);
3635
+ this.gauges.set(name, gaugeMap);
3636
+ }
3637
+ observeHistogram(name, labels, value) {
3638
+ const key = this.getKey(name, labels);
3639
+ const histogramMap = this.histograms.get(name) || new Map;
3640
+ const values = histogramMap.get(key) || [];
3641
+ values.push(value);
3642
+ histogramMap.set(key, values);
3643
+ this.histograms.set(name, histogramMap);
3644
+ }
3645
+ async getAllDataPoints() {
3646
+ const dataPoints = [];
3647
+ for (const [name, counterMap] of this.counters.entries()) {
3648
+ for (const [key, value] of counterMap.entries()) {
3649
+ const labels = this.parseKey(key);
3650
+ dataPoints.push({
3651
+ name,
3652
+ type: "counter",
3653
+ value,
3654
+ labels: labels && Object.keys(labels).length > 0 ? labels : undefined
3655
+ });
3656
+ }
3657
+ }
3658
+ for (const [name, gaugeMap] of this.gauges.entries()) {
3659
+ for (const [key, value] of gaugeMap.entries()) {
3660
+ const labels = this.parseKey(key);
3661
+ dataPoints.push({
3662
+ name,
3663
+ type: "gauge",
3664
+ value,
3665
+ labels: labels && Object.keys(labels).length > 0 ? labels : undefined
3666
+ });
3667
+ }
3668
+ }
3669
+ for (const [name, histogramMap] of this.histograms.entries()) {
3670
+ for (const [key, values] of histogramMap.entries()) {
3671
+ const labels = this.parseKey(key);
3672
+ const sum = values.reduce((a, b) => a + b, 0);
3673
+ const count = values.length;
3674
+ const buckets = this.calculateBuckets(values);
3675
+ dataPoints.push({
3676
+ name: `${name}_sum`,
3677
+ type: "histogram",
3678
+ value: sum,
3679
+ labels: labels && Object.keys(labels).length > 0 ? labels : undefined
3680
+ });
3681
+ dataPoints.push({
3682
+ name: `${name}_count`,
3683
+ type: "histogram",
3684
+ value: count,
3685
+ labels: labels && Object.keys(labels).length > 0 ? labels : undefined
3686
+ });
3687
+ for (const [bucket, bucketCount] of Object.entries(buckets)) {
3688
+ dataPoints.push({
3689
+ name: `${name}_bucket`,
3690
+ type: "histogram",
3691
+ value: bucketCount,
3692
+ labels: {
3693
+ ...labels,
3694
+ le: bucket
3695
+ }
3696
+ });
3697
+ }
3698
+ }
3699
+ }
3700
+ for (const metric of this.customMetrics) {
3701
+ try {
3702
+ const value = await metric.getValue();
3703
+ dataPoints.push({
3704
+ name: metric.name,
3705
+ type: metric.type,
3706
+ value,
3707
+ help: metric.help
3708
+ });
3709
+ } catch (error) {
3710
+ console.error(`Failed to collect custom metric ${metric.name}:`, error);
3711
+ }
3712
+ }
3713
+ return dataPoints;
3714
+ }
3715
+ reset() {
3716
+ this.counters.clear();
3717
+ this.gauges.clear();
3718
+ this.histograms.clear();
3719
+ }
3720
+ getKey(name, labels) {
3721
+ if (!labels || Object.keys(labels).length === 0) {
3722
+ return "";
3723
+ }
3724
+ const sortedLabels = Object.keys(labels).sort().map((key) => `${key}="${labels[key]}"`).join(",");
3725
+ return `{${sortedLabels}}`;
3726
+ }
3727
+ parseKey(key) {
3728
+ if (!key || key === "") {
3729
+ return;
3730
+ }
3731
+ const labels = {};
3732
+ const match = key.match(/\{([^}]+)\}/);
3733
+ if (match) {
3734
+ const labelPairs = match[1].split(",");
3735
+ for (const pair of labelPairs) {
3736
+ const [k, v] = pair.split("=");
3737
+ if (k && v) {
3738
+ labels[k.trim()] = v.trim().replace(/^"|"$/g, "");
3739
+ }
3740
+ }
3741
+ }
3742
+ return Object.keys(labels).length > 0 ? labels : undefined;
3743
+ }
3744
+ calculateBuckets(values) {
3745
+ const defaultBuckets = [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10];
3746
+ const buckets = {};
3747
+ for (const bucket of defaultBuckets) {
3748
+ buckets[bucket.toString()] = values.filter((v) => v <= bucket).length;
3749
+ }
3750
+ buckets["+Inf"] = values.length;
3751
+ return buckets;
3752
+ }
3753
+ }
3754
+
3755
+ // src/metrics/prometheus.ts
3756
+ class PrometheusFormatter {
3757
+ format(dataPoints) {
3758
+ const lines = [];
3759
+ const metricGroups = this.groupByMetricName(dataPoints);
3760
+ for (const [metricName, points] of metricGroups.entries()) {
3761
+ const help = points[0]?.help;
3762
+ if (help) {
3763
+ lines.push(`# HELP ${metricName} ${help}`);
3764
+ }
3765
+ const type = points[0]?.type;
3766
+ if (type) {
3767
+ lines.push(`# TYPE ${metricName} ${type}`);
3768
+ }
3769
+ for (const point of points) {
3770
+ const labelString = this.formatLabels(point.labels);
3771
+ const line = labelString ? `${point.name}${labelString} ${point.value}` : `${point.name} ${point.value}`;
3772
+ lines.push(line);
3773
+ }
3774
+ lines.push("");
3775
+ }
3776
+ return lines.join(`
3777
+ `);
3778
+ }
3779
+ groupByMetricName(dataPoints) {
3780
+ const groups = new Map;
3781
+ for (const point of dataPoints) {
3782
+ const name = point.name;
3783
+ const existing = groups.get(name) || [];
3784
+ existing.push(point);
3785
+ groups.set(name, existing);
3786
+ }
3787
+ return groups;
3788
+ }
3789
+ formatLabels(labels) {
3790
+ if (!labels || Object.keys(labels).length === 0) {
3791
+ return "";
3792
+ }
3793
+ const labelPairs = Object.keys(labels).sort().map((key) => `${key}="${this.escapeLabelValue(labels[key])}"`).join(",");
3794
+ return `{${labelPairs}}`;
3795
+ }
3796
+ escapeLabelValue(value) {
3797
+ return value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"").replace(/\n/g, "\\n");
3798
+ }
3799
+ }
3800
+
3801
+ // src/metrics/types.ts
3802
+ var METRICS_SERVICE_TOKEN = Symbol("@dangao/bun-server:metrics:service");
3803
+ var METRICS_OPTIONS_TOKEN = Symbol("@dangao/bun-server:metrics:options");
3804
+
3805
+ // src/metrics/controller.ts
3806
+ class MetricsController {
3807
+ collector;
3808
+ options;
3809
+ formatter;
3810
+ constructor(collector, options) {
3811
+ this.collector = collector;
3812
+ this.options = options;
3813
+ this.formatter = new PrometheusFormatter;
3814
+ }
3815
+ async metrics() {
3816
+ const dataPoints = await this.collector.getAllDataPoints();
3817
+ const prometheusText = this.formatter.format(dataPoints);
3818
+ return new Response(prometheusText, {
3819
+ headers: {
3820
+ "Content-Type": "text/plain; version=0.0.4; charset=utf-8"
3821
+ }
3822
+ });
3823
+ }
3824
+ }
3825
+ __legacyDecorateClassTS([
3826
+ GET("/metrics"),
3827
+ __legacyMetadataTS("design:type", Function),
3828
+ __legacyMetadataTS("design:paramtypes", []),
3829
+ __legacyMetadataTS("design:returntype", typeof Promise === "undefined" ? Object : Promise)
3830
+ ], MetricsController.prototype, "metrics", null);
3831
+ MetricsController = __legacyDecorateClassTS([
3832
+ Controller("/"),
3833
+ __legacyDecorateParamTS(0, Inject(METRICS_SERVICE_TOKEN)),
3834
+ __legacyDecorateParamTS(1, Inject(METRICS_OPTIONS_TOKEN)),
3835
+ __legacyMetadataTS("design:paramtypes", [
3836
+ typeof MetricsCollector === "undefined" ? Object : MetricsCollector,
3837
+ typeof MetricsModuleOptions === "undefined" ? Object : MetricsModuleOptions
3838
+ ])
3839
+ ], MetricsController);
3840
+
3841
+ // src/metrics/metrics-module.ts
3842
+ class MetricsModule {
3843
+ static forRoot(options = {}) {
3844
+ const providers2 = [];
3845
+ const collector = new MetricsCollector;
3846
+ if (options.customMetrics) {
3847
+ for (const metric of options.customMetrics) {
3848
+ collector.registerCustomMetric(metric);
3849
+ }
3850
+ }
3851
+ providers2.push({
3852
+ provide: METRICS_SERVICE_TOKEN,
3853
+ useValue: collector
3854
+ }, {
3855
+ provide: METRICS_OPTIONS_TOKEN,
3856
+ useValue: options
3857
+ }, MetricsCollector);
3858
+ const existingMetadata = Reflect.getMetadata(MODULE_METADATA_KEY, MetricsModule) || {};
3859
+ const metadata = {
3860
+ ...existingMetadata,
3861
+ controllers: [...existingMetadata.controllers || [], MetricsController],
3862
+ providers: [...existingMetadata.providers || [], ...providers2],
3863
+ exports: [
3864
+ ...existingMetadata.exports || [],
3865
+ METRICS_SERVICE_TOKEN,
3866
+ MetricsCollector
3867
+ ]
3868
+ };
3869
+ Reflect.defineMetadata(MODULE_METADATA_KEY, metadata, MetricsModule);
3870
+ return MetricsModule;
3871
+ }
3872
+ }
3873
+ MetricsModule = __legacyDecorateClassTS([
3874
+ Module({
3875
+ controllers: [MetricsController],
3876
+ providers: []
3877
+ })
3878
+ ], MetricsModule);
3879
+ // src/metrics/middleware.ts
3880
+ function createHttpMetricsMiddleware(collector) {
3881
+ return async (context2, next) => {
3882
+ const startTime = Date.now();
3883
+ const response = await next();
3884
+ const duration = Date.now() - startTime;
3885
+ const durationSeconds = duration / 1000;
3886
+ const method = context2.method;
3887
+ const path = context2.path;
3888
+ const statusCode = response.status;
3889
+ collector.incrementCounter("http_requests_total", {
3890
+ method,
3891
+ path,
3892
+ status: statusCode.toString()
3893
+ });
3894
+ collector.observeHistogram("http_request_duration_seconds", {
3895
+ method,
3896
+ path,
3897
+ status: statusCode.toString()
3898
+ }, durationSeconds);
3899
+ collector.observeHistogram("http_request_duration_seconds_summary", {
3900
+ method,
3901
+ path
3902
+ }, durationSeconds);
3903
+ return response;
3904
+ };
3905
+ }
3341
3906
  // src/testing/harness.ts
3342
3907
  import { performance as performance2 } from "perf_hooks";
3343
3908
 
@@ -3401,11 +3966,15 @@ class StressTester {
3401
3966
  export {
3402
3967
  requiresAuth,
3403
3968
  getAuthMetadata,
3969
+ createUserKeyGenerator,
3970
+ createTokenKeyGenerator,
3404
3971
  createSwaggerUIMiddleware,
3405
3972
  createStaticFileMiddleware,
3406
3973
  createSecurityFilter,
3407
3974
  createRequestLoggingMiddleware,
3975
+ createRateLimitMiddleware,
3408
3976
  createLoggerMiddleware,
3977
+ createHttpMetricsMiddleware,
3409
3978
  createFileUploadMiddleware,
3410
3979
  createErrorHandlingMiddleware,
3411
3980
  createCorsMiddleware,
@@ -3428,7 +3997,9 @@ export {
3428
3997
  RoleBasedAccessDecisionManager,
3429
3998
  ResponseBuilder,
3430
3999
  RequestWrapper,
4000
+ RateLimit,
3431
4001
  Query,
4002
+ PrometheusFormatter,
3432
4003
  PerformanceHarness,
3433
4004
  ParamBinder,
3434
4005
  Param,
@@ -3447,6 +4018,10 @@ export {
3447
4018
  Module,
3448
4019
  MinLength,
3449
4020
  MiddlewarePipeline,
4021
+ MetricsModule,
4022
+ MetricsCollector,
4023
+ METRICS_SERVICE_TOKEN,
4024
+ METRICS_OPTIONS_TOKEN,
3450
4025
  LoggerModule,
3451
4026
  LoggerExtension,
3452
4027
  LogLevel2 as LogLevel,