@g1cloud/api-gen 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/.claude/settings.local.json +22 -0
  2. package/CLAUDE.md +63 -0
  3. package/README.md +379 -0
  4. package/dist/analyzer/controllerAnalyzer.d.ts +20 -0
  5. package/dist/analyzer/controllerAnalyzer.d.ts.map +1 -0
  6. package/dist/analyzer/controllerAnalyzer.js +101 -0
  7. package/dist/analyzer/controllerAnalyzer.js.map +1 -0
  8. package/dist/analyzer/parameterAnalyzer.d.ts +19 -0
  9. package/dist/analyzer/parameterAnalyzer.d.ts.map +1 -0
  10. package/dist/analyzer/parameterAnalyzer.js +207 -0
  11. package/dist/analyzer/parameterAnalyzer.js.map +1 -0
  12. package/dist/analyzer/responseAnalyzer.d.ts +12 -0
  13. package/dist/analyzer/responseAnalyzer.d.ts.map +1 -0
  14. package/dist/analyzer/responseAnalyzer.js +116 -0
  15. package/dist/analyzer/responseAnalyzer.js.map +1 -0
  16. package/dist/analyzer/schemaGenerator.d.ts +6 -0
  17. package/dist/analyzer/schemaGenerator.d.ts.map +1 -0
  18. package/dist/analyzer/schemaGenerator.js +347 -0
  19. package/dist/analyzer/schemaGenerator.js.map +1 -0
  20. package/dist/analyzer/securityAnalyzer.d.ts +6 -0
  21. package/dist/analyzer/securityAnalyzer.d.ts.map +1 -0
  22. package/dist/analyzer/securityAnalyzer.js +177 -0
  23. package/dist/analyzer/securityAnalyzer.js.map +1 -0
  24. package/dist/generator/openapiGenerator.d.ts +14 -0
  25. package/dist/generator/openapiGenerator.d.ts.map +1 -0
  26. package/dist/generator/openapiGenerator.js +340 -0
  27. package/dist/generator/openapiGenerator.js.map +1 -0
  28. package/dist/index.d.ts +3 -0
  29. package/dist/index.d.ts.map +1 -0
  30. package/dist/index.js +218 -0
  31. package/dist/index.js.map +1 -0
  32. package/dist/lib.d.ts +61 -0
  33. package/dist/lib.d.ts.map +1 -0
  34. package/dist/lib.js +199 -0
  35. package/dist/lib.js.map +1 -0
  36. package/dist/mcp-server.d.ts +9 -0
  37. package/dist/mcp-server.d.ts.map +1 -0
  38. package/dist/mcp-server.js +257 -0
  39. package/dist/mcp-server.js.map +1 -0
  40. package/dist/mcp-server.mjs +45586 -0
  41. package/dist/parser/astAnalyzer.d.ts +87 -0
  42. package/dist/parser/astAnalyzer.d.ts.map +1 -0
  43. package/dist/parser/astAnalyzer.js +321 -0
  44. package/dist/parser/astAnalyzer.js.map +1 -0
  45. package/dist/parser/javaParser.d.ts +10 -0
  46. package/dist/parser/javaParser.d.ts.map +1 -0
  47. package/dist/parser/javaParser.js +805 -0
  48. package/dist/parser/javaParser.js.map +1 -0
  49. package/dist/types/index.d.ts +217 -0
  50. package/dist/types/index.d.ts.map +1 -0
  51. package/dist/types/index.js +3 -0
  52. package/dist/types/index.js.map +1 -0
  53. package/examples/CreateUserRequest.java +80 -0
  54. package/examples/DepartmentDTO.java +45 -0
  55. package/examples/Filter.java +39 -0
  56. package/examples/PaginatedList.java +71 -0
  57. package/examples/ProductController.java +136 -0
  58. package/examples/ProductDTO.java +129 -0
  59. package/examples/RoleDTO.java +47 -0
  60. package/examples/SearchParam.java +55 -0
  61. package/examples/Sort.java +70 -0
  62. package/examples/UpdateUserRequest.java +74 -0
  63. package/examples/UserController.java +98 -0
  64. package/examples/UserDTO.java +119 -0
  65. package/package.json +51 -0
  66. package/prompt/01_Initial.md +358 -0
  67. package/prompt/02_/354/266/224/352/260/200.md +31 -0
  68. package/src/analyzer/controllerAnalyzer.ts +125 -0
  69. package/src/analyzer/parameterAnalyzer.ts +259 -0
  70. package/src/analyzer/responseAnalyzer.ts +142 -0
  71. package/src/analyzer/schemaGenerator.ts +412 -0
  72. package/src/analyzer/securityAnalyzer.ts +200 -0
  73. package/src/generator/openapiGenerator.ts +378 -0
  74. package/src/index.ts +212 -0
  75. package/src/lib.ts +240 -0
  76. package/src/mcp-server.ts +310 -0
  77. package/src/parser/astAnalyzer.ts +373 -0
  78. package/src/parser/javaParser.ts +901 -0
  79. package/src/types/index.ts +238 -0
  80. package/test-boolean.yaml +607 -0
  81. package/test-filter.yaml +576 -0
  82. package/test-inner.ts +59 -0
  83. package/test-output.yaml +650 -0
  84. package/test-paginated.yaml +585 -0
  85. package/tsconfig.json +20 -0
  86. package/tsup.config.ts +30 -0
