@hazeljs/swagger 0.8.5 → 0.8.7

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 (43) hide show
  1. package/README.md +78 -516
  2. package/dist/index.d.ts +6 -3
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +8 -2
  5. package/dist/openapi-document.test.d.ts +2 -0
  6. package/dist/openapi-document.test.d.ts.map +1 -0
  7. package/dist/openapi-document.test.js +42 -0
  8. package/dist/swagger-config.d.ts +5 -0
  9. package/dist/swagger-config.d.ts.map +1 -0
  10. package/dist/swagger-config.js +15 -0
  11. package/dist/swagger.controller.d.ts +2 -1
  12. package/dist/swagger.controller.d.ts.map +1 -1
  13. package/dist/swagger.controller.js +44 -85
  14. package/dist/swagger.controller.test.js +41 -156
  15. package/dist/swagger.module.d.ts +7 -0
  16. package/dist/swagger.module.d.ts.map +1 -1
  17. package/dist/swagger.module.js +16 -0
  18. package/dist/swagger.service.d.ts +14 -27
  19. package/dist/swagger.service.d.ts.map +1 -1
  20. package/dist/swagger.service.js +143 -162
  21. package/dist/swagger.service.test.js +164 -22
  22. package/dist/swagger.types.d.ts +52 -1
  23. package/dist/swagger.types.d.ts.map +1 -1
  24. package/package.json +4 -8
  25. package/dist/src/index.d.ts +0 -8
  26. package/dist/src/index.d.ts.map +0 -1
  27. package/dist/src/index.js +0 -15
  28. package/dist/src/swagger.controller.d.ts +0 -12
  29. package/dist/src/swagger.controller.d.ts.map +0 -1
  30. package/dist/src/swagger.controller.js +0 -238
  31. package/dist/src/swagger.decorator.d.ts +0 -7
  32. package/dist/src/swagger.decorator.d.ts.map +0 -1
  33. package/dist/src/swagger.decorator.js +0 -26
  34. package/dist/src/swagger.module.d.ts +0 -5
  35. package/dist/src/swagger.module.d.ts.map +0 -1
  36. package/dist/src/swagger.module.js +0 -30
  37. package/dist/src/swagger.service.d.ts +0 -24
  38. package/dist/src/swagger.service.d.ts.map +0 -1
  39. package/dist/src/swagger.service.js +0 -117
  40. package/dist/src/swagger.types.d.ts +0 -50
  41. package/dist/src/swagger.types.d.ts.map +0 -1
  42. package/dist/src/swagger.types.js +0 -2
  43. package/dist/tsconfig.tsbuildinfo +0 -1
package/dist/index.js CHANGED
@@ -4,12 +4,18 @@
4
4
  */
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.getOperationMetadata = exports.getSwaggerMetadata = exports.ApiOperation = exports.Swagger = exports.SwaggerService = exports.SwaggerModule = void 0;
7
+ exports.createOpenApiDocument = createOpenApiDocument;
8
+ const swagger_service_1 = require("./swagger.service");
7
9
  var swagger_module_1 = require("./swagger.module");
8
10
  Object.defineProperty(exports, "SwaggerModule", { enumerable: true, get: function () { return swagger_module_1.SwaggerModule; } });
9
- var swagger_service_1 = require("./swagger.service");
10
- Object.defineProperty(exports, "SwaggerService", { enumerable: true, get: function () { return swagger_service_1.SwaggerService; } });
11
+ var swagger_service_2 = require("./swagger.service");
12
+ Object.defineProperty(exports, "SwaggerService", { enumerable: true, get: function () { return swagger_service_2.SwaggerService; } });
11
13
  var swagger_decorator_1 = require("./swagger.decorator");
12
14
  Object.defineProperty(exports, "Swagger", { enumerable: true, get: function () { return swagger_decorator_1.Swagger; } });
13
15
  Object.defineProperty(exports, "ApiOperation", { enumerable: true, get: function () { return swagger_decorator_1.ApiOperation; } });
14
16
  Object.defineProperty(exports, "getSwaggerMetadata", { enumerable: true, get: function () { return swagger_decorator_1.getSwaggerMetadata; } });
