@hazeljs/swagger 0.3.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -5,4 +5,5 @@ export { SwaggerModule } from './swagger.module';
5
5
  export { SwaggerService, type SwaggerSpec } from './swagger.service';
6
6
  export { Swagger, ApiOperation, getSwaggerMetadata, getOperationMetadata, } from './swagger.decorator';
7
7
  export type { SwaggerOptions, SwaggerOperation, SwaggerSchema } from './swagger.types';
8
+ export type { AutoSwaggerOptions } from './swagger.service';
8
9
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrE,OAAO,EACL,OAAO,EACP,YAAY,EACZ,kBAAkB,EAClB,oBAAoB,GACrB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EAAE,cAAc,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrE,OAAO,EACL,OAAO,EACP,YAAY,EACZ,kBAAkB,EAClB,oBAAoB,GACrB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EAAE,cAAc,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAGvF,YAAY,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC"}
@@ -1,5 +1,11 @@
1
1
  import { SwaggerOperation, SwaggerSchema } from './swagger.types';
2
2
  import { Type } from '@hazeljs/core';
3
+ export interface AutoSwaggerOptions {
4
+ title?: string;
5
+ description?: string;
6
+ version?: string;
7
+ autoGenerateOperations?: boolean;
8
+ }
3
9
  export interface SwaggerSpec {
4
10
  openapi: string;
5
11
  info: {
@@ -18,6 +24,12 @@ export interface SwaggerSpec {
18
24
  }
19
25
  export declare class SwaggerService {
20
26
  private spec;
27
+ generateAutoSpec(moduleType: Type<unknown>, options?: AutoSwaggerOptions): SwaggerSpec;
28
+ private processControllerAuto;
29
+ private processRouteAuto;
30
+ private generateAutoOperation;
31
+ private extractPathParameters;
32
+ private addDefaultSchemas;
21
33
  generateSpec(controllers: Type<unknown>[]): SwaggerSpec;
22
34
  private normalizePath;
23
35
  }
@@ -1 +1 @@
1
- {"version":3,"file":"swagger.service.d.ts","sourceRoot":"","sources":["../src/swagger.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAGlE,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAErC,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE;QACJ,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC;IACF,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAAC;IACxD,UAAU,EAAE;QACV,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;KACxC,CAAC;IACF,IAAI,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACrD;AAQD,qBACa,cAAc;IACzB,OAAO,CAAC,IAAI,CAOV;IAEF,YAAY,CAAC,WAAW,EAAE,IAAI,CAAC,OAAO,CAAC,EAAE,GAAG,WAAW;IAsGvD,OAAO,CAAC,aAAa;CAStB"}
1
+ {"version":3,"file":"swagger.service.d.ts","sourceRoot":"","sources":["../src/swagger.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAGlE,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAGrC,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,sBAAsB,CAAC,EAAE,OAAO,CAAC;CAClC;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE;QACJ,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC;IACF,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAAC;IACxD,UAAU,EAAE;QACV,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;KACxC,CAAC;IACF,IAAI,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACrD;AAQD,qBACa,cAAc;IACzB,OAAO,CAAC,IAAI,CAOV;IAGF,gBAAgB,CAAC,UAAU,EAAE,IAAI,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC,EAAE,kBAAkB,GAAG,WAAW;IAqCtF,OAAO,CAAC,qBAAqB;IA8B7B,OAAO,CAAC,gBAAgB;IA8BxB,OAAO,CAAC,qBAAqB;IAkG7B,OAAO,CAAC,qBAAqB;IAY7B,OAAO,CAAC,iBAAiB;IAyCzB,YAAY,CAAC,WAAW,EAAE,IAAI,CAAC,OAAO,CAAC,EAAE,GAAG,WAAW;IAsGvD,OAAO,CAAC,aAAa;CAStB"}
@@ -13,6 +13,7 @@ exports.SwaggerService = void 0;
13
13
  const core_1 = require("@hazeljs/core");
14
14
  const swagger_decorator_1 = require("./swagger.decorator");
15
15
  const core_2 = __importDefault(require("@hazeljs/core"));
16
+ const core_3 = require("@hazeljs/core");
16
17
  let SwaggerService = class SwaggerService {
17
18
  constructor() {
18
19
  this.spec = {
@@ -24,6 +25,220 @@ let SwaggerService = class SwaggerService {
24
25
  },
25
26
  };
26
27
  }
28
+ // Auto-generate spec from module without explicit Swagger decorators
29
+ generateAutoSpec(moduleType, options) {
30
+ try {
31
+ core_2.default.debug('Auto-generating Swagger spec from module:', moduleType.name);
32
+ // Reset spec
33
+ this.spec = {
34
+ openapi: '3.0.0',
35
+ info: {
36
+ title: options?.title || 'HazelJS API',
37
+ description: options?.description || 'Auto-generated API documentation',
38
+ version: options?.version || '1.0.0',
39
+ },
40
+ paths: {},
41
+ components: {
42
+ schemas: {},
43
+ },
44
+ tags: [],
45
+ };
46
+ const controllers = (0, core_3.collectControllersFromModule)(moduleType);
47
+ // Process each controller
48
+ controllers.forEach((controller) => {
49
+ this.processControllerAuto(controller, options?.autoGenerateOperations !== false);
50
+ });
51
+ // Add default error schemas
52
+ this.addDefaultSchemas();
53
+ core_2.default.debug('Auto-generated Swagger specification completed');
54
+ return this.spec;
55
+ }
56
+ catch (error) {
57
+ core_2.default.error('Failed to auto-generate Swagger specification:', error);
58
+ throw error;
59
+ }
60
+ }
61
+ processControllerAuto(controller, autoGenerateOps) {
62
+ if (!controller)
63
+ return;
64
+ // Get controller path from metadata
65
+ const controllerMetadata = Reflect.getMetadata('hazel:controller', controller) || {};
66
+ const basePath = controllerMetadata.path || '';
67
+ // Get API tags from metadata
68
+ const apiTags = Reflect.getMetadata('hazel:api:tags', controller) || [];
69
+ // Add controller as a tag if not already present
70
+ if (!this.spec.tags)
71
+ this.spec.tags = [];
72
+ const controllerTag = {
73
+ name: apiTags.length > 0 ? apiTags[0] : controller.name,
74
+ description: `${controller.name} endpoints`,
75
+ };
76
+ if (!this.spec.tags.find((tag) => tag.name === controllerTag.name)) {
77
+ this.spec.tags.push(controllerTag);
78
+ }
79
+ // Get route metadata
80
+ const routes = Reflect.getMetadata('hazel:routes', controller) || [];
81
+ // Process each route
82
+ routes.forEach((route) => {
83
+ this.processRouteAuto(controller, route, basePath, controllerTag.name, autoGenerateOps);
84
+ });
85
+ }
86
+ processRouteAuto(controller, route, basePath, tag, autoGenerateOps) {
87
+ const { path, method, propertyKey } = route;
88
+ const fullPath = this.normalizePath(`${basePath}${path}`);
89
+ // Check for existing operation metadata
90
+ let operation = (0, swagger_decorator_1.getOperationMetadata)(controller.prototype, propertyKey);
91
+ // Auto-generate operation if not found and auto-generation is enabled
92
+ if (!operation && autoGenerateOps) {
93
+ operation = this.generateAutoOperation(method, propertyKey, tag, route);
94
+ }
95
+ if (!operation)
96
+ return;
97
+ // Add operation to paths
98
+ const pathItem = this.spec.paths[fullPath] || {};
99
+ pathItem[method.toLowerCase()] = {
100
+ ...operation,
101
+ tags: operation.tags || [tag],
102
+ };
103
+ this.spec.paths[fullPath] = pathItem;
104
+ }
105
+ generateAutoOperation(method, propertyKey, tag, route) {
106
+ const methodName = String(propertyKey);
107
+ const isGetMethod = method.toLowerCase() === 'get';
108
+ const isPostMethod = method.toLowerCase() === 'post';
109
+ const isPutMethod = method.toLowerCase() === 'put';
110
+ const isDeleteMethod = method.toLowerCase() === 'delete';
111
+ // Generate summary based on method name
112
+ let summary = '';
113
+ if (methodName.includes('create') || isPostMethod) {
114
+ summary = `Create new resource`;
115
+ }
116
+ else if (methodName.includes('update') || isPutMethod) {
117
+ summary = `Update resource`;
118
+ }
119
+ else if (methodName.includes('delete') || isDeleteMethod) {
120
+ summary = `Delete resource`;
121
+ }
122
+ else if (methodName.includes('find') || isGetMethod) {
123
+ summary = `Get resource(s)`;
124
+ }
125
+ else {
126
+ summary = `${method.toUpperCase()} ${methodName}`;
127
+ }
128
+ const operation = {
129
+ summary,
130
+ description: `Auto-generated ${method.toUpperCase()} operation`,
131
+ tags: [tag],
132
+ responses: {
133
+ '200': {
134
+ description: 'Successful response',
135
+ content: {
136
+ 'application/json': {
137
+ schema: {
138
+ type: 'object',
139
+ properties: {
140
+ data: {
141
+ type: 'object',
142
+ },
143
+ },
144
+ },
145
+ },
146
+ },
147
+ },
148
+ },
149
+ };
150
+ // Add parameters for path variables - need to extract from the route path
151
+ const pathParams = this.extractPathParameters(route.path);
152
+ if (pathParams.length > 0) {
153
+ operation.parameters = pathParams.map((param) => ({
154
+ name: param,
155
+ in: 'path',
156
+ required: true,
157
+ schema: { type: 'string' },
158
+ }));
159
+ }
160
+ // Add request body for POST/PUT/PATCH
161
+ if (isPostMethod || isPutMethod) {
162
+ operation.requestBody = {
163
+ required: true,
164
+ content: {
165
+ 'application/json': {
166
+ schema: {
167
+ type: 'object',
168
+ },
169
+ },
170
+ },
171
+ };
172
+ }
173
+ // Add common error responses
174
+ if (operation.responses) {
175
+ operation.responses['400'] = {
176
+ description: 'Bad request',
177
+ content: {
178
+ 'application/json': {
179
+ schema: { type: 'object' },
180
+ },
181
+ },
182
+ };
183
+ operation.responses['500'] = {
184
+ description: 'Internal server error',
185
+ content: {
186
+ 'application/json': {
187
+ schema: { type: 'object' },
188
+ },
189
+ },
190
+ };
191
+ }
192
+ return operation;
193
+ }
194
+ extractPathParameters(path) {
195
+ const params = [];
196
+ const paramRegex = /:([^/]+)/g;
197
+ let match;
198
+ while ((match = paramRegex.exec(path)) !== null) {
199
+ params.push(match[1]);
200
+ }
201
+ return params;
202
+ }
203
+ addDefaultSchemas() {
204
+ // Add common error schemas
205
+ this.spec.components.schemas.Error = {
206
+ type: 'object',
207
+ properties: {
208
+ error: {
209
+ type: 'object',
210
+ properties: {
211
+ message: { type: 'string' },
212
+ statusCode: { type: 'number' },
213
+ timestamp: { type: 'string', format: 'date-time' },
214
+ },
215
+ },
216
+ },
217
+ };
218
+ this.spec.components.schemas.ValidationError = {
219
+ type: 'object',
220
+ properties: {
221
+ error: {
222
+ type: 'object',
223
+ properties: {
224
+ message: { type: 'string' },
225
+ statusCode: { type: 'number' },
226
+ timestamp: { type: 'string', format: 'date-time' },
227
+ errors: {
228
+ type: 'array',
229
+ items: {
230
+ type: 'object',
231
+ properties: {
232
+ field: { type: 'string' },
233
+ message: { type: 'string' },
234
+ },
235
+ },
236
+ },
237
+ },
238
+ },
239
+ },
240
+ };
241
+ }
27
242
  generateSpec(controllers) {
28
243
  try {
29
244
  if (!Array.isArray(controllers)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hazeljs/swagger",
3
- "version": "0.3.1",
3
+ "version": "0.4.0",
4
4
  "description": "Swagger/OpenAPI documentation module for HazelJS framework",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -51,5 +51,5 @@
51
51
  "peerDependencies": {
52
52
  "@hazeljs/core": ">=0.2.0-beta.0"
53
53
  },
54
- "gitHead": "db6df492988e5d7b03253a98977c71c7a4540e1c"
54
+ "gitHead": "3cde5425a60eed497a777795724c93a926c69155"
55
55
  }