@@ -0,0 +1,373 @@
1
+ import { JavaClass, JavaAnnotation, JavaMethod } from '../types';
2
+
3
+ export class ASTAnalyzer {
4
+ /**
5
+ * Check if a class is a REST controller
6
+ */
7
+ static isRestController(javaClass: JavaClass): boolean {
8
+ const annotations = javaClass.annotations;
9
+
10
+ // Check for @RestController
11
+ if (annotations.some((a) => a.name === 'RestController')) {
12
+ return true;
13
+ }
14
+
15
+ // Check for @Controller + @ResponseBody
16
+ const hasController = annotations.some((a) => a.name === 'Controller');
17
+ const hasResponseBody = annotations.some((a) => a.name === 'ResponseBody');
18
+
19
+ return hasController && hasResponseBody;
20
+ }
21
+
22
+ /**
23
+ * Extract base path from @RequestMapping annotation on class level
24
+ */
25
+ static getClassBasePath(javaClass: JavaClass): string {
26
+ const requestMapping = javaClass.annotations.find((a) => a.name === 'RequestMapping');
27
+
28
+ if (!requestMapping) {
29
+ return '';
30
+ }
31
+
32
+ const value = requestMapping.values['value'] || requestMapping.values['path'] || '';
33
+
34
+ if (Array.isArray(value)) {
35
+ return value[0] || '';
36
+ }
37
+
38
+ if (typeof value === 'boolean') {
39
+ return '';
40
+ }
41
+
42
+ return value;
43
+ }
44
+
45
+ /**
46
+ * Check if a method is an endpoint (has HTTP method annotation)
47
+ */
48
+ static isEndpointMethod(method: JavaMethod): boolean {
49
+ const httpAnnotations = [
50
+ 'GetMapping',
51
+ 'PostMapping',
52
+ 'PutMapping',
53
+ 'DeleteMapping',
54
+ 'PatchMapping',
55
+ 'RequestMapping',
56
+ ];
57
+
58
+ return method.annotations.some((a) => httpAnnotations.includes(a.name));
59
+ }
60
+
61
+ /**
62
+ * Get HTTP method from annotation
63
+ */
64
+ static getHttpMethod(method: JavaMethod): string | null {
65
+ const mappingAnnotations: Record<string, string> = {
66
+ GetMapping: 'get',
67
+ PostMapping: 'post',
68
+ PutMapping: 'put',
69
+ DeleteMapping: 'delete',
70
+ PatchMapping: 'patch',
71
+ };
72
+
73
+ for (const annotation of method.annotations) {
74
+ if (annotation.name in mappingAnnotations) {
75
+ return mappingAnnotations[annotation.name];
76
+ }
77
+
78
+ if (annotation.name === 'RequestMapping') {
79
+ const methodValue = annotation.values['method'];
80
+ if (methodValue && typeof methodValue !== 'boolean') {
81
+ const methodStr = Array.isArray(methodValue) ? methodValue[0] : methodValue;
82
+ const normalizedMethod = methodStr.toLowerCase().replace('requestmethod.', '');
83
+ return normalizedMethod;
84
+ }
85
+ // Default to GET if no method specified
86
+ return 'get';
87
+ }
88
+ }
89
+
90
+ return null;
91
+ }
92
+
93
+ /**
94
+ * Get endpoint path from method annotation
95
+ */
96
+ static getMethodPath(method: JavaMethod): string {
97
+ const mappingAnnotations = [
98
+ 'GetMapping',
99
+ 'PostMapping',
100
+ 'PutMapping',
101
+ 'DeleteMapping',
102
+ 'PatchMapping',
103
+ 'RequestMapping',
104
+ ];
105
+
106
+ for (const annotation of method.annotations) {
107
+ if (mappingAnnotations.includes(annotation.name)) {
108
+ const value = annotation.values['value'] || annotation.values['path'] || '';
109
+
110
+ if (Array.isArray(value)) {
111
+ return value[0] || '';
112
+ }
113
+
114
+ if (typeof value === 'boolean') {
115
+ return '';
116
+ }
117
+
118
+ return value;
119
+ }
120
+ }
121
+
122
+ return '';
123
+ }
124
+
125
+ /**
126
+ * Check if an annotation indicates a request parameter
127
+ */
128
+ static isParameterAnnotation(annotation: JavaAnnotation): boolean {
129
+ const paramAnnotations = ['RequestParam', 'PathVariable', 'RequestHeader', 'RequestBody'];
130
+ return paramAnnotations.includes(annotation.name);
131
+ }
132
+
133
+ /**
134
+ * Get parameter type (query, path, header, body)
135
+ */
136
+ static getParameterType(annotations: JavaAnnotation[]): 'query' | 'path' | 'header' | 'body' | null {
137
+ for (const annotation of annotations) {
138
+ switch (annotation.name) {
139
+ case 'RequestParam':
140
+ return 'query';
141
+ case 'PathVariable':
142
+ return 'path';
143
+ case 'RequestHeader':
144
+ return 'header';
145
+ case 'RequestBody':
146
+ return 'body';
147
+ }
148
+ }
149
+ return null;
150
+ }
151
+
152
+ /**
153
+ * Check if a type is a SearchParam
154
+ */
155
+ static isSearchParam(type: string): boolean {
156
+ return type === 'SearchParam' || type.endsWith('.SearchParam');
157
+ }
158
+
159
+ /**
160
+ * Check if a type is a PaginatedList
161
+ */
162
+ static isPaginatedList(type: string): boolean {
163
+ return type === 'PaginatedList' || type.startsWith('PaginatedList') || type.endsWith('.PaginatedList');
164
+ }
165
+
166
+ /**
167
+ * Extract generic type from a parameterized type
168
+ */
169
+ static extractGenericType(genericType: string | undefined): string | undefined {
170
+ if (!genericType) return undefined;
171
+
172
+ // Handle nested generics like PaginatedList<UserDTO>
173
+ const match = genericType.match(/^([^<]+)(?:<(.+)>)?$/);
174
+ if (match) {
175
+ return match[1].trim();
176
+ }
177
+
178
+ return genericType;
179
+ }
180
+
181
+ /**
182
+ * Check if method has security annotations
183
+ */
184
+ static hasSecurityAnnotation(method: JavaMethod): boolean {
185
+ const securityAnnotations = ['PreAuthorize', 'PostAuthorize', 'Secured', 'RolesAllowed'];
186
+ return method.annotations.some((a) => securityAnnotations.includes(a.name));
187
+ }
188
+
189
+ /**
190
+ * Get security annotation from method
191
+ */
192
+ static getSecurityAnnotations(method: JavaMethod): JavaAnnotation[] {
193
+ const securityAnnotations = ['PreAuthorize', 'PostAuthorize', 'Secured', 'RolesAllowed'];
194
+ return method.annotations.filter((a) => securityAnnotations.includes(a.name));
195
+ }
196
+
197
+ /**
198
+ * Combine base path with method path
199
+ */
200
+ static combinePaths(basePath: string, methodPath: string): string {
201
+ // Normalize paths
202
+ const normalizedBase = basePath.replace(/\/+$/, '');
203
+ const normalizedMethod = methodPath.replace(/^\/+/, '');
204
+
205
+ if (!normalizedBase && !normalizedMethod) {
206
+ return '/';
207
+ }
208
+
209
+ if (!normalizedBase) {
210
+ return '/' + normalizedMethod;
211
+ }
212
+
213
+ if (!normalizedMethod) {
214
+ return normalizedBase.startsWith('/') ? normalizedBase : '/' + normalizedBase;
215
+ }
216
+
217
+ const combined = `${normalizedBase}/${normalizedMethod}`;
218
+ return combined.startsWith('/') ? combined : '/' + combined;
219
+ }
220
+
221
+ /**
222
+ * Convert Java type to OpenAPI type
223
+ */
224
+ static javaTypeToOpenAPI(javaType: string): { type: string; format?: string } {
225
+ const typeMap: Record<string, { type: string; format?: string }> = {
226
+ // Primitives
227
+ int: { type: 'integer', format: 'int32' },
228
+ long: { type: 'integer', format: 'int64' },
229
+ short: { type: 'integer' },
230
+ byte: { type: 'integer' },
231
+ float: { type: 'number', format: 'float' },
232
+ double: { type: 'number', format: 'double' },
233
+ boolean: { type: 'boolean' },
234
+ char: { type: 'string' },
235
+
236
+ // Wrapper types
237
+ Integer: { type: 'integer', format: 'int32' },
238
+ Long: { type: 'integer', format: 'int64' },
239
+ Short: { type: 'integer' },
240
+ Byte: { type: 'integer' },
241
+ Float: { type: 'number', format: 'float' },
242
+ Double: { type: 'number', format: 'double' },
243
+ Boolean: { type: 'boolean' },
244
+ Character: { type: 'string' },
245
+
246
+ // String types
247
+ String: { type: 'string' },
248
+
249
+ // Date/Time types
250
+ Date: { type: 'string', format: 'date-time' },
251
+ LocalDate: { type: 'string', format: 'date' },
252
+ LocalDateTime: { type: 'string', format: 'date-time' },
253
+ LocalTime: { type: 'string', format: 'time' },
254
+ Instant: { type: 'string', format: 'date-time' },
255
+ ZonedDateTime: { type: 'string', format: 'date-time' },
256
+ OffsetDateTime: { type: 'string', format: 'date-time' },
257
+
258
+ // UUID
259
+ UUID: { type: 'string', format: 'uuid' },
260
+
261
+ // BigDecimal/BigInteger
262
+ BigDecimal: { type: 'number' },
263
+ BigInteger: { type: 'integer' },
264
+
265
+ // Currency
266
+ Currency: { type: 'string' },
267
+
268
+ // Void
269
+ void: { type: 'object' },
270
+ Void: { type: 'object' },
271
+ };
272
+
273
+ // Check if it's an array type
274
+ if (javaType.endsWith('[]')) {
275
+ return { type: 'array' };
276
+ }
277
+
278
+ // Extract simple type name from fully qualified name
279
+ const simpleName = javaType.split('.').pop() || javaType;
280
+
281
+ return typeMap[simpleName] || { type: 'object' };
282
+ }
283
+
284
+ /**
285
+ * Check if a Java type is a primitive or wrapper type
286
+ */
287
+ static isPrimitiveOrWrapper(javaType: string): boolean {
288
+ const primitiveTypes = [
289
+ 'int',
290
+ 'long',
291
+ 'short',
292
+ 'byte',
293
+ 'float',
294
+ 'double',
295
+ 'boolean',
296
+ 'char',
297
+ 'Integer',
298
+ 'Long',
299
+ 'Short',
300
+ 'Byte',
301
+ 'Float',
302
+ 'Double',
303
+ 'Boolean',
304
+ 'Character',
305
+ 'String',
306
+ 'Date',
307
+ 'LocalDate',
308
+ 'LocalDateTime',
309
+ 'LocalTime',
310
+ 'Instant',
311
+ 'ZonedDateTime',
312
+ 'OffsetDateTime',
313
+ 'UUID',
314
+ 'BigDecimal',
315
+ 'BigInteger',
316
+ 'Currency',
317
+ 'void',
318
+ 'Void',
319
+ ];
320
+
321
+ const simpleName = javaType.split('.').pop() || javaType;
322
+ return primitiveTypes.includes(simpleName);
323
+ }
324
+
325
+ /**
326
+ * Check if a type is a collection type
327
+ */
328
+ static isCollectionType(javaType: string): boolean {
329
+ const collectionTypes = ['List', 'Set', 'Collection', 'ArrayList', 'HashSet', 'LinkedList', 'TreeSet'];
330
+ const simpleName = javaType.split('.').pop() || javaType;
331
+ return collectionTypes.includes(simpleName) || javaType.endsWith('[]');
332
+ }
333
+
334
+ /**
335
+ * Check if a type is a Map type
336
+ */
337
+ static isMapType(javaType: string): boolean {
338
+ const mapTypes = ['Map', 'HashMap', 'TreeMap', 'LinkedHashMap', 'ConcurrentHashMap'];
339
+ const simpleName = javaType.split('.').pop() || javaType;
340
+ return mapTypes.includes(simpleName);
341
+ }
342
+
343
+ /**
344
+ * Check if the return type is ResponseEntity
345
+ */
346
+ static isResponseEntity(returnType: string): boolean {
347
+ return returnType === 'ResponseEntity' || returnType.endsWith('.ResponseEntity');
348
+ }
349
+
350
+ /**
351
+ * Generate operation ID from method name
352
+ */
353
+ static generateOperationId(className: string, methodName: string): string {
354
+ // Remove Controller suffix from class name
355
+ const simplifiedClassName = className.replace(/Controller$/, '');
356
+
357
+ // Convert to camelCase
358
+ const operationId = simplifiedClassName.charAt(0).toLowerCase() + simplifiedClassName.slice(1) + '_' + methodName;
359
+
360
+ return operationId;
361
+ }
362
+
363
+ /**
364
+ * Generate summary from method name
365
+ */
366
+ static generateSummary(methodName: string): string {
367
+ // Convert camelCase to words
368
+ const words = methodName.replace(/([A-Z])/g, ' $1').trim();
369
+
370
+ // Capitalize first letter
371
+ return words.charAt(0).toUpperCase() + words.slice(1);
372
+ }
373
+ }