15
17
  Object.defineProperty(exports, "getOperationMetadata", { enumerable: true, get: function () { return swagger_decorator_1.getOperationMetadata; } });
18
+ /** Build an OpenAPI document without serving HTTP (e.g. CI export). */
19
+ function createOpenApiDocument(rootModule, options) {
20
+ return new swagger_service_1.SwaggerService().generateAutoSpec(rootModule, options);
21
+ }
@@ -0,0 +1,2 @@
1
+ import 'reflect-metadata';
2
+ //# sourceMappingURL=openapi-document.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openapi-document.test.d.ts","sourceRoot":"","sources":["../src/openapi-document.test.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC"}
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || 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") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ require("reflect-metadata");
13
+ const index_1 = require("./index");
14
+ const core_1 = require("@hazeljs/core");
15
+ describe('createOpenApiDocument', () => {
16
+ it('returns a spec from the app module', () => {
17
+ let PingController = class PingController {
18
+ ping() {
19
+ return 'pong';
20
+ }
21
+ };
22
+ __decorate([
23
+ (0, core_1.Get)(),
24
+ __metadata("design:type", Function),
25
+ __metadata("design:paramtypes", []),
26
+ __metadata("design:returntype", String)
27
+ ], PingController.prototype, "ping", null);
28
+ PingController = __decorate([
29
+ (0, core_1.Controller)({ path: '/ping' })
30
+ ], PingController);
31
+ Reflect.defineMetadata('hazel:controller', { path: '/ping' }, PingController);
32
+ Reflect.defineMetadata('hazel:routes', [{ propertyKey: 'ping', path: '', method: 'GET' }], PingController);
33
+ let AppModule = class AppModule {
34
+ };
35
+ AppModule = __decorate([
36
+ (0, core_1.HazelModule)({ controllers: [PingController], imports: [] })
37
+ ], AppModule);
38
+ const spec = (0, index_1.createOpenApiDocument)(AppModule, { title: 'CI', version: '0.0.1' });
39
+ expect(spec.info.title).toBe('CI');
40
+ expect(spec.paths['/ping']?.get).toBeDefined();
41
+ });
42
+ });
@@ -0,0 +1,5 @@
1
+ import type { SwaggerModuleOptions } from './swagger.types';
2
+ export declare function mergeSwaggerModuleOptions(options: SwaggerModuleOptions): void;
3
+ export declare function replaceSwaggerModuleOptions(options: SwaggerModuleOptions): void;
4
+ export declare function getSwaggerModuleOptions(): SwaggerModuleOptions;
5
+ //# sourceMappingURL=swagger-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"swagger-config.d.ts","sourceRoot":"","sources":["../src/swagger-config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAI5D,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,oBAAoB,GAAG,IAAI,CAE7E;AAED,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,oBAAoB,GAAG,IAAI,CAE/E;AAED,wBAAgB,uBAAuB,IAAI,oBAAoB,CAE9D"}
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.mergeSwaggerModuleOptions = mergeSwaggerModuleOptions;
4
+ exports.replaceSwaggerModuleOptions = replaceSwaggerModuleOptions;
5
+ exports.getSwaggerModuleOptions = getSwaggerModuleOptions;
6
+ let swaggerModuleOptions = {};
7
+ function mergeSwaggerModuleOptions(options) {
8
+ swaggerModuleOptions = { ...swaggerModuleOptions, ...options };
9
+ }
10
+ function replaceSwaggerModuleOptions(options) {
11
+ swaggerModuleOptions = { ...options };
12
+ }
13
+ function getSwaggerModuleOptions() {
14
+ return { ...swaggerModuleOptions };
15
+ }
@@ -1,11 +1,12 @@
1
1
  import { SwaggerService } from './swagger.service';
2
2
  import { RequestContext, Type } from '@hazeljs/core';
