@dangao/bun-server 0.1.3 → 0.1.5

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 (85) hide show
  1. package/dist/auth/controller.d.ts +35 -0
  2. package/dist/auth/controller.d.ts.map +1 -0
  3. package/dist/auth/decorators.d.ts +38 -0
  4. package/dist/auth/decorators.d.ts.map +1 -0
  5. package/dist/auth/index.d.ts +6 -0
  6. package/dist/auth/index.d.ts.map +1 -0
  7. package/dist/auth/jwt.d.ts +43 -0
  8. package/dist/auth/jwt.d.ts.map +1 -0
  9. package/dist/auth/oauth2.d.ts +45 -0
  10. package/dist/auth/oauth2.d.ts.map +1 -0
  11. package/dist/auth/types.d.ts +238 -0
  12. package/dist/auth/types.d.ts.map +1 -0
  13. package/dist/config/config-module.d.ts +14 -0
  14. package/dist/config/config-module.d.ts.map +1 -0
  15. package/dist/config/index.d.ts +4 -0
  16. package/dist/config/index.d.ts.map +1 -0
  17. package/dist/config/service.d.ts +32 -0
  18. package/dist/config/service.d.ts.map +1 -0
  19. package/dist/config/types.d.ts +22 -0
  20. package/dist/config/types.d.ts.map +1 -0
  21. package/dist/controller/controller.d.ts +5 -0
  22. package/dist/controller/controller.d.ts.map +1 -1
  23. package/dist/core/application.d.ts.map +1 -1
  24. package/dist/core/context.d.ts.map +1 -1
  25. package/dist/di/module-registry.d.ts +14 -2
  26. package/dist/di/module-registry.d.ts.map +1 -1
  27. package/dist/di/module.d.ts +12 -1
  28. package/dist/di/module.d.ts.map +1 -1
  29. package/dist/extensions/index.d.ts +1 -0
  30. package/dist/extensions/index.d.ts.map +1 -1
  31. package/dist/extensions/logger-module.d.ts +30 -0
  32. package/dist/extensions/logger-module.d.ts.map +1 -0
  33. package/dist/health/controller.d.ts +27 -0
  34. package/dist/health/controller.d.ts.map +1 -0
  35. package/dist/health/health-module.d.ts +9 -0
  36. package/dist/health/health-module.d.ts.map +1 -0
  37. package/dist/health/index.d.ts +4 -0
  38. package/dist/health/index.d.ts.map +1 -0
  39. package/dist/health/types.d.ts +22 -0
  40. package/dist/health/types.d.ts.map +1 -0
  41. package/dist/index.d.ts +6 -1
  42. package/dist/index.d.ts.map +1 -1
  43. package/dist/index.js +1388 -13
  44. package/dist/router/registry.d.ts +4 -1
  45. package/dist/router/registry.d.ts.map +1 -1
  46. package/dist/router/route.d.ts +9 -1
  47. package/dist/router/route.d.ts.map +1 -1
  48. package/dist/router/router.d.ts +14 -2
  49. package/dist/router/router.d.ts.map +1 -1
  50. package/dist/security/access-decision-manager.d.ts +14 -0
  51. package/dist/security/access-decision-manager.d.ts.map +1 -0
  52. package/dist/security/authentication-manager.d.ts +22 -0
  53. package/dist/security/authentication-manager.d.ts.map +1 -0
  54. package/dist/security/context.d.ts +51 -0
  55. package/dist/security/context.d.ts.map +1 -0
  56. package/dist/security/filter.d.ts +27 -0
  57. package/dist/security/filter.d.ts.map +1 -0
  58. package/dist/security/index.d.ts +8 -0
  59. package/dist/security/index.d.ts.map +1 -0
  60. package/dist/security/providers/index.d.ts +3 -0
  61. package/dist/security/providers/index.d.ts.map +1 -0
  62. package/dist/security/providers/jwt-provider.d.ts +19 -0
  63. package/dist/security/providers/jwt-provider.d.ts.map +1 -0
  64. package/dist/security/providers/oauth2-provider.d.ts +19 -0
  65. package/dist/security/providers/oauth2-provider.d.ts.map +1 -0
  66. package/dist/security/security-module.d.ts +50 -0
  67. package/dist/security/security-module.d.ts.map +1 -0
  68. package/dist/security/types.d.ts +152 -0
  69. package/dist/security/types.d.ts.map +1 -0
  70. package/dist/swagger/decorators.d.ts +56 -0
  71. package/dist/swagger/decorators.d.ts.map +1 -0
  72. package/dist/swagger/generator.d.ts +17 -0
  73. package/dist/swagger/generator.d.ts.map +1 -0
  74. package/dist/swagger/index.d.ts +7 -0
  75. package/dist/swagger/index.d.ts.map +1 -0
  76. package/dist/swagger/swagger-extension.d.ts +25 -0
  77. package/dist/swagger/swagger-extension.d.ts.map +1 -0
  78. package/dist/swagger/swagger-module.d.ts +37 -0
  79. package/dist/swagger/swagger-module.d.ts.map +1 -0
  80. package/dist/swagger/types.d.ts +176 -0
  81. package/dist/swagger/types.d.ts.map +1 -0
  82. package/dist/swagger/ui.d.ts +13 -0
  83. package/dist/swagger/ui.d.ts.map +1 -0
  84. package/package.json +1 -1
  85. package/readme.md +14 -10
package/dist/index.js CHANGED
@@ -1,4 +1,20 @@
1
1
  // @bun
2
+ var __legacyDecorateClassTS = function(decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
5
+ r = Reflect.decorate(decorators, target, key, desc);
6
+ else
7
+ for (var i = decorators.length - 1;i >= 0; i--)
8
+ if (d = decorators[i])
9
+ r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
10
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
11
+ };
12
+ var __legacyDecorateParamTS = (index, decorator) => (target, key) => decorator(target, key, index);
13
+ var __legacyMetadataTS = (k, v) => {
14
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function")
15
+ return Reflect.metadata(k, v);
16
+ };
17
+
2
18
  // src/request/body-parser.ts