3
- import { SwaggerSpec } from './swagger.service';
3
+ import type { SwaggerSpec } from './swagger.types';
4
4
  export declare class SwaggerController {
5
5
  private swaggerService;
6
6
  private static rootModule;
7
7
  constructor(swaggerService: SwaggerService);
8
8
  static setRootModule(module: Type<unknown>): void;
9
+ private static emptySpec;
9
10
  getSpec(_context: RequestContext): Promise<SwaggerSpec>;
10
11
  getDocs(_context: RequestContext): Promise<string>;
11
12
  }
@@ -1 +1 @@
1
- {"version":3,"file":"swagger.controller.d.ts","sourceRoot":"","sources":["../src/swagger.controller.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAIrD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,qBAca,iBAAiB;IAGhB,OAAO,CAAC,cAAc;IAFlC,OAAO,CAAC,MAAM,CAAC,UAAU,CAAgB;gBAErB,cAAc,EAAE,cAAc;IAElD,MAAM,CAAC,aAAa,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,IAAI;IA6B3C,OAAO,CAAC,QAAQ,EAAE,cAAc,GAAG,OAAO,CAAC,WAAW,CAAC;IA6IvD,OAAO,CAAC,QAAQ,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC;CA6DzD"}
1
+ {"version":3,"file":"swagger.controller.d.ts","sourceRoot":"","sources":["../src/swagger.controller.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAGrD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAGnD,qBAca,iBAAiB;IAGhB,OAAO,CAAC,cAAc;IAFlC,OAAO,CAAC,MAAM,CAAC,UAAU,CAAgB;gBAErB,cAAc,EAAE,cAAc;IAElD,MAAM,CAAC,aAAa,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,IAAI;IAKjD,OAAO,CAAC,MAAM,CAAC,SAAS;IAuClB,OAAO,CAAC,QAAQ,EAAE,cAAc,GAAG,OAAO,CAAC,WAAW,CAAC;IAkDvD,OAAO,CAAC,QAAQ,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC;CAqEzD"}
@@ -16,115 +16,75 @@ Object.defineProperty(exports, "__esModule", { value: true });
16
16
  exports.SwaggerController = void 0;
17
17
  const core_1 = require("@hazeljs/core");
18
18
  const swagger_service_1 = require("./swagger.service");
19
- const core_2 = require("@hazeljs/core");
20
- const core_3 = __importDefault(require("@hazeljs/core"));
19
+ const core_2 = __importDefault(require("@hazeljs/core"));
21
20
  const swagger_decorator_1 = require("./swagger.decorator");
21
+ const swagger_config_1 = require("./swagger-config");
22
22
  let SwaggerController = SwaggerController_1 = class SwaggerController {
23
23
  constructor(swaggerService) {
24
24
  this.swaggerService = swaggerService;
25
25
  }
26
26
  static setRootModule(module) {
27
- core_3.default.debug(`Setting root module for SwaggerController: ${module.name}`);
27
+ core_2.default.debug(`Setting root module for SwaggerController: ${module.name}`);
28
28
  SwaggerController_1.rootModule = module;
29
29
  }
30
+ static emptySpec(description) {
31
+ return {
32
+ openapi: '3.0.0',
33
+ info: {
34
+ title: 'API Documentation',
35
+ version: '1.0.0',
36
+ description,
37
+ },
38
+ paths: {},
39
+ components: {
40
+ schemas: {},
41
+ },
42
+ };
43
+ }
30
44
  async getSpec(_context) {
31
45
  try {
32
46
  if (!SwaggerController_1.rootModule) {
33
- core_3.default.warn('No root module provided');
34
- return {
35
- openapi: '3.0.0',
36
- info: {
37
- title: 'API Documentation',
38
- version: '1.0.0',
39
- description: 'No root module provided',
40
- },
41
- paths: {},
42
- components: {
43
- schemas: {},
44
- },
45
- };
47
+ core_2.default.warn('No root module provided');
48
+ return SwaggerController_1.emptySpec('No root module provided');
46
49
  }
47
- core_3.default.debug('Root module:', SwaggerController_1.rootModule.name);
48
- // Get all controllers from the AppModule and its imported modules.
49
- // Avoid JSON.stringify on full metadata because dynamic module objects can be circular.
50
- const moduleMetadata = (0, core_2.getModuleMetadata)(SwaggerController_1.rootModule);
51
- core_3.default.debug('Module metadata summary:', moduleMetadata
52
- ? {
53
- controllers: moduleMetadata.controllers?.length || 0,
54
- imports: moduleMetadata.imports?.length || 0,
55
- providers: moduleMetadata.providers?.length || 0,
56
- }
57
- : null);
58
- const controllers = new Set();
59
- // Helper function to recursively collect controllers from modules
60
- const collectControllers = (moduleRef) => {
61
- const moduleName = typeof moduleRef === 'function'
62
- ? moduleRef.name
63
- : moduleRef.module?.name;
64
- core_3.default.debug(`Collecting controllers from module: ${moduleName}`);
65
- const metadata = (0, core_2.getModuleMetadata)(moduleRef);
66
- if (!metadata) {
67
- core_3.default.warn(`No metadata found for module: ${moduleName}`);
68
- return;
69
- }
70
- // Add controllers from current module
71
- if (metadata.controllers) {
72
- const validControllers = metadata.controllers.filter((c) => c && typeof c === 'function');
73
- core_3.default.debug(`${moduleName} controllers:`, validControllers.map((c) => typeof c === 'function' ? c.name : undefined));
74
- validControllers.forEach((controller) => controllers.add(controller));
75
- }
76
- else {
77
- core_3.default.debug(`No controllers found in module: ${moduleName}`);
78
- }
79
- // Recursively process imported modules (Type or DynamicModule)
80
- if (metadata.imports) {
81
- const validModules = metadata.imports.filter((m) => m && (typeof m === 'function' || (typeof m === 'object' && 'module' in m)));
82
- core_3.default.debug(`${moduleName} imported modules:`, validModules.map((m) => typeof m === 'function' ? m.name : m.module?.name));
83
- validModules.forEach((moduleRef) => collectControllers(moduleRef));
84
- }
85
- else {
86
- core_3.default.debug(`No imports found in module: ${moduleName}`);
87
- }
88
- };
89
- // Start collecting controllers from the root module
90
- collectControllers(SwaggerController_1.rootModule);
91
- const controllerArray = Array.from(controllers);
92
- core_3.default.debug('Total controllers found:', controllerArray.length);
93
- core_3.default.debug('Controller names:', controllerArray.map((c) => c.name));
94
- if (controllerArray.length === 0) {
95
- core_3.default.warn('No valid controllers found');
50
+ core_2.default.debug('Root module:', SwaggerController_1.rootModule.name);
51
+ const opts = (0, swagger_config_1.getSwaggerModuleOptions)();
52
+ const spec = this.swaggerService.generateAutoSpec(SwaggerController_1.rootModule, opts);
53
+ if (Object.keys(spec.paths).length === 0) {
54
+ core_2.default.warn('No routes found for OpenAPI document');
96
55
  return {
97
- openapi: '3.0.0',
56
+ ...spec,
98
57
  info: {
99
- title: 'API Documentation',
100
- version: '1.0.0',
101
- description: 'No controllers found',
102
- },
103
- paths: {},
104
- components: {
105
- schemas: {},
58
+ ...spec.info,
59
+ description: spec.info.description || 'No routes found',
106
60
  },
107
61
  };
108
62
  }
109
- const spec = this.swaggerService.generateSpec(controllerArray);
110
63
  return spec;
111
64
  }
112
65
  catch (error) {
113
66
  if (process.env.NODE_ENV !== 'test') {
114
- core_3.default.error('Error generating Swagger spec:', error);
67
+ core_2.default.error('Error generating Swagger spec:', error);
115
68
  }
116
69
  throw error;
117
70
  }
118
71
  }
119
72
  async getDocs(_context) {
120
- return `
121
- <!DOCTYPE html>
73
+ const opts = (0, swagger_config_1.getSwaggerModuleOptions)();
74
+ const cdn = opts.swaggerUiCdnBase?.replace(/\/$/, '') ?? 'https://unpkg.com/swagger-ui-dist@5.11.0';
75
+ let prefix = opts.globalPrefix?.trim() ?? '';
76
+ if (prefix && !prefix.startsWith('/')) {
77
+ prefix = `/${prefix}`;
78
+ }
79
+ prefix = prefix.replace(/\/$/, '');
80
+ const specPath = prefix ? `${prefix}/swagger/spec` : '/swagger/spec';
81
+ return `<!DOCTYPE html>
122
82
  <html>
123
83
  <head>
124
84
  <title>API Documentation</title>
125
85
  <meta charset="utf-8"/>
126
86
  <meta name="viewport" content="width=device-width, initial-scale=1">
127
- <link rel="stylesheet" type="text/css" href="https://unpkg.com/swagger-ui-dist@4.18.3/swagger-ui.css" />
87
+ <link rel="stylesheet" type="text/css" href="${cdn}/swagger-ui.css" />
128
88
  <style>
129
89
  body {
130
90
  margin: 0;
@@ -146,19 +106,18 @@ let SwaggerController = SwaggerController_1 = class SwaggerController {
146
106
  <div class="loading">Loading API Documentation...</div>
147
107
  </div>
148
108
 
149
- <script src="https://unpkg.com/swagger-ui-dist@4.18.3/swagger-ui-bundle.js"></script>
150
- <script src="https://unpkg.com/swagger-ui-dist@4.18.3/swagger-ui-standalone-preset.js"></script>
109
+ <script src="${cdn}/swagger-ui-bundle.js"></script>
110
+ <script src="${cdn}/swagger-ui-standalone-preset.js"></script>
151
111
  <script>
152
- // Basic error handling
153
112
  window.onerror = function(msg, url, line) {
154
- document.getElementById('swagger-ui').innerHTML =
113
+ document.getElementById('swagger-ui').innerHTML =
155
114
  '<div style="color: red; padding: 20px;">Error: ' + msg + '<br>at line ' + line + '</div>';
156
115
  return false;
157
116
  };
158
117
 
159
- // Simple initialization
118
+ const specPath = ${JSON.stringify(specPath)};
160
119
  const ui = SwaggerUIBundle({
161
- url: window.location.origin + "/swagger/spec",
120
+ url: window.location.origin + specPath,
162
121
  dom_id: '#swagger-ui',
163
122
  deepLinking: true,
164
123
  presets: [
@@ -175,7 +134,7 @@ let SwaggerController = SwaggerController_1 = class SwaggerController {
175
134
  </script>
176
135
  </body>
177
136
  </html>
178
- `;
137
+ `;
179
138
  }
180
139
  };
181
140
  exports.SwaggerController = SwaggerController;
@@ -1,14 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const swagger_controller_1 = require("./swagger.controller");
4
- const core_1 = require("@hazeljs/core");
5
- // Mock the logger and getModuleMetadata
4
+ const swagger_module_1 = require("./swagger.module");
6
5
  jest.mock('@hazeljs/core', () => {
7
6
  const actual = jest.requireActual('@hazeljs/core');
8
7
  return {
9
8
  ...actual,
10
9
  __esModule: true,
11
- getModuleMetadata: jest.fn(),
12
10
  default: {
13
11
  error: jest.fn(),
14
12
  debug: jest.fn(),
@@ -23,89 +21,71 @@ describe('SwaggerController', () => {
23
21
  let controller;
24
22
  let swaggerService;
25
23
  beforeEach(() => {
24
+ swagger_module_1.SwaggerModule.configure({}, true);
26
25
  swaggerService = {
27
- generateSpec: jest.fn(),
26
+ generateAutoSpec: jest.fn(),
28
27
  };
29
28
  controller = new swagger_controller_1.SwaggerController(swaggerService);
30
- // Reset all mocks before each test
31
29
  jest.clearAllMocks();
30
+ swagger_controller_1.SwaggerController.rootModule = undefined;
32
31
  });
33
32
  it('should be defined', () => {
34
33
  expect(controller).toBeDefined();
35
34
  });
36
- it('should return swagger spec', async () => {
35
+ it('should return swagger spec from generateAutoSpec', async () => {
36
+ class AppModule {
37
+ }
38
+ swagger_controller_1.SwaggerController.setRootModule(AppModule);
37
39
  const mockSpec = {
38
40
  openapi: '3.0.0',
39
41
  info: {
40
42
  title: 'API Documentation',
41
43
  version: '1.0.0',
42
- description: 'No root module provided',
44
+ description: 'Test',
43
45
  },
44
- paths: {},
46
+ paths: { '/x': { get: { summary: 'x' } } },
45
47
  components: {
46
48
  schemas: {},
47
49
  },
48
50
  };
49
- swaggerService.generateSpec.mockReturnValue(mockSpec);
51
+ swaggerService.generateAutoSpec.mockReturnValue(mockSpec);
50
52
  const spec = await controller.getSpec({});
51
53
  expect(spec).toEqual(mockSpec);
54
+ expect(swaggerService.generateAutoSpec).toHaveBeenCalledWith(AppModule, expect.any(Object));
52
55
  });
53
56
  describe('getSpec', () => {
54
- it('should return Swagger specification', async () => {
57
+ it('should call generateAutoSpec with root module', async () => {
55
58
  const mockSpec = {
56
59
  openapi: '3.0.0',
57
60
  info: {
58
- title: 'API Documentation',
61
+ title: 'API',
59
62
  version: '1.0.0',
60
- description: 'No root module provided',
61
- },
62
- paths: {},
63
- components: {
64
- schemas: {},
65
63
  },
64
+ paths: { '/a': { get: { summary: 'a' } } },
65
+ components: { schemas: { Error: { type: 'object' } } },
66
66
  };
67
- // Set up the root module
68
67
  class TestModule {
69
68
  }
70
69
  swagger_controller_1.SwaggerController.setRootModule(TestModule);
71
- // Mock getModuleMetadata to return some controllers
72
- core_1.getModuleMetadata.mockReturnValue({
73
- controllers: [class TestController {
74
- }],
75
- imports: [],
76
- });
77
- swaggerService.generateSpec.mockReturnValue(mockSpec);
78
- const context = {};
79
- const result = await controller.getSpec(context);
70
+ swaggerService.generateAutoSpec.mockReturnValue(mockSpec);
71
+ const result = await controller.getSpec({});
80
72
  expect(result).toEqual(mockSpec);
81
- expect(swaggerService.generateSpec).toHaveBeenCalled();
73
+ expect(swaggerService.generateAutoSpec).toHaveBeenCalledWith(TestModule, expect.any(Object));
82
74
  });
83
75
  it('should handle errors', async () => {
84
76
  const error = new Error('Test error');
85
- swaggerService.generateSpec.mockImplementation(() => {
77
+ swaggerService.generateAutoSpec.mockImplementation(() => {
86
78
  throw error;
87
79
  });
88
- // Set up the root module
89
80
  class TestModule {
90
81
  }
91
82
  swagger_controller_1.SwaggerController.setRootModule(TestModule);
92
- // Mock getModuleMetadata to return some controllers
93
- core_1.getModuleMetadata.mockReturnValue({
94
- controllers: [class TestController {
95
- }],
96
- imports: [],
97
- });
98
- const context = {};
99
- await expect(controller.getSpec(context)).rejects.toThrow('Test error');
100
- expect(swaggerService.generateSpec).toHaveBeenCalled();
83
+ await expect(controller.getSpec({})).rejects.toThrow('Test error');
101
84
  });
102
85
  });
103
86
  describe('getSpec edge cases', () => {
104
87
  it('should return default spec when no root module is set', async () => {
105
- // Clear root module by setting it to undefined directly
106
- swagger_controller_1.SwaggerController.rootModule = undefined;
107
- const context = {};
108
- const result = await controller.getSpec(context);
88
+ const result = await controller.getSpec({});
109
89
  expect(result).toEqual({
110
90
  openapi: '3.0.0',
111
91
  info: {
@@ -119,132 +99,37 @@ describe('SwaggerController', () => {
119
99
  },
120
100
  });
121
101
  });
122
- it('should handle module with no metadata', async () => {
123
- class TestModule {
124
- }
125
- swagger_controller_1.SwaggerController.setRootModule(TestModule);
126
- core_1.getModuleMetadata.mockReturnValue(null);
127
- const context = {};
128
- const result = await controller.getSpec(context);
129
- expect(result).toEqual({
130
- openapi: '3.0.0',
131
- info: {
132
- title: 'API Documentation',
133
- version: '1.0.0',
134
- description: 'No controllers found',
135
- },
136
- paths: {},
137
- components: {
138
- schemas: {},
139
- },
140
- });
141
- });
142
- it('should handle module with no controllers', async () => {
143
- class TestModule {
144
- }
145
- swagger_controller_1.SwaggerController.setRootModule(TestModule);
146
- core_1.getModuleMetadata.mockReturnValue({
147
- controllers: [],
148
- imports: [],
149
- });
150
- const context = {};
151
- const result = await controller.getSpec(context);
152
- expect(result).toEqual({
153
- openapi: '3.0.0',
154
- info: {
155
- title: 'API Documentation',
156
- version: '1.0.0',
157
- description: 'No controllers found',
158
- },
159
- paths: {},
160
- components: {
161
- schemas: {},
162
- },
163
- });
164
- });
165
- it('should handle module with no imports', async () => {
102
+ it('should annotate description when spec has no paths', async () => {
166
103
  class TestModule {
167
104
  }
168
- class TestController {
169
- }
170
105
  swagger_controller_1.SwaggerController.setRootModule(TestModule);
171
- core_1.getModuleMetadata.mockReturnValue({
172
- controllers: [TestController],
173
- imports: undefined,
174
- });
175
- swaggerService.generateSpec.mockReturnValue({
106
+ swaggerService.generateAutoSpec.mockReturnValue({
176
107
  openapi: '3.0.0',
177
- info: { title: 'Test API', version: '1.0.0' },
108
+ info: { title: 'T', version: '1' },
178
109
  paths: {},
179
110
  components: { schemas: {} },
180
111
  });
181
- const context = {};
182
- await controller.getSpec(context);
183
- expect(swaggerService.generateSpec).toHaveBeenCalledWith([TestController]);
184
- });
185
- it('should recursively collect controllers from imported modules', async () => {
186
- class RootModule {
187
- }
188
- class ImportedModule {
189
- }
190
- class RootController {
191
- }
192
- class ImportedController {
193
- }
194
- swagger_controller_1.SwaggerController.setRootModule(RootModule);
195
- core_1.getModuleMetadata.mockImplementation((moduleType) => {
196
- if (moduleType === RootModule) {
197
- return {
198
- controllers: [RootController],
199
- imports: [ImportedModule],
200
- };
201
- }
202
- if (moduleType === ImportedModule) {
203
- return {
204
- controllers: [ImportedController],
205
- imports: [],
206
- };
207
- }
208
- return null;
209
- });
210
- swaggerService.generateSpec.mockReturnValue({
211
- openapi: '3.0.0',
212
- info: { title: 'Test API', version: '1.0.0' },
213
- paths: {},
214
- components: { schemas: {} },
215
- });
216
- const context = {};
217
- await controller.getSpec(context);
218
- expect(swaggerService.generateSpec).toHaveBeenCalledWith(expect.arrayContaining([RootController, ImportedController]));
219
- });
220
- it('should filter out invalid controllers', async () => {
221
- class TestModule {
222
- }
223
- class ValidController {
224
- }
225
- swagger_controller_1.SwaggerController.setRootModule(TestModule);
226
- core_1.getModuleMetadata.mockReturnValue({
227
- controllers: [ValidController, null, undefined, {}],
228
- imports: [],
229
- });
230
- swaggerService.generateSpec.mockReturnValue({
231
- openapi: '3.0.0',
232
- info: { title: 'Test API', version: '1.0.0' },
233
- paths: {},
234
- components: { schemas: {} },
235
- });
236
- const context = {};
237
- await controller.getSpec(context);
238
- expect(swaggerService.generateSpec).toHaveBeenCalledWith([ValidController]);
112
+ const result = await controller.getSpec({});
113
+ expect(result.paths).toEqual({});
114
+ expect(result.info.description).toBe('No routes found');
239
115
  });
240
116
  });
241
117
  describe('getDocs', () => {
242
- it('should return Swagger UI HTML', async () => {
243
- const context = {};
244
- const result = await controller.getDocs(context);
118
+ it('should return Swagger UI HTML with default CDN', async () => {
119
+ const result = await controller.getDocs({});
245
120
  expect(result).toContain('<!DOCTYPE html>');
246
121
  expect(result).toContain('API Documentation');
247
- expect(result).toContain('Loading API Documentation...');
122
+ expect(result).toContain('https://unpkg.com/swagger-ui-dist@5.11.0/swagger-ui.css');
123
+ expect(result).toContain('"/swagger/spec"');
124
+ });
125
+ it('should use globalPrefix and custom CDN when configured', async () => {
126
+ swagger_module_1.SwaggerModule.configure({
127
+ globalPrefix: '/api',
128
+ swaggerUiCdnBase: 'https://cdn.example.com/ui',
129
+ }, true);
130
+ const result = await controller.getDocs({});
131
+ expect(result).toContain('https://cdn.example.com/ui/swagger-ui.css');
132
+ expect(result).toContain('"/api/swagger/spec"');
248
133
  });
249
134
  });
250
135
  });
@@ -1,5 +1,12 @@
1
1
  import { Type } from '@hazeljs/core';
2
+ import type { SwaggerModuleOptions } from './swagger.types';
2
3
  export declare class SwaggerModule {
4
+ /**
5
+ * Document + UI options.
6
+ * @param replaceAll When true, replaces all options (use to reset). Default merges shallowly with previous `configure` calls.
7
+ */
8
+ static configure(options: SwaggerModuleOptions, replaceAll?: boolean): void;
9
+ static getOptions(): SwaggerModuleOptions;
3
10
  static setRootModule(rootModule: Type<unknown>): void;
4
11
  }
5
12
  //# sourceMappingURL=swagger.module.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"swagger.module.d.ts","sourceRoot":"","sources":["../src/swagger.module.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAGrC,qBAKa,aAAa;IACxB,MAAM,CAAC,aAAa,CAAC,UAAU,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,IAAI;CAItD"}
1
+ {"version":3,"file":"swagger.module.d.ts","sourceRoot":"","sources":["../src/swagger.module.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAErC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAO5D,qBAKa,aAAa;IACxB;;;OAGG;IACH,MAAM,CAAC,SAAS,CAAC,OAAO,EAAE,oBAAoB,EAAE,UAAU,UAAQ,GAAG,IAAI;IAQzE,MAAM,CAAC,UAAU,IAAI,oBAAoB;IAIzC,MAAM,CAAC,aAAa,CAAC,UAAU,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,IAAI;CAItD"}
@@ -14,7 +14,23 @@ const core_1 = require("@hazeljs/core");
14
14
  const swagger_service_1 = require("./swagger.service");
15
15
  const swagger_controller_1 = require("./swagger.controller");
16
16
  const core_2 = __importDefault(require("@hazeljs/core"));
17
+ const swagger_config_1 = require("./swagger-config");
17
18
  let SwaggerModule = class SwaggerModule {
19
+ /**
20
+ * Document + UI options.
21
+ * @param replaceAll When true, replaces all options (use to reset). Default merges shallowly with previous `configure` calls.
22
+ */
23
+ static configure(options, replaceAll = false) {
24
+ if (replaceAll) {
25
+ (0, swagger_config_1.replaceSwaggerModuleOptions)(options);
26
+ }
27
+ else {
28
+ (0, swagger_config_1.mergeSwaggerModuleOptions)(options);
29
+ }
30
+ }
31
+ static getOptions() {
32
+ return (0, swagger_config_1.getSwaggerModuleOptions)();
33
+ }
18
34
  static setRootModule(rootModule) {
19
35
  core_2.default.debug('SwaggerModule: Setting root module:', rootModule.name);
20
36
  swagger_controller_1.SwaggerController.setRootModule(rootModule);