3
19
  class BodyParser {
4
20
  static async parse(request) {
@@ -141,9 +157,12 @@ class Context {
141
157
  this.statusCode = code;
142
158
  }
143
159
  createResponse(body, init) {
160
+ if (body instanceof Response) {
161
+ return body;
162
+ }
144
163
  const headers = new Headers(this.responseHeaders);
145
164
  if (body !== undefined && body !== null) {
146
- if (typeof body === "object" && !(body instanceof Response) && !(body instanceof ArrayBuffer)) {
165
+ if (typeof body === "object" && !(body instanceof ArrayBuffer) && !(body instanceof Blob)) {
147
166
  headers.set("Content-Type", "application/json");
148
167
  const jsonString = JSON.stringify(body);
149
168
  return new Response(jsonString, {
@@ -271,15 +290,19 @@ class Route {
271
290
  method;
272
291
  path;
273
292
  handler;
293
+ controllerClass;
294
+ methodName;
274
295
  pattern;
275
296
  paramNames;
276
297
  middlewarePipeline;
277
298
  staticKey;
278
299
  isStatic;
279
- constructor(method, path, handler, middlewares = []) {
300
+ constructor(method, path, handler, middlewares = [], controllerClass, methodName) {
280
301
  this.method = method;
281
302
  this.path = path;
282
303
  this.handler = handler;
304
+ this.controllerClass = controllerClass;
305
+ this.methodName = methodName;
283
306
  const { pattern, paramNames } = this.parsePath(path);
284
307
  this.pattern = pattern;
285
308
  this.paramNames = paramNames;
@@ -328,8 +351,15 @@ class Router {
328
351
  routes = [];
329
352
  staticRoutes = new Map;
330
353
  dynamicRoutes = [];
331
- register(method, path, handler, middlewares = []) {
332
- const route = new Route(method, path, handler, middlewares);
354
+ normalizePath(path) {
355
+ if (path.length > 1 && path.endsWith("/")) {
356
+ return path.slice(0, -1);
357
+ }
358
+ return path;
359
+ }
360
+ register(method, path, handler, middlewares = [], controllerClass, methodName) {
361
+ const normalizedPath = this.normalizePath(path);
362
+ const route = new Route(method, normalizedPath, handler, middlewares, controllerClass, methodName);
333
363
  this.routes.push(route);
334
364
  const staticKey = route.getStaticKey();
335
365
  if (staticKey) {
@@ -366,16 +396,32 @@ class Router {
366
396
  }
367
397
  return;
368
398
  }
369
- async handle(context) {
399
+ async preHandle(context) {
370
400
  const method = context.method;
371
- const route = this.findRoute(method, context.path);
401
+ const path = this.normalizePath(context.path);
402
+ const route = this.findRoute(method, path);
372
403
  if (!route) {
373
404
  return;
374
405
  }
375
- const match = route.match(method, context.path);
406
+ const match = route.match(method, path);
376
407
  if (match.matched) {
377
408
  context.params = match.params;
378
409
  }
410
+ if (route.controllerClass && route.methodName) {
411
+ context.routeHandler = {
412
+ controller: route.controllerClass,
413
+ method: route.methodName
414
+ };
415
+ }
416
+ }
417
+ async handle(context) {
418
+ await this.preHandle(context);
419
+ const method = context.method;
420
+ const path = this.normalizePath(context.path);
421
+ const route = this.findRoute(method, path);
422
+ if (!route) {
423
+ return;
424
+ }
379
425
  return await route.execute(context);
380
426
  }
381
427
  getRoutes() {
@@ -401,8 +447,8 @@ class RouteRegistry {
401
447
  }
402
448
  return RouteRegistry.instance;
403
449
  }
404
- register(method, path, handler, middlewares = []) {
405
- this.router.register(method, path, handler, middlewares);
450
+ register(method, path, handler, middlewares = [], controllerClass, methodName) {
451
+ this.router.register(method, path, handler, middlewares, controllerClass, methodName);
406
452
  }
407
453
  get(path, handler, middlewares = []) {
408
454
  this.router.get(path, handler, middlewares);
@@ -1487,6 +1533,9 @@ class ControllerRegistry {
1487
1533
  }
1488
1534
  const result = method.apply(controllerInstance, params);
1489
1535
  const responseData = await Promise.resolve(result);
1536
+ if (responseData instanceof Response) {
1537
+ return responseData;
1538
+ }
1490
1539
  return context.createResponse(responseData);
1491
1540
  } catch (error) {
1492
1541
  if (error instanceof ValidationError) {
@@ -1512,7 +1561,7 @@ class ControllerRegistry {
1512
1561
  if (propertyKey) {
1513
1562
  middlewares.push(...getMethodMiddlewares(prototype, propertyKey));
1514
1563
  }
1515
- registry.register(route.method, fullPath, handler, middlewares);
1564
+ registry.register(route.method, fullPath, handler, middlewares, controllerClass, propertyKey);
1516
1565
  }
1517
1566
  }
1518
1567
  combinePaths(basePath, methodPath) {
@@ -1526,6 +1575,9 @@ class ControllerRegistry {
1526
1575
  getContainer() {
1527
1576
  return this.container;
1528
1577
  }
1578
+ getRegisteredControllers() {
1579
+ return Array.from(this.controllerContainers.keys());
1580
+ }
1529
1581
  clear() {
1530
1582
  this.controllers.clear();
1531
1583
  this.container.clear();
@@ -1685,7 +1737,9 @@ function getModuleMetadata(moduleClass) {
1685
1737
  imports: metadata.imports ?? [],
1686
1738
  controllers: metadata.controllers ?? [],
1687
1739
  providers: metadata.providers ?? [],
1688
- exports: metadata.exports ?? []
1740
+ exports: metadata.exports ?? [],
1741
+ extensions: metadata.extensions ?? [],
1742
+ middlewares: metadata.middlewares ?? []
1689
1743
  };
1690
1744
  }
1691
1745
 
@@ -1744,7 +1798,9 @@ class ModuleRegistry {
1744
1798
  metadata,
1745
1799
  container,
1746
1800
  controllersRegistered: false,
1747
- attachedParents: new Set
1801
+ attachedParents: new Set,
1802
+ extensions: metadata.extensions ?? [],
1803
+ middlewares: metadata.middlewares ?? []
1748
1804
  };
1749
1805
  this.registerControllers(ref);
1750
1806
  this.moduleRefs.set(moduleClass, ref);
@@ -1796,6 +1852,27 @@ class ModuleRegistry {
1796
1852
  for (const exportedToken of moduleRef.metadata.exports) {
1797
1853
  this.registerExport(parentContainer, moduleRef, exportedToken);
1798
1854
  }
1855
+ for (const imported of moduleRef.metadata.imports) {
1856
+ const importedRef = this.moduleRefs.get(imported);
1857
+ if (importedRef) {
1858
+ moduleRef.extensions.push(...importedRef.extensions);
1859
+ moduleRef.middlewares.push(...importedRef.middlewares);
1860
+ }
1861
+ }
1862
+ }
1863
+ getModuleExtensions(moduleClass) {
1864
+ const moduleRef = this.moduleRefs.get(moduleClass);
1865
+ if (!moduleRef) {
1866
+ return [];
1867
+ }
1868
+ return moduleRef.extensions;
1869
+ }
1870
+ getModuleMiddlewares(moduleClass) {
1871
+ const moduleRef = this.moduleRefs.get(moduleClass);
1872
+ if (!moduleRef) {
1873
+ return [];
1874
+ }
1875
+ return moduleRef.middlewares;
1799
1876
  }
1800
1877
  registerExport(parentContainer, moduleRef, token) {
1801
1878
  if (!moduleRef.container.isRegistered(token)) {
@@ -1852,6 +1929,7 @@ class Application {
1852
1929
  }
1853
1930
  const registry = RouteRegistry.getInstance();
1854
1931
  const router = registry.getRouter();
1932
+ await router.preHandle(context);
1855
1933
  return await this.middlewarePipeline.run(context, async () => {
1856
1934
  const response = await router.handle(context);
1857
1935
  if (response) {
@@ -1868,6 +1946,14 @@ class Application {
1868
1946
  registerModule(moduleClass) {
1869
1947
  const registry = ModuleRegistry.getInstance();
1870
1948
  registry.register(moduleClass, this.getContainer());
1949
+ const extensions = registry.getModuleExtensions(moduleClass);
1950
+ for (const extension of extensions) {
1951
+ this.registerExtension(extension);
1952
+ }
1953
+ const middlewares = registry.getModuleMiddlewares(moduleClass);
1954
+ for (const middleware of middlewares) {
1955
+ this.use(middleware);
1956
+ }
1871
1957
  }
1872
1958
  registerWebSocketGateway(gatewayClass) {
1873
1959
  this.websocketRegistry.register(gatewayClass);
@@ -1993,8 +2079,1265 @@ class ResponseBuilder {
1993
2079
  });
1994
2080
  }
1995
2081
  }
2082
+ // src/extensions/logger-module.ts
2083
+ class LoggerModule {
2084
+ static forRoot(options = {}) {
2085
+ const extensions = [];
2086
+ const middlewares = [];
2087
+ const loggerExtension = new LoggerExtension(options.logger);
2088
+ extensions.push(loggerExtension);
2089
+ if (options.enableRequestLogging !== false) {
2090
+ const requestLoggingMiddleware = createLoggerMiddleware({
2091
+ prefix: options.requestLoggingPrefix
2092
+ });
2093
+ middlewares.push(requestLoggingMiddleware);
2094
+ }
2095
+ const existingMetadata = Reflect.getMetadata(MODULE_METADATA_KEY, LoggerModule) || {};
2096
+ const metadata = {
2097
+ ...existingMetadata,
2098
+ extensions,
2099
+ middlewares
2100
+ };
2101
+ Reflect.defineMetadata(MODULE_METADATA_KEY, metadata, LoggerModule);
2102
+ return LoggerModule;
2103
+ }
2104
+ }
2105
+ LoggerModule = __legacyDecorateClassTS([
2106
+ Module({
2107
+ extensions: [],
2108
+ middlewares: []
2109
+ })
2110
+ ], LoggerModule);
2111
+
1996
2112
  // src/extensions/index.ts
1997
2113
  import { LoggerManager as LoggerManager7, LogLevel as LogLevel2, SimpleLogger as SimpleLogger2 } from "@dangao/logsmith";
2114
+ // src/swagger/decorators.ts
2115
+ import"reflect-metadata";
2116
+ var API_TAG_METADATA_KEY = Symbol("swagger:api-tag");
2117
+ var API_OPERATION_METADATA_KEY = Symbol("swagger:api-operation");
2118
+ var API_PARAM_METADATA_KEY = Symbol("swagger:api-param");
2119
+ var API_BODY_METADATA_KEY = Symbol("swagger:api-body");
2120
+ var API_RESPONSE_METADATA_KEY = Symbol("swagger:api-response");
2121
+ function ApiTags(...tags) {
2122
+ return function(target, propertyKey) {
2123
+ if (propertyKey === undefined) {
2124
+ Reflect.defineMetadata(API_TAG_METADATA_KEY, tags, target);
2125
+ } else {
2126
+ const existingTags = Reflect.getMetadata(API_TAG_METADATA_KEY, target, propertyKey) || [];
2127
+ Reflect.defineMetadata(API_TAG_METADATA_KEY, [...existingTags, ...tags], target, propertyKey);
2128
+ }
2129
+ };
2130
+ }
2131
+ function ApiOperation(metadata) {
2132
+ return function(target, propertyKey) {
2133
+ Reflect.defineMetadata(API_OPERATION_METADATA_KEY, metadata, target, propertyKey);
2134
+ };
2135
+ }
2136
+ function ApiParam(metadata) {
2137
+ return function(target, propertyKey, parameterIndex) {
2138
+ const existingParams = Reflect.getMetadata(API_PARAM_METADATA_KEY, target, propertyKey) || [];
2139
+ existingParams.push({ index: parameterIndex, metadata });
2140
+ Reflect.defineMetadata(API_PARAM_METADATA_KEY, existingParams, target, propertyKey);
2141
+ };
2142
+ }
2143
+ function ApiBody(metadata) {
2144
+ return function(target, propertyKey) {
2145
+ Reflect.defineMetadata(API_BODY_METADATA_KEY, metadata, target, propertyKey);
2146
+ };
2147
+ }
2148
+ function ApiResponse(metadata) {
2149
+ return function(target, propertyKey) {
2150
+ const existingResponses = Reflect.getMetadata(API_RESPONSE_METADATA_KEY, target, propertyKey) || [];
2151
+ existingResponses.push(metadata);
2152
+ Reflect.defineMetadata(API_RESPONSE_METADATA_KEY, existingResponses, target, propertyKey);
2153
+ };
2154
+ }
2155
+ function getApiTags(target, propertyKey) {
2156
+ if (propertyKey === undefined) {
2157
+ return Reflect.getMetadata(API_TAG_METADATA_KEY, target) || [];
2158
+ }
2159
+ return Reflect.getMetadata(API_TAG_METADATA_KEY, target, propertyKey) || [];
2160
+ }
2161
+ function getApiOperation(target, propertyKey) {
2162
+ return Reflect.getMetadata(API_OPERATION_METADATA_KEY, target, propertyKey);
2163
+ }
2164
+ function getApiParams(target, propertyKey) {
2165
+ return Reflect.getMetadata(API_PARAM_METADATA_KEY, target, propertyKey) || [];
2166
+ }
2167
+ function getApiBody(target, propertyKey) {
2168
+ return Reflect.getMetadata(API_BODY_METADATA_KEY, target, propertyKey);
2169
+ }
2170
+ function getApiResponses(target, propertyKey) {
2171
+ return Reflect.getMetadata(API_RESPONSE_METADATA_KEY, target, propertyKey) || [];
2172
+ }
2173
+ // src/swagger/generator.ts
2174
+ class SwaggerGenerator {
2175
+ options;
2176
+ constructor(options) {
2177
+ this.options = options;
2178
+ }
2179
+ generate() {
2180
+ const document = {
2181
+ openapi: "3.0.0",
2182
+ info: {
2183
+ title: this.options.info.title,
2184
+ version: this.options.info.version,
2185
+ description: this.options.info.description,
2186
+ contact: this.options.info.contact,
2187
+ license: this.options.info.license
2188
+ },
2189
+ servers: this.options.servers,
2190
+ tags: this.options.tags,
2191
+ paths: {}
2192
+ };
2193
+ const controllerRegistry = ControllerRegistry.getInstance();
2194
+ const controllers = controllerRegistry.getRegisteredControllers();
2195
+ if (controllers.length === 0) {
2196
+ return document;
2197
+ }
2198
+ for (const controllerClass of controllers) {
2199
+ const controllerMetadata = getControllerMetadata(controllerClass);
2200
+ if (!controllerMetadata) {
2201
+ console.log(`[SwaggerGenerator] No metadata for controller: ${controllerClass.name}`);
2202
+ continue;
2203
+ }
2204
+ const basePath = controllerMetadata.path;
2205
+ const prototype = controllerClass.prototype;
2206
+ const routes = getRouteMetadata(prototype);
2207
+ if (!routes || routes.length === 0) {
2208
+ continue;
2209
+ }
2210
+ const controllerTags = getApiTags(controllerClass);
2211
+ for (const route of routes) {
2212
+ let propertyKey = route.propertyKey;
2213
+ if (!propertyKey && route.handler) {
2214
+ propertyKey = route.handler.name;
2215
+ if (!propertyKey || propertyKey === "") {
2216
+ const propertyNames = Object.getOwnPropertyNames(prototype);
2217
+ for (const key of propertyNames) {
2218
+ if (key === "constructor")
2219
+ continue;
2220
+ const descriptor = Object.getOwnPropertyDescriptor(prototype, key);
2221
+ if (descriptor && descriptor.value === route.handler) {
2222
+ propertyKey = key;
2223
+ break;
2224
+ }
2225
+ }
2226
+ }
2227
+ }
2228
+ if (!propertyKey) {
2229
+ continue;
2230
+ }
2231
+ const method = route.method.toLowerCase();
2232
+ let methodPath = route.path;
2233
+ if (methodPath && !methodPath.startsWith("/")) {
2234
+ methodPath = "/" + methodPath;
2235
+ }
2236
+ const swaggerPath = (basePath + methodPath).replace(/:([^/]+)/g, "{$1}");
2237
+ const fullPath = this.normalizePath(swaggerPath);
2238
+ const operationMetadata = getApiOperation(prototype, propertyKey);
2239
+ const methodTags = getApiTags(prototype, propertyKey);
2240
+ const params = getApiParams(prototype, propertyKey);
2241
+ const body = getApiBody(prototype, propertyKey);
2242
+ const responses = getApiResponses(prototype, propertyKey);
2243
+ const tags = [...new Set([...controllerTags, ...methodTags])];
2244
+ const pathItem = {
2245
+ summary: operationMetadata?.summary,
2246
+ description: operationMetadata?.description,
2247
+ operationId: operationMetadata?.operationId || propertyKey,
2248
+ tags: tags.length > 0 ? tags : undefined,
2249
+ deprecated: operationMetadata?.deprecated
2250
+ };
2251
+ const pathParams = [];
2252
+ const pathParamMatches = fullPath.matchAll(/\{([^}]+)\}/g);
2253
+ for (const match of pathParamMatches) {
2254
+ const paramName = match[1];
2255
+ const existingParam = params.find((p) => p.metadata.name === paramName && p.metadata.in === "path");
2256
+ if (!existingParam) {
2257
+ pathParams.push({
2258
+ name: paramName,
2259
+ in: "path",
2260
+ required: true,
2261
+ schema: { type: "string" }
2262
+ });
2263
+ }
2264
+ }
2265
+ for (const param of params) {
2266
+ pathParams.push({
2267
+ name: param.metadata.name,
2268
+ in: param.metadata.in,
2269
+ description: param.metadata.description,
2270
+ required: param.metadata.required,
2271
+ schema: param.metadata.schema
2272
+ });
2273
+ }
2274
+ if (pathParams.length > 0) {
2275
+ pathItem.parameters = pathParams;
2276
+ }
2277
+ if (body) {
2278
+ pathItem.requestBody = {
2279
+ description: body.description,
2280
+ required: body.required,
2281
+ content: {
2282
+ "application/json": {
2283
+ schema: body.schema,
2284
+ examples: body.examples
2285
+ }
2286
+ }
2287
+ };
2288
+ }
2289
+ if (responses.length > 0) {
2290
+ pathItem.responses = {};
2291
+ for (const response of responses) {
2292
+ pathItem.responses[String(response.status)] = {
2293
+ description: response.description,
2294
+ content: {
2295
+ "application/json": {
2296
+ schema: response.schema,
2297
+ examples: response.examples
2298
+ }
2299
+ }
2300
+ };
2301
+ }
2302
+ } else {
2303
+ pathItem.responses = {
2304
+ "200": {
2305
+ description: "Success"
2306
+ }
2307
+ };
2308
+ }
2309
+ if (!document.paths[fullPath]) {
2310
+ document.paths[fullPath] = {};
2311
+ }
2312
+ document.paths[fullPath][method] = pathItem;
2313
+ }
2314
+ }
2315
+ return document;
2316
+ }
2317
+ normalizePath(path) {
2318
+ path = path.replace(/\/+/g, "/");
2319
+ if (!path.startsWith("/")) {
2320
+ path = "/" + path;
2321
+ }
2322
+ if (path.length > 1 && path.endsWith("/")) {
2323
+ path = path.slice(0, -1);
2324
+ }
2325
+ return path;
2326
+ }
2327
+ }
2328
+ // src/swagger/swagger-extension.ts
2329
+ class SwaggerExtension {
2330
+ options;
2331
+ generator;
2332
+ constructor(options) {
2333
+ this.options = options;
2334
+ }
2335
+ register(_container) {
2336
+ this.generator = new SwaggerGenerator(this.options);
2337
+ }
2338
+ getGenerator() {
2339
+ if (!this.generator) {
2340
+ this.generator = new SwaggerGenerator(this.options);
2341
+ }
2342
+ return this.generator;
2343
+ }
2344
+ generateJSON() {
2345
+ return JSON.stringify(this.getGenerator().generate(), null, 2);
2346
+ }
2347
+ }
2348
+ // src/swagger/ui.ts
2349
+ var SWAGGER_UI_HTML = (jsonUrl, title) => `<!DOCTYPE html>
2350
+ <html lang="en">
2351
+ <head>
2352
+ <meta charset="UTF-8">
2353
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
2354
+ <title>${title} - Swagger UI</title>
2355
+ <link rel="stylesheet" type="text/css" href="https://unpkg.com/swagger-ui-dist@5.9.0/swagger-ui.css" />
2356
+ <style>
2357
+ html {
2358
+ box-sizing: border-box;
2359
+ overflow: -moz-scrollbars-vertical;
2360
+ overflow-y: scroll;
2361
+ }
2362
+ *, *:before, *:after {
2363
+ box-sizing: inherit;
2364
+ }
2365
+ body {
2366
+ margin:0;
2367
+ background: #fafafa;
2368
+ }
2369
+ </style>
2370
+ </head>
2371
+ <body>
2372
+ <div id="swagger-ui"></div>
2373
+ <script src="https://unpkg.com/swagger-ui-dist@5.9.0/swagger-ui-bundle.js"></script>
2374
+ <script src="https://unpkg.com/swagger-ui-dist@5.9.0/swagger-ui-standalone-preset.js"></script>
2375
+ <script>
2376
+ window.onload = function() {
2377
+ const ui = SwaggerUIBundle({
2378
+ url: "${jsonUrl}",
2379
+ dom_id: '#swagger-ui',
2380
+ deepLinking: true,
2381
+ presets: [
2382
+ SwaggerUIBundle.presets.apis,
2383
+ SwaggerUIStandalonePreset
2384
+ ],
2385
+ plugins: [
2386
+ SwaggerUIBundle.plugins.DownloadUrl
2387
+ ],
2388
+ layout: "StandaloneLayout"
2389
+ });
2390
+ };
2391
+ </script>
2392
+ </body>
2393
+ </html>`;
2394
+ function createSwaggerUIMiddleware(extension, options = {}) {
2395
+ const uiPath = options.uiPath || "/swagger";
2396
+ const jsonPath = options.jsonPath || "/swagger.json";
2397
+ const title = options.title || "API Documentation";
2398
+ return async (ctx, next) => {
2399
+ const url = new URL(ctx.request.url);
2400
+ const pathname = url.pathname;
2401
+ if (pathname === uiPath || pathname === `${uiPath}/`) {
2402
+ const html = SWAGGER_UI_HTML(jsonPath, title);
2403
+ return new Response(html, {
2404
+ headers: {
2405
+ "Content-Type": "text/html; charset=utf-8"
2406
+ }
2407
+ });
2408
+ }
2409
+ if (pathname === jsonPath) {
2410
+ const json = extension.generateJSON();
2411
+ return new Response(json, {
2412
+ headers: {
2413
+ "Content-Type": "application/json; charset=utf-8"
2414
+ }
2415
+ });
2416
+ }
2417
+ return await next();
2418
+ };
2419
+ }
2420
+
2421
+ // src/swagger/swagger-module.ts
2422
+ class SwaggerModule {
2423
+ static forRoot(options) {
2424
+ const extensions = [];
2425
+ const middlewares = [];
2426
+ const swaggerExtension = new SwaggerExtension({
2427
+ info: options.info,
2428
+ servers: options.servers,
2429
+ basePath: options.basePath,
2430
+ tags: options.tags
2431
+ });
2432
+ extensions.push(swaggerExtension);
2433
+ if (options.enableUI !== false) {
2434
+ const uiMiddleware = createSwaggerUIMiddleware(swaggerExtension, {
2435
+ uiPath: options.uiPath || "/swagger",
2436
+ jsonPath: options.jsonPath || "/swagger.json",
2437
+ title: options.uiTitle || options.info.title || "API Documentation"
2438
+ });
2439
+ middlewares.push(uiMiddleware);
2440
+ }
2441
+ const existingMetadata = Reflect.getMetadata(MODULE_METADATA_KEY, SwaggerModule) || {};
2442
+ const metadata = {
2443
+ ...existingMetadata,
2444
+ extensions,
2445
+ middlewares
2446
+ };
2447
+ Reflect.defineMetadata(MODULE_METADATA_KEY, metadata, SwaggerModule);
2448
+ return SwaggerModule;
2449
+ }
2450
+ }
2451
+ SwaggerModule = __legacyDecorateClassTS([
2452
+ Module({
2453
+ extensions: [],
2454
+ middlewares: []
2455
+ })
2456
+ ], SwaggerModule);
2457
+ // src/security/context.ts
2458
+ import { AsyncLocalStorage } from "async_hooks";
2459
+
2460
+ class SecurityContextImpl {
2461
+ _authentication = null;
2462
+ get authentication() {
2463
+ return this._authentication;
2464
+ }
2465
+ setAuthentication(authentication) {
2466
+ this._authentication = authentication;
2467
+ }
2468
+ isAuthenticated() {
2469
+ return this._authentication?.authenticated ?? false;
2470
+ }
2471
+ getPrincipal() {
2472
+ return this._authentication?.principal ?? null;
2473
+ }
2474
+ getAuthorities() {
2475
+ return this._authentication?.authorities ?? [];
2476
+ }
2477
+ clear() {
2478
+ this._authentication = null;
2479
+ }
2480
+ }
2481
+
2482
+ class SecurityContextHolder {
2483
+ static storage = new AsyncLocalStorage;
2484
+ static getContext() {
2485
+ let context = this.storage.getStore();
2486
+ if (!context) {
2487
+ context = new SecurityContextImpl;
2488
+ this.storage.enterWith(context);
2489
+ }
2490
+ return context;
2491
+ }
2492
+ static runWithContext(callback) {
2493
+ const existing = this.storage.getStore() ?? new SecurityContextImpl;
2494
+ return this.storage.run(existing, callback);
2495
+ }
2496
+ static clearContext() {
2497
+ const context = this.storage.getStore();
2498
+ if (context) {
2499
+ context.clear();
2500
+ }
2501
+ }
2502
+ }
2503
+ // src/security/authentication-manager.ts
2504
+ class AuthenticationManager {
2505
+ providers = [];
2506
+ registerProvider(provider) {
2507
+ this.providers.push(provider);
2508
+ }
2509
+ async authenticate(request) {
2510
+ const type = request.type || "default";
2511
+ const provider = this.providers.find((p) => p.supports(type));
2512
+ if (!provider) {
2513
+ throw new Error(`No authentication provider found for type: ${type}`);
2514
+ }
2515
+ return await provider.authenticate(request);
2516
+ }
2517
+ getProviders() {
2518
+ return [...this.providers];
2519
+ }
2520
+ }
2521
+ // src/security/access-decision-manager.ts
2522
+ class RoleBasedAccessDecisionManager {
2523
+ decide(authentication, requiredAuthorities) {
2524
+ if (requiredAuthorities.length === 0) {
2525
+ return true;
2526
+ }
2527
+ if (!authentication.authenticated) {
2528
+ return false;
2529
+ }
2530
+ const userAuthorities = authentication.authorities || [];
2531
+ return requiredAuthorities.some((required) => userAuthorities.includes(required));
2532
+ }
2533
+ }
2534
+ // src/auth/decorators.ts
2535
+ import"reflect-metadata";
2536
+ var AUTH_METADATA_KEY = Symbol("@dangao/bun-server:auth");
2537
+ function Auth(config = {}) {
2538
+ return (target, propertyKey) => {
2539
+ const metadata = Reflect.getMetadata(AUTH_METADATA_KEY, target) || {};
2540
+ metadata[propertyKey] = {
2541
+ required: config.required !== false,
2542
+ roles: config.roles || [],
2543
+ allowAnonymous: config.allowAnonymous || false
2544
+ };
2545
+ Reflect.defineMetadata(AUTH_METADATA_KEY, metadata, target);
2546
+ };
2547
+ }
2548
+ function getAuthMetadata(target, propertyKey) {
2549
+ const metadata = Reflect.getMetadata(AUTH_METADATA_KEY, target);
2550
+ return metadata?.[propertyKey];
2551
+ }
2552
+ function requiresAuth(target, propertyKey) {
2553
+ const config = getAuthMetadata(target, propertyKey);
2554
+ if (!config) {
2555
+ return false;
2556
+ }
2557
+ return config.required !== false;
2558
+ }
2559
+ function checkRoles(target, propertyKey, userRoles = []) {
2560
+ const config = getAuthMetadata(target, propertyKey);
2561
+ if (!config?.roles || config.roles.length === 0) {
2562
+ return true;
2563
+ }
2564
+ return config.roles.some((role) => userRoles.includes(role));
2565
+ }
2566
+
2567
+ // src/security/filter.ts
2568
+ function createSecurityFilter(config) {
2569
+ const {
2570
+ authenticationManager,
2571
+ accessDecisionManager = new RoleBasedAccessDecisionManager,
2572
+ excludePaths = [],
2573
+ defaultAuthRequired = true,
2574
+ extractToken
2575
+ } = config;
2576
+ return async (ctx, next) => {
2577
+ return SecurityContextHolder.runWithContext(async () => {
2578
+ const path = ctx.path || ctx.request.url.split("?")[0].replace(/^https?:\/\/[^/]+/, "");
2579
+ if (excludePaths.some((exclude) => path.startsWith(exclude))) {
2580
+ return await next();
2581
+ }
2582
+ const securityContext = SecurityContextHolder.getContext();
2583
+ try {
2584
+ const token = extractToken ? extractToken(ctx) : extractTokenFromHeader(ctx);
2585
+ if (token) {
2586
+ const request = {
2587
+ principal: "",
2588
+ credentials: token,
2589
+ type: "jwt"
2590
+ };
2591
+ const authentication = await authenticationManager.authenticate(request);
2592
+ if (authentication) {
2593
+ securityContext.setAuthentication(authentication);
2594
+ }
2595
+ }
2596
+ const handler = ctx.routeHandler;
2597
+ if (handler) {
2598
+ const controllerClass = handler.controller;
2599
+ const controllerTarget = controllerClass && controllerClass.prototype || controllerClass;
2600
+ const method = handler.method;
2601
+ if (requiresAuth(controllerTarget, method)) {
2602
+ const authentication = securityContext.authentication;
2603
+ if (!authentication || !authentication.authenticated) {
2604
+ throw new UnauthorizedException("Authentication required");
2605
+ }
2606
+ const requiredRoles = getRequiredRoles(controllerTarget, method);
2607
+ if (requiredRoles.length > 0) {
2608
+ const hasAccess = accessDecisionManager.decide(authentication, requiredRoles);
2609
+ if (!hasAccess) {
2610
+ const userRoles = authentication.authorities || [];
2611
+ throw new ForbiddenException(`Insufficient permissions. Required roles: ${requiredRoles.join(", ")}, User roles: ${userRoles.join(", ")}`);
2612
+ }
2613
+ }
2614
+ }
2615
+ } else if (defaultAuthRequired && !securityContext.isAuthenticated()) {
2616
+ throw new UnauthorizedException("Authentication required");
2617
+ }
2618
+ ctx.security = securityContext;
2619
+ ctx.auth = {
2620
+ isAuthenticated: securityContext.isAuthenticated(),
2621
+ user: securityContext.getPrincipal(),
2622
+ payload: securityContext.authentication?.details
2623
+ };
2624
+ return await next();
2625
+ } finally {
2626
+ SecurityContextHolder.clearContext();
2627
+ }
2628
+ });
2629
+ };
2630
+ }
2631
+ function extractTokenFromHeader(ctx) {
2632
+ const authHeader = ctx.getHeader("authorization");
2633
+ if (!authHeader) {
2634
+ return null;
2635
+ }
2636
+ const parts = authHeader.split(" ");
2637
+ if (parts.length !== 2 || parts[0] !== "Bearer") {
2638
+ return null;
2639
+ }
2640
+ return parts[1];
2641
+ }
2642
+ function getRequiredRoles(target, propertyKey) {
2643
+ const metadata = getAuthMetadata(target, propertyKey);
2644
+ return metadata?.roles || [];
2645
+ }
2646
+ // src/security/providers/jwt-provider.ts
2647
+ class JwtAuthenticationProvider {
2648
+ jwtUtil;
2649
+ supportedTypes = ["jwt", "bearer"];
2650
+ constructor(jwtUtil) {
2651
+ this.jwtUtil = jwtUtil;
2652
+ }
2653
+ supports(type) {
2654
+ return this.supportedTypes.includes(type.toLowerCase());
2655
+ }
2656
+ async authenticate(request) {
2657
+ const token = request.credentials;
2658
+ if (!token) {
2659
+ return null;
2660
+ }
2661
+ const payload = this.jwtUtil.verify(token);
2662
+ if (!payload) {
2663
+ return null;
2664
+ }
2665
+ const principal = {
2666
+ id: payload.sub,
2667
+ username: payload.username || payload.sub,
2668
+ roles: payload.roles || []
2669
+ };
2670
+ const authorities = principal.roles || [];
2671
+ return {
2672
+ authenticated: true,
2673
+ principal,
2674
+ credentials: { type: "jwt", data: token },
2675
+ authorities,
2676
+ details: payload
2677
+ };
2678
+ }
2679
+ }
2680
+ // src/security/providers/oauth2-provider.ts
2681
+ class OAuth2AuthenticationProvider {
2682
+ oauth2Service;
2683
+ supportedTypes = ["oauth2", "authorization_code"];
2684
+ constructor(oauth2Service) {
2685
+ this.oauth2Service = oauth2Service;
2686
+ }
2687
+ supports(type) {
2688
+ return this.supportedTypes.includes(type.toLowerCase());
2689
+ }
2690
+ async authenticate(request) {
2691
+ const credentials = request.credentials;
2692
+ if (!credentials || credentials.grantType !== "authorization_code") {
2693
+ return null;
2694
+ }
2695
+ const tokenResponse = await this.oauth2Service.exchangeCodeForToken(credentials);
2696
+ if (!tokenResponse) {
2697
+ return null;
2698
+ }
2699
+ const userInfo = {
2700
+ id: "user-1",
2701
+ username: "user",
2702
+ roles: ["user"]
2703
+ };
2704
+ const principal = {
2705
+ id: userInfo.id,
2706
+ username: userInfo.username,
2707
+ roles: userInfo.roles
2708
+ };
2709
+ return {
2710
+ authenticated: true,
2711
+ principal,
2712
+ credentials: {
2713
+ type: "oauth2",
2714
+ data: tokenResponse
2715
+ },
2716
+ authorities: principal.roles || [],
2717
+ details: tokenResponse
2718
+ };
2719
+ }
2720
+ }
2721
+ // src/auth/jwt.ts
2722
+ class JWTUtil {
2723
+ config;
2724
+ constructor(config) {
2725
+ this.config = {
2726
+ secret: config.secret,
2727
+ accessTokenExpiresIn: config.accessTokenExpiresIn ?? 3600,
2728
+ refreshTokenExpiresIn: config.refreshTokenExpiresIn ?? 86400 * 7,
2729
+ algorithm: config.algorithm ?? "HS256"
2730
+ };
2731
+ }
2732
+ generateAccessToken(payload) {
2733
+ const now = Math.floor(Date.now() / 1000);
2734
+ const tokenPayload = {
2735
+ ...payload,
2736
+ sub: payload.sub,
2737
+ iat: now,
2738
+ exp: now + this.config.accessTokenExpiresIn
2739
+ };
2740
+ return this.sign(tokenPayload);
2741
+ }
2742
+ generateRefreshToken(payload) {
2743
+ const now = Math.floor(Date.now() / 1000);
2744
+ const tokenPayload = {
2745
+ ...payload,
2746
+ sub: payload.sub,
2747
+ iat: now,
2748
+ exp: now + this.config.refreshTokenExpiresIn
2749
+ };
2750
+ return this.sign(tokenPayload);
2751
+ }
2752
+ verify(token) {
2753
+ try {
2754
+ const parts = token.split(".");
2755
+ if (parts.length !== 3) {
2756
+ return null;
2757
+ }
2758
+ const payload = JSON.parse(Buffer.from(parts[1], "base64url").toString("utf-8"));
2759
+ const signature = this.signature(parts[0] + "." + parts[1]);
2760
+ if (signature !== parts[2]) {
2761
+ return null;
2762
+ }
2763
+ if (payload.exp && payload.exp < Math.floor(Date.now() / 1000)) {
2764
+ return null;
2765
+ }
2766
+ return payload;
2767
+ } catch {
2768
+ return null;
2769
+ }
2770
+ }
2771
+ sign(payload) {
2772
+ const header = {
2773
+ alg: this.config.algorithm,
2774
+ typ: "JWT"
2775
+ };
2776
+ const encodedHeader = this.base64UrlEncode(JSON.stringify(header));
2777
+ const encodedPayload = this.base64UrlEncode(JSON.stringify(payload));
2778
+ const signature = this.signature(encodedHeader + "." + encodedPayload);
2779
+ return `${encodedHeader}.${encodedPayload}.${signature}`;
2780
+ }
2781
+ signature(data) {
2782
+ const encoder = new TextEncoder;
2783
+ const keyData = encoder.encode(this.config.secret);
2784
+ const messageData = encoder.encode(data);
2785
+ const hash = this.hmacSha256(keyData, messageData);
2786
+ return this.base64UrlEncode(Buffer.from(hash).toString("base64"));
2787
+ }
2788
+ hmacSha256(key, data) {
2789
+ const blockSize = 64;
2790
+ let keyBuffer;
2791
+ if (key.length > blockSize) {
2792
+ const hasher = new Bun.CryptoHasher("sha256");
2793
+ hasher.update(key);
2794
+ keyBuffer = new Uint8Array(hasher.digest());
2795
+ } else {
2796
+ keyBuffer = new Uint8Array(blockSize);
2797
+ keyBuffer.set(key);
2798
+ }
2799
+ const oKeyPad = new Uint8Array(blockSize);
2800
+ const iKeyPad = new Uint8Array(blockSize);
2801
+ for (let i = 0;i < blockSize; i++) {
2802
+ oKeyPad[i] = keyBuffer[i] ^ 92;
2803
+ iKeyPad[i] = keyBuffer[i] ^ 54;
2804
+ }
2805
+ const innerData = new Uint8Array(iKeyPad.length + data.length);
2806
+ innerData.set(iKeyPad);
2807
+ innerData.set(data, iKeyPad.length);
2808
+ const innerHasher = new Bun.CryptoHasher("sha256");
2809
+ innerHasher.update(innerData);
2810
+ const innerHash = new Uint8Array(innerHasher.digest());
2811
+ const outerData = new Uint8Array(oKeyPad.length + innerHash.length);
2812
+ outerData.set(oKeyPad);
2813
+ outerData.set(innerHash, oKeyPad.length);
2814
+ const outerHasher = new Bun.CryptoHasher("sha256");
2815
+ outerHasher.update(outerData);
2816
+ return new Uint8Array(outerHasher.digest());
2817
+ }
2818
+ base64UrlEncode(str) {
2819
+ return Buffer.from(str).toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
2820
+ }
2821
+ }
2822
+
2823
+ // src/auth/oauth2.ts
2824
+ class OAuth2Service {
2825
+ jwtUtil;
2826
+ clients;
2827
+ codes;
2828
+ codeConfig;
2829
+ userProvider;
2830
+ constructor(jwtUtil, clients = [], codeConfig = {}, userProvider) {
2831
+ this.jwtUtil = jwtUtil;
2832
+ this.clients = new Map;
2833
+ this.codes = new Map;
2834
+ this.codeConfig = {
2835
+ expiresIn: codeConfig.expiresIn ?? 600,
2836
+ length: codeConfig.length ?? 32
2837
+ };
2838
+ for (const client of clients) {
2839
+ this.clients.set(client.clientId, client);
2840
+ }
2841
+ this.userProvider = userProvider;
2842
+ setInterval(() => this.cleanupExpiredCodes(), 60000);
2843
+ }
2844
+ registerClient(client) {
2845
+ this.clients.set(client.clientId, client);
2846
+ }
2847
+ validateAuthorizationRequest(request) {
2848
+ const client = this.clients.get(request.clientId);
2849
+ if (!client) {
2850
+ return { valid: false, error: "invalid_client" };
2851
+ }
2852
+ if (request.responseType !== "code") {
2853
+ return { valid: false, error: "unsupported_response_type" };
2854
+ }
2855
+ if (!client.redirectUris.includes(request.redirectUri)) {
2856
+ return { valid: false, error: "invalid_redirect_uri" };
2857
+ }
2858
+ return { valid: true };
2859
+ }
2860
+ generateAuthorizationCode(clientId, redirectUri, userId, scope) {
2861
+ const code = this.generateRandomString(this.codeConfig.length);
2862
+ const expiresAt = Date.now() + this.codeConfig.expiresIn * 1000;
2863
+ this.codes.set(code, {
2864
+ code,
2865
+ clientId,
2866
+ redirectUri,
2867
+ userId,
2868
+ scope,
2869
+ expiresAt
2870
+ });
2871
+ return code;
2872
+ }
2873
+ async exchangeCodeForToken(request) {
2874
+ if (request.grantType !== "authorization_code") {
2875
+ return null;
2876
+ }
2877
+ const codeData = this.codes.get(request.code);
2878
+ if (!codeData) {
2879
+ return null;
2880
+ }
2881
+ if (codeData.expiresAt < Date.now()) {
2882
+ this.codes.delete(request.code);
2883
+ return null;
2884
+ }
2885
+ const client = this.clients.get(request.clientId);
2886
+ if (!client || client.clientSecret !== request.clientSecret) {
2887
+ return null;
2888
+ }
2889
+ if (codeData.redirectUri !== request.redirectUri) {
2890
+ return null;
2891
+ }
2892
+ this.codes.delete(request.code);
2893
+ let userInfo = null;
2894
+ if (this.userProvider) {
2895
+ userInfo = await this.userProvider(codeData.userId);
2896
+ }
2897
+ if (!userInfo) {
2898
+ userInfo = {
2899
+ id: codeData.userId,
2900
+ username: codeData.userId
2901
+ };
2902
+ }
2903
+ const payload = {
2904
+ sub: userInfo.id,
2905
+ username: userInfo.username,
2906
+ roles: userInfo.roles
2907
+ };
2908
+ const accessToken = this.jwtUtil.generateAccessToken(payload);
2909
+ const refreshToken = this.jwtUtil.generateRefreshToken(payload);
2910
+ return {
2911
+ accessToken,
2912
+ tokenType: "Bearer",
2913
+ expiresIn: this.jwtUtil["config"].accessTokenExpiresIn,
2914
+ refreshToken,
2915
+ scope: codeData.scope
2916
+ };
2917
+ }
2918
+ async refreshToken(refreshToken) {
2919
+ const payload = this.jwtUtil.verify(refreshToken);
2920
+ if (!payload || !payload.sub) {
2921
+ return null;
2922
+ }
2923
+ let userInfo = null;
2924
+ if (this.userProvider) {
2925
+ userInfo = await this.userProvider(payload.sub);
2926
+ }
2927
+ if (!userInfo) {
2928
+ userInfo = {
2929
+ id: payload.sub,
2930
+ username: payload.username || payload.sub,
2931
+ roles: payload.roles
2932
+ };
2933
+ }
2934
+ const newPayload = {
2935
+ sub: userInfo.id,
2936
+ username: userInfo.username,
2937
+ roles: userInfo.roles
2938
+ };
2939
+ const accessToken = this.jwtUtil.generateAccessToken(newPayload);
2940
+ const newRefreshToken = this.jwtUtil.generateRefreshToken(newPayload);
2941
+ return {
2942
+ accessToken,
2943
+ tokenType: "Bearer",
2944
+ expiresIn: this.jwtUtil["config"].accessTokenExpiresIn,
2945
+ refreshToken: newRefreshToken
2946
+ };
2947
+ }
2948
+ generateRandomString(length) {
2949
+ const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
2950
+ let result = "";
2951
+ for (let i = 0;i < length; i++) {
2952
+ result += chars.charAt(Math.floor(Math.random() * chars.length));
2953
+ }
2954
+ return result;
2955
+ }
2956
+ cleanupExpiredCodes() {
2957
+ const now = Date.now();
2958
+ for (const [code, data] of this.codes.entries()) {
2959
+ if (data.expiresAt < now) {
2960
+ this.codes.delete(code);
2961
+ }
2962
+ }
2963
+ }
2964
+ }
2965
+ // src/auth/controller.ts
2966
+ var OAUTH2_SERVICE_TOKEN = Symbol("OAUTH2_SERVICE");
2967
+ var JWT_UTIL_TOKEN = Symbol("JWT_UTIL");
2968
+
2969
+ class OAuth2Controller {
2970
+ oauth2Service;
2971
+ constructor(oauth2Service) {
2972
+ this.oauth2Service = oauth2Service;
2973
+ }
2974
+ authorize(clientId, redirectUri, state, scope) {
2975
+ const query = {
2976
+ client_id: clientId,
2977
+ redirect_uri: redirectUri,
2978
+ ...state && { state },
2979
+ ...scope && { scope }
2980
+ };
2981
+ const request = {
2982
+ clientId: query.client_id || "",
2983
+ redirectUri: query.redirect_uri || "",
2984
+ responseType: "code",
2985
+ scope: query.scope,
2986
+ state: query.state
2987
+ };
2988
+ const validation = this.oauth2Service.validateAuthorizationRequest(request);
2989
+ if (!validation.valid) {
2990
+ throw new Error(`Invalid authorization request: ${validation.error}`);
2991
+ }
2992
+ const userId = "user-1";
2993
+ const code = this.oauth2Service.generateAuthorizationCode(request.clientId, request.redirectUri, userId, request.scope);
2994
+ const redirectUrl = new URL(request.redirectUri);
2995
+ redirectUrl.searchParams.set("code", code);
2996
+ if (request.state) {
2997
+ redirectUrl.searchParams.set("state", request.state);
2998
+ }
2999
+ return ResponseBuilder.redirect(redirectUrl.toString());
3000
+ }
3001
+ async token(body) {
3002
+ const request = {
3003
+ code: body.code || "",
3004
+ clientId: body.client_id || "",
3005
+ clientSecret: body.client_secret || "",
3006
+ redirectUri: body.redirect_uri || "",
3007
+ grantType: body.grant_type || "authorization_code",
3008
+ refreshToken: body.refresh_token
3009
+ };
3010
+ if (request.grantType === "authorization_code") {
3011
+ const tokenResponse = await this.oauth2Service.exchangeCodeForToken(request);
3012
+ if (!tokenResponse) {
3013
+ return {
3014
+ error: "invalid_grant",
3015
+ error_description: "Invalid authorization code"
3016
+ };
3017
+ }
3018
+ return tokenResponse;
3019
+ }
3020
+ if (request.grantType === "refresh_token" && request.refreshToken) {
3021
+ const tokenResponse = await this.oauth2Service.refreshToken(request.refreshToken);
3022
+ if (!tokenResponse) {
3023
+ return {
3024
+ error: "invalid_grant",
3025
+ error_description: "Invalid refresh token"
3026
+ };
3027
+ }
3028
+ return tokenResponse;
3029
+ }
3030
+ return {
3031
+ error: "unsupported_grant_type",
3032
+ error_description: "Unsupported grant type"
3033
+ };
3034
+ }
3035
+ userinfo() {
3036
+ return {
3037
+ sub: "user-1",
3038
+ username: "alice",
3039
+ roles: ["user"]
3040
+ };
3041
+ }
3042
+ }
3043
+ __legacyDecorateClassTS([
3044
+ GET("/authorize"),
3045
+ __legacyDecorateParamTS(0, Query("client_id")),
3046
+ __legacyDecorateParamTS(1, Query("redirect_uri")),
3047
+ __legacyDecorateParamTS(2, Query("state")),
3048
+ __legacyDecorateParamTS(3, Query("scope")),
3049
+ __legacyMetadataTS("design:type", Function),
3050
+ __legacyMetadataTS("design:paramtypes", [
3051
+ String,
3052
+ String,
3053
+ String,
3054
+ String
3055
+ ]),
3056
+ __legacyMetadataTS("design:returntype", undefined)
3057
+ ], OAuth2Controller.prototype, "authorize", null);
3058
+ __legacyDecorateClassTS([
3059
+ POST("/token"),
3060
+ __legacyDecorateParamTS(0, Body()),
3061
+ __legacyMetadataTS("design:type", Function),
3062
+ __legacyMetadataTS("design:paramtypes", [
3063
+ typeof Record === "undefined" ? Object : Record
3064
+ ]),
3065
+ __legacyMetadataTS("design:returntype", typeof Promise === "undefined" ? Object : Promise)
3066
+ ], OAuth2Controller.prototype, "token", null);
3067
+ __legacyDecorateClassTS([
3068
+ GET("/userinfo"),
3069
+ Auth(),
3070
+ __legacyMetadataTS("design:type", Function),
3071
+ __legacyMetadataTS("design:paramtypes", []),
3072
+ __legacyMetadataTS("design:returntype", undefined)
3073
+ ], OAuth2Controller.prototype, "userinfo", null);
3074
+ OAuth2Controller = __legacyDecorateClassTS([
3075
+ Controller("/oauth2"),
3076
+ Injectable(),
3077
+ __legacyDecorateParamTS(0, Inject(OAUTH2_SERVICE_TOKEN)),
3078
+ __legacyMetadataTS("design:paramtypes", [
3079
+ typeof OAuth2Service === "undefined" ? Object : OAuth2Service
3080
+ ])
3081
+ ], OAuth2Controller);
3082
+
3083
+ // src/security/security-module.ts
3084
+ class SecurityModule {
3085
+ static forRoot(config) {
3086
+ const jwtUtil = new JWTUtil(config.jwt);
3087
+ const userProvider = config.userProvider ? async (userId) => config.userProvider.findById(userId) : undefined;
3088
+ const oauth2Service = new OAuth2Service(jwtUtil, config.oauth2Clients || [], {}, userProvider);
3089
+ const authenticationManager = new AuthenticationManager;
3090
+ authenticationManager.registerProvider(new JwtAuthenticationProvider(jwtUtil));
3091
+ authenticationManager.registerProvider(new OAuth2AuthenticationProvider(oauth2Service));
3092
+ const securityFilter = createSecurityFilter({
3093
+ authenticationManager,
3094
+ excludePaths: [
3095
+ ...config.excludePaths || [],
3096
+ ...config.enableOAuth2Endpoints !== false ? [config.oauth2Prefix || "/oauth2"] : []
3097
+ ],
3098
+ defaultAuthRequired: config.defaultAuthRequired ?? false
3099
+ });
3100
+ const controllers = [];
3101
+ const providers = [];
3102
+ const middlewares = [];
3103
+ if (config.enableOAuth2Endpoints !== false) {
3104
+ controllers.push(OAuth2Controller);
3105
+ }
3106
+ providers.push({
3107
+ provide: JWT_UTIL_TOKEN,
3108
+ useValue: jwtUtil
3109
+ }, {
3110
+ provide: OAUTH2_SERVICE_TOKEN,
3111
+ useValue: oauth2Service
3112
+ }, {
3113
+ provide: AuthenticationManager,
3114
+ useValue: authenticationManager
3115
+ });
3116
+ middlewares.push(securityFilter);
3117
+ const existingMetadata = Reflect.getMetadata(MODULE_METADATA_KEY, SecurityModule) || {};
3118
+ const metadata = {
3119
+ ...existingMetadata,
3120
+ controllers: [...existingMetadata.controllers || [], ...controllers],
3121
+ providers: [...existingMetadata.providers || [], ...providers],
3122
+ middlewares: [...existingMetadata.middlewares || [], ...middlewares],
3123
+ exports: [
3124
+ ...existingMetadata.exports || [],
3125
+ JWT_UTIL_TOKEN,
3126
+ OAUTH2_SERVICE_TOKEN,
3127
+ AuthenticationManager
3128
+ ]
3129
+ };
3130
+ Reflect.defineMetadata(MODULE_METADATA_KEY, metadata, SecurityModule);
3131
+ return SecurityModule;
3132
+ }
3133
+ }
3134
+ SecurityModule = __legacyDecorateClassTS([
3135
+ Module({
3136
+ controllers: [],
3137
+ providers: [],
3138
+ middlewares: []
3139
+ })
3140
+ ], SecurityModule);
3141
+ // src/config/service.ts
3142
+ class ConfigService {
3143
+ config;
3144
+ namespace;
3145
+ constructor(config, namespace) {
3146
+ this.config = config;
3147
+ this.namespace = namespace;
3148
+ }
3149
+ getAll() {
3150
+ return this.config;
3151
+ }
3152
+ get(key, defaultValue) {
3153
+ const namespacedKey = this.applyNamespace(key);
3154
+ const value = this.getValueByPath(this.config, namespacedKey);
3155
+ if (value === undefined) {
3156
+ return defaultValue;
3157
+ }
3158
+ return value;
3159
+ }
3160
+ getRequired(key) {
3161
+ const value = this.get(key);
3162
+ if (value === undefined) {
3163
+ throw new Error(`Config value required for key: ${key}`);
3164
+ }
3165
+ return value;
3166
+ }
3167
+ withNamespace(namespace) {
3168
+ return new ConfigService(this.config, namespace);
3169
+ }
3170
+ applyNamespace(key) {
3171
+ if (!this.namespace) {
3172
+ return key;
3173
+ }
3174
+ if (!key) {
3175
+ return this.namespace;
3176
+ }
3177
+ if (key.startsWith(this.namespace + ".")) {
3178
+ return key;
3179
+ }
3180
+ return `${this.namespace}.${key}`;
3181
+ }
3182
+ getValueByPath(obj, path) {
3183
+ if (!path) {
3184
+ return obj;
3185
+ }
3186
+ const segments = path.split(".");
3187
+ let current = obj;
3188
+ for (const segment of segments) {
3189
+ if (current === undefined || current === null || typeof current !== "object") {
3190
+ return;
3191
+ }
3192
+ current = current[segment];
3193
+ }
3194
+ return current;
3195
+ }
3196
+ }
3197
+
3198
+ // src/config/types.ts
3199
+ var CONFIG_SERVICE_TOKEN = Symbol("@dangao/bun-server:config:service");
3200
+
3201
+ // src/config/config-module.ts
3202
+ class ConfigModule {
3203
+ static forRoot(options = {}) {
3204
+ const providers2 = [];
3205
+ const env = ConfigModule.snapshotEnv();
3206
+ const defaultConfig = options.defaultConfig ?? {};
3207
+ const loadedConfig = options.load ? options.load(env) : {};
3208
+ const mergedConfig = {
3209
+ ...defaultConfig,
3210
+ ...loadedConfig
3211
+ };
3212
+ const service = new ConfigService(mergedConfig, options.namespace);
3213
+ if (options.validate) {
3214
+ options.validate(mergedConfig);
3215
+ }
3216
+ providers2.push({
3217
+ provide: CONFIG_SERVICE_TOKEN,
3218
+ useValue: service
3219
+ }, ConfigService);
3220
+ const existingMetadata = Reflect.getMetadata(MODULE_METADATA_KEY, ConfigModule) || {};
3221
+ const metadata = {
3222
+ ...existingMetadata,
3223
+ providers: [...existingMetadata.providers || [], ...providers2],
3224
+ exports: [
3225
+ ...existingMetadata.exports || [],
3226
+ CONFIG_SERVICE_TOKEN,
3227
+ ConfigService
3228
+ ]
3229
+ };
3230
+ Reflect.defineMetadata(MODULE_METADATA_KEY, metadata, ConfigModule);
3231
+ return ConfigModule;
3232
+ }
3233
+ static snapshotEnv() {
3234
+ const env = {};
3235
+ for (const [key, value] of Object.entries(process.env)) {
3236
+ env[key] = value;
3237
+ }
3238
+ return env;
3239
+ }
3240
+ }
3241
+ ConfigModule = __legacyDecorateClassTS([
3242
+ Module({
3243
+ providers: []
3244
+ })
3245
+ ], ConfigModule);
3246
+ // src/health/types.ts
3247
+ var HEALTH_INDICATORS_TOKEN = Symbol("@dangao/bun-server:health:indicators");
3248
+ var HEALTH_OPTIONS_TOKEN = Symbol("@dangao/bun-server:health:options");
3249
+
3250
+ // src/health/controller.ts
3251
+ class HealthController {
3252
+ indicators;
3253
+ options;
3254
+ constructor(indicators = [], options) {
3255
+ this.indicators = indicators;
3256
+ this.options = options;
3257
+ }
3258
+ async health() {
3259
+ return await this.checkIndicators();
3260
+ }
3261
+ async ready() {
3262
+ return await this.checkIndicators();
3263
+ }
3264
+ async checkIndicators() {
3265
+ const details = {};
3266
+ for (const indicator of this.indicators || []) {
3267
+ try {
3268
+ const result = await indicator.check();
3269
+ details[indicator.name] = result;
3270
+ } catch (error) {
3271
+ details[indicator.name] = {
3272
+ status: "down",
3273
+ details: {
3274
+ error: error.message
3275
+ }
3276
+ };
3277
+ }
3278
+ }
3279
+ const allUp = Object.keys(details).length === 0 || Object.values(details).every((result) => result.status === "up");
3280
+ return {
3281
+ status: allUp ? "up" : "down",
3282
+ details
3283
+ };
3284
+ }
3285
+ }
3286
+ __legacyDecorateClassTS([
3287
+ GET("/health"),
3288
+ __legacyMetadataTS("design:type", Function),
3289
+ __legacyMetadataTS("design:paramtypes", []),
3290
+ __legacyMetadataTS("design:returntype", typeof Promise === "undefined" ? Object : Promise)
3291
+ ], HealthController.prototype, "health", null);
3292
+ __legacyDecorateClassTS([
3293
+ GET("/ready"),
3294
+ __legacyMetadataTS("design:type", Function),
3295
+ __legacyMetadataTS("design:paramtypes", []),
3296
+ __legacyMetadataTS("design:returntype", typeof Promise === "undefined" ? Object : Promise)
3297
+ ], HealthController.prototype, "ready", null);
3298
+ HealthController = __legacyDecorateClassTS([
3299
+ Controller("/"),
3300
+ __legacyDecorateParamTS(0, Inject(HEALTH_INDICATORS_TOKEN)),
3301
+ __legacyDecorateParamTS(1, Inject(HEALTH_OPTIONS_TOKEN)),
3302
+ __legacyMetadataTS("design:paramtypes", [
3303
+ Array,
3304
+ typeof HealthModuleOptions === "undefined" ? Object : HealthModuleOptions
3305
+ ])
3306
+ ], HealthController);
3307
+
3308
+ // src/health/health-module.ts
3309
+ class HealthModule {
3310
+ static forRoot(options = {}) {
3311
+ const providers2 = [];
3312
+ const indicators = options.indicators ?? [];
3313
+ providers2.push({
3314
+ provide: HEALTH_INDICATORS_TOKEN,
3315
+ useValue: indicators
3316
+ }, {
3317
+ provide: HEALTH_OPTIONS_TOKEN,
3318
+ useValue: options
3319
+ });
3320
+ const existingMetadata = Reflect.getMetadata(MODULE_METADATA_KEY, HealthModule) || {};
3321
+ const metadata = {
3322
+ ...existingMetadata,
3323
+ controllers: [...existingMetadata.controllers || [], HealthController],
3324
+ providers: [...existingMetadata.providers || [], ...providers2],
3325
+ exports: [
3326
+ ...existingMetadata.exports || [],
3327
+ HEALTH_INDICATORS_TOKEN,
3328
+ HEALTH_OPTIONS_TOKEN
3329
+ ]
3330
+ };
3331
+ Reflect.defineMetadata(MODULE_METADATA_KEY, metadata, HealthModule);
3332
+ return HealthModule;
3333
+ }
3334
+ }
3335
+ HealthModule = __legacyDecorateClassTS([
3336
+ Module({
3337
+ controllers: [HealthController],
3338
+ providers: []
3339
+ })
3340
+ ], HealthModule);
1998
3341
  // src/testing/harness.ts
1999
3342
  import { performance as performance2 } from "perf_hooks";
2000
3343
 
@@ -2056,22 +3399,33 @@ class StressTester {
2056
3399
  }
2057
3400
  }
2058
3401
  export {
3402
+ requiresAuth,
3403
+ getAuthMetadata,
3404
+ createSwaggerUIMiddleware,
2059
3405
  createStaticFileMiddleware,
3406
+ createSecurityFilter,
2060
3407
  createRequestLoggingMiddleware,
2061
3408
  createLoggerMiddleware,
2062
3409
  createFileUploadMiddleware,
2063
3410
  createErrorHandlingMiddleware,
2064
3411
  createCorsMiddleware,
3412
+ checkRoles,
2065
3413
  WebSocketGatewayRegistry,
2066
3414
  WebSocketGateway,
2067
3415
  ValidationError,
2068
3416
  Validate,
2069
3417
  UseMiddleware,
2070
3418
  UnauthorizedException,
3419
+ SwaggerModule,
3420
+ SwaggerGenerator,
3421
+ SwaggerExtension,
2071
3422
  StressTester,
3423
+ SecurityModule,
3424
+ SecurityContextHolder,
2072
3425
  Router,
2073
3426
  RouteRegistry,
2074
3427
  Route,
3428
+ RoleBasedAccessDecisionManager,
2075
3429
  ResponseBuilder,
2076
3430
  RequestWrapper,
2077
3431
  Query,
@@ -2084,15 +3438,23 @@ export {
2084
3438
  OnOpen,
2085
3439
  OnMessage,
2086
3440
  OnClose,
3441
+ OAuth2Service,
3442
+ OAuth2Controller,
3443
+ OAuth2AuthenticationProvider,
3444
+ OAUTH2_SERVICE_TOKEN,
2087
3445
  NotFoundException,
2088
3446
  ModuleRegistry,
2089
3447
  Module,
2090
3448
  MinLength,
2091
3449
  MiddlewarePipeline,
3450
+ LoggerModule,
2092
3451
  LoggerExtension,
2093
3452
  LogLevel2 as LogLevel,
2094
3453
  Lifecycle,
2095
3454
  LOGGER_TOKEN,
3455
+ JwtAuthenticationProvider,
3456
+ JWT_UTIL_TOKEN,
3457
+ JWTUtil,
2096
3458
  IsString,
2097
3459
  IsOptional,
2098
3460
  IsNumber,
@@ -2101,7 +3463,10 @@ export {
2101
3463
  Injectable,
2102
3464
  Inject,
2103
3465
  HttpException,
3466
+ HealthModule,
2104
3467
  Header,
3468
+ HEALTH_OPTIONS_TOKEN,
3469
+ HEALTH_INDICATORS_TOKEN,
2105
3470
  GET,
2106
3471
  ForbiddenException,
2107
3472
  ExceptionFilterRegistry,
@@ -2110,9 +3475,19 @@ export {
2110
3475
  Controller,
2111
3476
  Context,
2112
3477
  Container,
3478
+ ConfigService,
3479
+ ConfigModule,
3480
+ CONFIG_SERVICE_TOKEN,
2113
3481
  BunServer,
2114
3482
  BodyParser,
2115
3483
  Body,
2116
3484
  BadRequestException,
2117
- Application
3485
+ AuthenticationManager,
3486
+ Auth,
3487
+ Application,
3488
+ ApiTags,
3489
+ ApiResponse,
3490
+ ApiParam,
3491
+ ApiOperation,
3492
+ ApiBody
2118
3493
  };