api-tests-coverage 1.0.15 → 1.0.16

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 (118) hide show
  1. package/dist/dashboard/dist/assets/_basePickBy-C2jmWITn.js +1 -0
  2. package/dist/dashboard/dist/assets/_baseUniq-DE6cyzJb.js +1 -0
  3. package/dist/dashboard/dist/assets/arc-B-Q4nGPT.js +1 -0
  4. package/dist/dashboard/dist/assets/architectureDiagram-VXUJARFQ-C_5dqWCI.js +36 -0
  5. package/dist/dashboard/dist/assets/blockDiagram-VD42YOAC-DbGIO6Kt.js +122 -0
  6. package/dist/dashboard/dist/assets/c4Diagram-YG6GDRKO-CAFpcejP.js +10 -0
  7. package/dist/dashboard/dist/assets/channel-Di9el3wE.js +1 -0
  8. package/dist/dashboard/dist/assets/chunk-4BX2VUAB-DY1boKsq.js +1 -0
  9. package/dist/dashboard/dist/assets/chunk-55IACEB6-BSL35gyW.js +1 -0
  10. package/dist/dashboard/dist/assets/chunk-B4BG7PRW-eTDXrKrv.js +165 -0
  11. package/dist/dashboard/dist/assets/chunk-DI55MBZ5-M-8I3jEy.js +220 -0
  12. package/dist/dashboard/dist/assets/chunk-FMBD7UC4-bSA0XiS0.js +15 -0
  13. package/dist/dashboard/dist/assets/chunk-QN33PNHL-BrOIYUBs.js +1 -0
  14. package/dist/dashboard/dist/assets/chunk-QZHKN3VN-CliaQGD4.js +1 -0
  15. package/dist/dashboard/dist/assets/chunk-TZMSLE5B-CyhcxGB1.js +1 -0
  16. package/dist/dashboard/dist/assets/classDiagram-2ON5EDUG-BkGN4Cpz.js +1 -0
  17. package/dist/dashboard/dist/assets/classDiagram-v2-WZHVMYZB-BkGN4Cpz.js +1 -0
  18. package/dist/dashboard/dist/assets/clone-Cvq8JuOb.js +1 -0
  19. package/dist/dashboard/dist/assets/cose-bilkent-S5V4N54A-BUkL7Wtq.js +1 -0
  20. package/dist/dashboard/dist/assets/dagre-6UL2VRFP-B8oEROJc.js +4 -0
  21. package/dist/dashboard/dist/assets/diagram-PSM6KHXK-5uki9Dw8.js +24 -0
  22. package/dist/dashboard/dist/assets/diagram-QEK2KX5R-BRNhmby2.js +43 -0
  23. package/dist/dashboard/dist/assets/diagram-S2PKOQOG-D-ku_X8U.js +24 -0
  24. package/dist/dashboard/dist/assets/erDiagram-Q2GNP2WA-DGl6gPe2.js +60 -0
  25. package/dist/dashboard/dist/assets/flowDiagram-NV44I4VS-Co89qYBD.js +162 -0
  26. package/dist/dashboard/dist/assets/ganttDiagram-JELNMOA3-2r3WpWQC.js +267 -0
  27. package/dist/dashboard/dist/assets/gitGraphDiagram-V2S2FVAM-CuJ5l3TK.js +65 -0
  28. package/dist/dashboard/dist/assets/graph-ZtgwAPQj.js +1 -0
  29. package/dist/dashboard/dist/assets/index-D3sRJga7.js +777 -0
  30. package/dist/dashboard/dist/assets/infoDiagram-HS3SLOUP-ujnMqVz3.js +2 -0
  31. package/dist/dashboard/dist/assets/journeyDiagram-XKPGCS4Q-DQzfeBIo.js +139 -0
  32. package/dist/dashboard/dist/assets/kanban-definition-3W4ZIXB7-ueIaoeks.js +89 -0
  33. package/dist/dashboard/dist/assets/layout-B1fTYUMj.js +1 -0
  34. package/dist/dashboard/dist/assets/mindmap-definition-VGOIOE7T-B7wYeLe1.js +68 -0
  35. package/dist/dashboard/dist/assets/pieDiagram-ADFJNKIX-Bf8vKEOf.js +30 -0
  36. package/dist/dashboard/dist/assets/quadrantDiagram-AYHSOK5B-CM8qiFLR.js +7 -0
  37. package/dist/dashboard/dist/assets/requirementDiagram-UZGBJVZJ-DPTtP4Ve.js +64 -0
  38. package/dist/dashboard/dist/assets/sankeyDiagram-TZEHDZUN-DEVTdH0h.js +10 -0
  39. package/dist/dashboard/dist/assets/sequenceDiagram-WL72ISMW-Bjr5wgXg.js +145 -0
  40. package/dist/dashboard/dist/assets/stateDiagram-FKZM4ZOC-DDrhZYly.js +1 -0
  41. package/dist/dashboard/dist/assets/stateDiagram-v2-4FDKWEC3-Im6pH8C-.js +1 -0
  42. package/dist/dashboard/dist/assets/timeline-definition-IT6M3QCI-DAT3r9va.js +61 -0
  43. package/dist/dashboard/dist/assets/treemap-GDKQZRPO-BlA8rg0m.js +162 -0
  44. package/dist/dashboard/dist/assets/xychartDiagram-PRI3JC2R-7aSkQtVu.js +7 -0
  45. package/dist/dashboard/dist/index.html +1 -1
  46. package/dist/src/ast/astTypes.d.ts +86 -0
  47. package/dist/src/ast/astTypes.d.ts.map +1 -1
  48. package/dist/src/discovery/frameworkDetector.d.ts +28 -0
  49. package/dist/src/discovery/frameworkDetector.d.ts.map +1 -0
  50. package/dist/src/discovery/frameworkDetector.js +189 -0
  51. package/dist/src/discovery/projectDiscovery.d.ts +5 -1
  52. package/dist/src/discovery/projectDiscovery.d.ts.map +1 -1
  53. package/dist/src/discovery/projectDiscovery.js +4 -0
  54. package/dist/src/inference/routeInference.d.ts.map +1 -1
  55. package/dist/src/inference/routeInference.js +224 -1
  56. package/dist/src/languages/java/graphqlSchemaParser.d.ts +65 -0
  57. package/dist/src/languages/java/graphqlSchemaParser.d.ts.map +1 -0
  58. package/dist/src/languages/java/graphqlSchemaParser.js +164 -0
  59. package/dist/src/languages/java/mybatisXmlParser.d.ts +52 -0
  60. package/dist/src/languages/java/mybatisXmlParser.d.ts.map +1 -0
  61. package/dist/src/languages/java/mybatisXmlParser.js +107 -0
  62. package/dist/src/languages/javascript/angularDetector.d.ts +74 -0
  63. package/dist/src/languages/javascript/angularDetector.d.ts.map +1 -0
  64. package/dist/src/languages/javascript/angularDetector.js +194 -0
  65. package/dist/src/languages/javascript/hapiDetector.d.ts +40 -0
  66. package/dist/src/languages/javascript/hapiDetector.d.ts.map +1 -0
  67. package/dist/src/languages/javascript/hapiDetector.js +131 -0
  68. package/dist/src/languages/javascript/mongooseDetector.d.ts +65 -0
  69. package/dist/src/languages/javascript/mongooseDetector.d.ts.map +1 -0
  70. package/dist/src/languages/javascript/mongooseDetector.js +237 -0
  71. package/dist/src/languages/javascript/vueDetector.d.ts +40 -0
  72. package/dist/src/languages/javascript/vueDetector.d.ts.map +1 -0
  73. package/dist/src/languages/javascript/vueDetector.js +87 -0
  74. package/dist/src/languages/python/index.d.ts +5 -1
  75. package/dist/src/languages/python/index.d.ts.map +1 -1
  76. package/dist/src/languages/python/index.js +167 -2
  77. package/dist/src/languages/python/testPatternDetector.d.ts +70 -0
  78. package/dist/src/languages/python/testPatternDetector.d.ts.map +1 -0
  79. package/dist/src/languages/python/testPatternDetector.js +201 -0
  80. package/dist/src/pipeline/stages/ast/astStage.d.ts.map +1 -1
  81. package/dist/src/pipeline/stages/ast/astStage.js +6 -0
  82. package/dist/src/pipeline/stages/ast/baseUrlComposer.d.ts +44 -0
  83. package/dist/src/pipeline/stages/ast/baseUrlComposer.d.ts.map +1 -0
  84. package/dist/src/pipeline/stages/ast/baseUrlComposer.js +83 -0
  85. package/dist/src/pipeline/stages/ast/crossFileResolutionPass.d.ts +54 -0
  86. package/dist/src/pipeline/stages/ast/crossFileResolutionPass.d.ts.map +1 -0
  87. package/dist/src/pipeline/stages/ast/crossFileResolutionPass.js +88 -0
  88. package/dist/src/pipeline/stages/ast/crossFileResolver.d.ts.map +1 -1
  89. package/dist/src/pipeline/stages/ast/crossFileResolver.js +10 -1
  90. package/dist/src/pipeline/stages/ast/optionalAuthUnifier.d.ts +39 -0
  91. package/dist/src/pipeline/stages/ast/optionalAuthUnifier.d.ts.map +1 -0
  92. package/dist/src/pipeline/stages/ast/optionalAuthUnifier.js +81 -0
  93. package/dist/src/pipeline/stages/ast/resolvers/angularInjectionResolver.d.ts +18 -0
  94. package/dist/src/pipeline/stages/ast/resolvers/angularInjectionResolver.d.ts.map +1 -0
  95. package/dist/src/pipeline/stages/ast/resolvers/angularInjectionResolver.js +77 -0
  96. package/dist/src/pipeline/stages/ast/resolvers/dddLayerResolver.d.ts +46 -0
  97. package/dist/src/pipeline/stages/ast/resolvers/dddLayerResolver.d.ts.map +1 -0
  98. package/dist/src/pipeline/stages/ast/resolvers/dddLayerResolver.js +238 -0
  99. package/dist/src/pipeline/stages/ast/resolvers/expressRouterResolver.d.ts +17 -0
  100. package/dist/src/pipeline/stages/ast/resolvers/expressRouterResolver.d.ts.map +1 -0
  101. package/dist/src/pipeline/stages/ast/resolvers/expressRouterResolver.js +65 -0
  102. package/dist/src/pipeline/stages/ast/resolvers/flaskBlueprintResolver.d.ts +17 -0
  103. package/dist/src/pipeline/stages/ast/resolvers/flaskBlueprintResolver.d.ts.map +1 -0
  104. package/dist/src/pipeline/stages/ast/resolvers/flaskBlueprintResolver.js +114 -0
  105. package/dist/src/pipeline/stages/ast/resolvers/mybatisResolver.d.ts +27 -0
  106. package/dist/src/pipeline/stages/ast/resolvers/mybatisResolver.d.ts.map +1 -0
  107. package/dist/src/pipeline/stages/ast/resolvers/mybatisResolver.js +74 -0
  108. package/dist/src/pipeline/stages/ast/resolvers/vuexActionResolver.d.ts +17 -0
  109. package/dist/src/pipeline/stages/ast/resolvers/vuexActionResolver.d.ts.map +1 -0
  110. package/dist/src/pipeline/stages/ast/resolvers/vuexActionResolver.js +55 -0
  111. package/dist/src/pipeline/stages/ast/rulesEnforcer.d.ts +24 -0
  112. package/dist/src/pipeline/stages/ast/rulesEnforcer.d.ts.map +1 -0
  113. package/dist/src/pipeline/stages/ast/rulesEnforcer.js +120 -0
  114. package/dist/src/pipeline/stages/ast/types.d.ts +114 -1
  115. package/dist/src/pipeline/stages/ast/types.d.ts.map +1 -1
  116. package/dist/src/pipeline/stages/tia/testLayerClassifier.d.ts.map +1 -1
  117. package/dist/src/pipeline/stages/tia/testLayerClassifier.js +5 -0
  118. package/package.json +1 -1
@@ -134,6 +134,12 @@ function inferRoutesFromFile(filePath) {
134
134
  if (ext === '.java' || ext === '.kt' || ext === '.kts') {
135
135
  return inferRoutesFromJavaFile(filePath);
136
136
  }
137
+ if (ext === '.py') {
138
+ return inferRoutesFromPythonFile(filePath);
139
+ }
140
+ if (ext === '.php') {
141
+ return inferRoutesFromPhpFile(filePath);
142
+ }
137
143
  let content;
138
144
  try {
139
145
  content = fs.readFileSync(filePath, 'utf-8');
@@ -203,7 +209,17 @@ function inferRoutesFromFile(filePath) {
203
209
  addRoute(method, routePath, lineIdx + 1, 'code');
204
210
  }
205
211
  }
206
- // 4. AngularJS / fetch / axios — { url: base + '/path', method: 'GET' }
212
+ // 4. HapiJS server.route({ method: 'GET', path: '/path', ... })
213
+ const hapiMethodMatch = line.match(/method\s*:\s*['"](\w+)['"]/);
214
+ const hapiPathMatch = line.match(/path\s*:\s*['"]([^'"]+)['"]/);
215
+ if (hapiMethodMatch && hapiPathMatch) {
216
+ const method = hapiMethodMatch[1].toLowerCase();
217
+ if (exports.HTTP_METHODS.includes(method)) {
218
+ addRoute(method, hapiPathMatch[1], lineIdx + 1, 'code');
219
+ continue;
220
+ }
221
+ }
222
+ // 5. AngularJS / fetch / axios — { url: base + '/path', method: 'GET' }
207
223
  // Only active when the file contains an HTTP-client signal.
208
224
  if (usesHttpClient) {
209
225
  const httpMethodKey = line.match(ANGULAR_HTTP_METHOD_KEY);
@@ -259,6 +275,22 @@ const SPRING_CLASS_BASE_PATH = /@RequestMapping\s*\(\s*(?:path\s*=\s*)?["']([^"'
259
275
  const SPRING_METHOD_MAPPING = /@(Get|Post|Put|Patch|Delete)Mapping(?:\s*\(\s*(?:path\s*=\s*)?["']([^"']*)["'])?/i;
260
276
  /** Matches @RequestMapping with explicit method= (used when special HTTP method needed) */
261
277
  const SPRING_REQUEST_MAPPING_EXPLICIT = /@RequestMapping\s*\((?:[^)]*?\bpath\s*=\s*["']([^"']+)["'][^)]*?\bmethod\s*=\s*(?:RequestMethod\.)?([A-Z]+)|(?:[^)]*?\bmethod\s*=\s*(?:RequestMethod\.)?([A-Z]+)[^)]*?\bpath\s*=\s*["']([^"']+)["']))/;
278
+ /**
279
+ * Matches DGS @DgsQuery and @DgsMutation annotations.
280
+ * @DgsQuery → Query field (method name = field name)
281
+ * @DgsQuery(field = "articles") → Query field "articles"
282
+ * @DgsMutation(field = "createArticle") → Mutation field "createArticle"
283
+ */
284
+ const DGS_QUERY = /@DgsQuery(?:\s*\(\s*(?:field\s*=\s*)?["']?(\w+)["']?\s*\))?/;
285
+ const DGS_MUTATION = /@DgsMutation(?:\s*\(\s*(?:field\s*=\s*)?["']?(\w+)["']?\s*\))?/;
286
+ /**
287
+ * Matches Spring GraphQL @QueryMapping and @MutationMapping.
288
+ * @QueryMapping → Query (method name)
289
+ * @QueryMapping("articles") → Query "articles"
290
+ * @MutationMapping("createArticle") → Mutation "createArticle"
291
+ */
292
+ const SPRING_QUERY_MAPPING = /@QueryMapping(?:\s*\(\s*["']?(\w+)["']?\s*\))?/;
293
+ const SPRING_MUTATION_MAPPING = /@MutationMapping(?:\s*\(\s*["']?(\w+)["']?\s*\))?/;
262
294
  /**
263
295
  * Infer Spring routes from a Java or Kotlin source file.
264
296
  * Works in two passes:
@@ -318,6 +350,197 @@ function inferRoutesFromJavaFile(filePath) {
318
350
  addRoute(rawMethod, fullPath, i + 1);
319
351
  }
320
352
  }
353
+ // DGS @DgsQuery / @DgsMutation — detect as POST /graphql (convention)
354
+ const dgsQ = line.match(DGS_QUERY);
355
+ if (dgsQ) {
356
+ const fieldName = dgsQ[1] || findNextMethodNameInJava(lines, i);
357
+ if (fieldName) {
358
+ addRoute('post', '/graphql', i + 1);
359
+ }
360
+ continue;
361
+ }
362
+ const dgsM = line.match(DGS_MUTATION);
363
+ if (dgsM) {
364
+ const fieldName = dgsM[1] || findNextMethodNameInJava(lines, i);
365
+ if (fieldName) {
366
+ addRoute('post', '/graphql', i + 1);
367
+ }
368
+ continue;
369
+ }
370
+ // Spring GraphQL @QueryMapping / @MutationMapping — detect as POST /graphql
371
+ const sqm = line.match(SPRING_QUERY_MAPPING);
372
+ if (sqm) {
373
+ addRoute('post', '/graphql', i + 1);
374
+ continue;
375
+ }
376
+ const smm = line.match(SPRING_MUTATION_MAPPING);
377
+ if (smm) {
378
+ addRoute('post', '/graphql', i + 1);
379
+ continue;
380
+ }
381
+ }
382
+ return [...byKey.values()];
383
+ }
384
+ /** Find the next method name after an annotation line (for DGS/GraphQL) */
385
+ function findNextMethodNameInJava(javaLines, fromLine) {
386
+ for (let j = fromLine + 1; j < Math.min(fromLine + 5, javaLines.length); j++) {
387
+ const methodMatch = javaLines[j].match(/(?:public|private|protected|fun)\s+\S+\s+(\w+)\s*\(/);
388
+ if (methodMatch)
389
+ return methodMatch[1];
390
+ const kotlinMatch = javaLines[j].match(/fun\s+(\w+)\s*\(/);
391
+ if (kotlinMatch)
392
+ return kotlinMatch[1];
393
+ }
394
+ return undefined;
395
+ }
396
+ // ─── Python / Flask / FastAPI inference ───────────────────────────────────────
397
+ /**
398
+ * Matches Flask @app.route() and @blueprint.route() decorators.
399
+ * @app.route('/articles', methods=['GET', 'POST'])
400
+ * @blueprint.route('/articles/<slug>', methods=['PUT'])
401
+ * @app.route('/tags') ← defaults to GET
402
+ */
403
+ const FLASK_ROUTE = /@(\w+)\.route\s*\(\s*['"]([^'"]+)['"](?:\s*,\s*methods\s*=\s*\[([^\]]+)\])?\s*\)/;
404
+ /**
405
+ * Matches FastAPI decorators.
406
+ * @app.get('/articles')
407
+ * @router.post('/users')
408
+ * @app.delete('/articles/{slug}')
409
+ */
410
+ const FASTAPI_ROUTE = /@(\w+)\.(get|post|put|patch|delete|head|options)\s*\(\s*['"]([^'"]+)['"]/i;
411
+ /**
412
+ * Matches Flask Blueprint constructor.
413
+ * articles = Blueprint('articles', __name__)
414
+ * bp = Blueprint("users", __name__, url_prefix="/users")
415
+ */
416
+ const FLASK_BLUEPRINT_CTOR = /(\w+)\s*=\s*Blueprint\s*\(\s*['"][^'"]+['"](?:\s*,\s*[^,)]+)*?(?:\s*,\s*url_prefix\s*=\s*['"]([^'"]+)['"])?\s*\)/;
417
+ /**
418
+ * Matches FastAPI APIRouter constructor with prefix.
419
+ * router = APIRouter(prefix="/articles")
420
+ */
421
+ const FASTAPI_APIROUTER = /(\w+)\s*=\s*APIRouter\s*\([^)]*?prefix\s*=\s*['"]([^'"]+)['"]/;
422
+ /**
423
+ * Infer routes from a Python (Flask/FastAPI) source file.
424
+ */
425
+ function inferRoutesFromPythonFile(filePath) {
426
+ let content;
427
+ try {
428
+ content = fs.readFileSync(filePath, 'utf-8');
429
+ }
430
+ catch {
431
+ return [];
432
+ }
433
+ const lines = content.split('\n');
434
+ const byKey = new Map();
435
+ const addRoute = (method, routePath, lineNumber) => {
436
+ const key = `${method}:${routePath}`;
437
+ if (!byKey.has(key)) {
438
+ byKey.set(key, { method, path: routePath, sourceFile: filePath, lineNumber, discoveredVia: 'code' });
439
+ }
440
+ };
441
+ // Pass 1: Detect blueprint/router prefix for the file
442
+ let localPrefix = '';
443
+ for (const line of lines) {
444
+ const bpMatch = line.match(FLASK_BLUEPRINT_CTOR);
445
+ if (bpMatch && bpMatch[2]) {
446
+ localPrefix = bpMatch[2];
447
+ break;
448
+ }
449
+ const arMatch = line.match(FASTAPI_APIROUTER);
450
+ if (arMatch && arMatch[2]) {
451
+ localPrefix = arMatch[2];
452
+ break;
453
+ }
454
+ }
455
+ // Pass 2: Detect routes
456
+ for (let i = 0; i < lines.length; i++) {
457
+ const line = lines[i];
458
+ // Flask @xxx.route('/path', methods=[...])
459
+ const flaskMatch = line.match(FLASK_ROUTE);
460
+ if (flaskMatch) {
461
+ const routePath = flaskMatch[2];
462
+ const methodsList = flaskMatch[3];
463
+ const fullPath = localPrefix + (routePath.startsWith('/') ? routePath : '/' + routePath);
464
+ // Normalize Flask <param> to {param}
465
+ const normalizedPath = fullPath.replace(/<(?:\w+:)?(\w+)>/g, '{$1}');
466
+ if (methodsList) {
467
+ // Parse methods=['GET', 'POST'] → individual routes
468
+ const methods = methodsList.replace(/['"]/g, '').split(',').map((m) => m.trim().toLowerCase());
469
+ for (const m of methods) {
470
+ if (exports.HTTP_METHODS.includes(m)) {
471
+ addRoute(m, normalizedPath, i + 1);
472
+ }
473
+ }
474
+ }
475
+ else {
476
+ // Default to GET
477
+ addRoute('get', normalizedPath, i + 1);
478
+ }
479
+ continue;
480
+ }
481
+ // FastAPI @xxx.get('/path'), @xxx.post('/path'), etc.
482
+ const fastapiMatch = line.match(FASTAPI_ROUTE);
483
+ if (fastapiMatch) {
484
+ const httpMethod = fastapiMatch[2].toLowerCase();
485
+ const routePath = fastapiMatch[3];
486
+ const fullPath = localPrefix + (routePath.startsWith('/') ? routePath : '/' + routePath);
487
+ // FastAPI uses {param} natively
488
+ addRoute(httpMethod, fullPath, i + 1);
489
+ continue;
490
+ }
491
+ }
492
+ return [...byKey.values()];
493
+ }
494
+ // ─── PHP / Slim inference ─────────────────────────────────────────────────────
495
+ /**
496
+ * Matches Slim PHP route definitions:
497
+ * $app->get('/articles', Controller::class . ':method')
498
+ * $app->post('/articles/{slug}', 'Controller:method')
499
+ * $group->get('/articles', ArticleController::class . ':list')
500
+ */
501
+ const SLIM_ROUTE = /\$\w+->(get|post|put|patch|delete|options|head)\s*\(\s*['"]([^'"]+)['"]/i;
502
+ /**
503
+ * Matches Slim group definitions:
504
+ * $app->group('/api', function($group) { ... })
505
+ * $app->group('/api/articles', function(RouteCollectorProxy $group) { ... })
506
+ */
507
+ const SLIM_GROUP = /\$\w+->group\s*\(\s*['"]([^'"]+)['"]/;
508
+ function inferRoutesFromPhpFile(filePath) {
509
+ let content;
510
+ try {
511
+ content = fs.readFileSync(filePath, 'utf-8');
512
+ }
513
+ catch {
514
+ return [];
515
+ }
516
+ const lines = content.split('\n');
517
+ const byKey = new Map();
518
+ const addRoute = (method, routePath, lineNumber) => {
519
+ const key = `${method}:${routePath}`;
520
+ if (!byKey.has(key)) {
521
+ byKey.set(key, { method, path: routePath, sourceFile: filePath, lineNumber, discoveredVia: 'code' });
522
+ }
523
+ };
524
+ // Track group prefixes
525
+ let groupPrefix = '';
526
+ for (let i = 0; i < lines.length; i++) {
527
+ const line = lines[i];
528
+ // Group prefix
529
+ const groupMatch = line.match(SLIM_GROUP);
530
+ if (groupMatch) {
531
+ groupPrefix = groupMatch[1];
532
+ continue;
533
+ }
534
+ // Route definition
535
+ const routeMatch = line.match(SLIM_ROUTE);
536
+ if (routeMatch) {
537
+ const method = routeMatch[1].toLowerCase();
538
+ const path = routeMatch[2];
539
+ const fullPath = groupPrefix
540
+ ? groupPrefix + (path.startsWith('/') ? path : '/' + path)
541
+ : path;
542
+ addRoute(method, fullPath, i + 1);
543
+ }
321
544
  }
322
545
  return [...byKey.values()];
323
546
  }
@@ -0,0 +1,65 @@
1
+ /**
2
+ * GraphQL schema parser (Feature 27, Sub-PR 5)
3
+ *
4
+ * Parses .graphqls and .graphql schema files to extract:
5
+ * 1. type Query { ... } fields as endpoint nodes with protocol: graphql
6
+ * 2. type Mutation { ... } fields as endpoint nodes with protocol: graphql
7
+ * 3. input types as parameter definitions
8
+ *
9
+ * Also detects DGS data fetcher annotations: @DgsQuery, @DgsMutation, @DgsData
10
+ */
11
+ export interface GraphqlField {
12
+ /** The field name (e.g. 'articles', 'createArticle') */
13
+ name: string;
14
+ /** 'query' or 'mutation' or 'subscription' */
15
+ operationType: 'query' | 'mutation' | 'subscription';
16
+ /** Return type as written in the schema */
17
+ returnType: string;
18
+ /** Arguments with their types */
19
+ arguments: Array<{
20
+ name: string;
21
+ type: string;
22
+ }>;
23
+ line?: number;
24
+ }
25
+ export interface GraphqlInputType {
26
+ name: string;
27
+ fields: Array<{
28
+ name: string;
29
+ type: string;
30
+ }>;
31
+ line?: number;
32
+ }
33
+ export interface GraphqlSchema {
34
+ filePath: string;
35
+ queries: GraphqlField[];
36
+ mutations: GraphqlField[];
37
+ subscriptions: GraphqlField[];
38
+ inputTypes: GraphqlInputType[];
39
+ }
40
+ /**
41
+ * Parse a .graphqls or .graphql schema file.
42
+ * Uses targeted regex parsing for the well-structured format.
43
+ */
44
+ export declare function parseGraphqlSchema(content: string, filePath: string): GraphqlSchema;
45
+ export interface DgsDataFetcher {
46
+ /** Java/Kotlin method name */
47
+ methodName: string;
48
+ /** The annotation type: @DgsQuery, @DgsMutation, @DgsData, etc. */
49
+ annotation: string;
50
+ /** The parent type (from @DgsData parentType=) or implicit from @DgsQuery/@DgsMutation */
51
+ parentType: string;
52
+ /** The field name (from annotation value or method name) */
53
+ fieldName: string;
54
+ sourceFile: string;
55
+ line?: number;
56
+ }
57
+ /**
58
+ * Detect DGS data fetcher annotations from Java/Kotlin source.
59
+ */
60
+ export declare function detectDgsAnnotations(sourceContent: string, filePath: string): DgsDataFetcher[];
61
+ /**
62
+ * Check if a file is a GraphQL schema file.
63
+ */
64
+ export declare function isGraphqlSchemaFile(filePath: string): boolean;
65
+ //# sourceMappingURL=graphqlSchemaParser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graphqlSchemaParser.d.ts","sourceRoot":"","sources":["../../../../src/languages/java/graphqlSchemaParser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,MAAM,WAAW,YAAY;IAC3B,wDAAwD;IACxD,IAAI,EAAE,MAAM,CAAC;IACb,8CAA8C;IAC9C,aAAa,EAAE,OAAO,GAAG,UAAU,GAAG,cAAc,CAAC;IACrD,2CAA2C;IAC3C,UAAU,EAAE,MAAM,CAAC;IACnB,iCAAiC;IACjC,SAAS,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACjD,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC9C,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,SAAS,EAAE,YAAY,EAAE,CAAC;IAC1B,aAAa,EAAE,YAAY,EAAE,CAAC;IAC9B,UAAU,EAAE,gBAAgB,EAAE,CAAC;CAChC;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,aAAa,CAOnF;AAkFD,MAAM,WAAW,cAAc;IAC7B,8BAA8B;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,mEAAmE;IACnE,UAAU,EAAE,MAAM,CAAC;IACnB,0FAA0F;IAC1F,UAAU,EAAE,MAAM,CAAC;IACnB,4DAA4D;IAC5D,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,aAAa,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,cAAc,EAAE,CA2D9F;AAcD;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAE7D"}
@@ -0,0 +1,164 @@
1
+ "use strict";
2
+ /**
3
+ * GraphQL schema parser (Feature 27, Sub-PR 5)
4
+ *
5
+ * Parses .graphqls and .graphql schema files to extract:
6
+ * 1. type Query { ... } fields as endpoint nodes with protocol: graphql
7
+ * 2. type Mutation { ... } fields as endpoint nodes with protocol: graphql
8
+ * 3. input types as parameter definitions
9
+ *
10
+ * Also detects DGS data fetcher annotations: @DgsQuery, @DgsMutation, @DgsData
11
+ */
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.parseGraphqlSchema = parseGraphqlSchema;
14
+ exports.detectDgsAnnotations = detectDgsAnnotations;
15
+ exports.isGraphqlSchemaFile = isGraphqlSchemaFile;
16
+ /**
17
+ * Parse a .graphqls or .graphql schema file.
18
+ * Uses targeted regex parsing for the well-structured format.
19
+ */
20
+ function parseGraphqlSchema(content, filePath) {
21
+ const queries = extractTypeFields(content, 'Query', 'query');
22
+ const mutations = extractTypeFields(content, 'Mutation', 'mutation');
23
+ const subscriptions = extractTypeFields(content, 'Subscription', 'subscription');
24
+ const inputTypes = extractInputTypes(content);
25
+ return { filePath, queries, mutations, subscriptions, inputTypes };
26
+ }
27
+ function extractTypeFields(content, typeName, operationType) {
28
+ const fields = [];
29
+ // Match type Query { ... } or extend type Query { ... }
30
+ const typePattern = new RegExp(`(?:extend\\s+)?type\\s+${typeName}\\s*\\{([\\s\\S]*?)\\}`, 'g');
31
+ let typeMatch;
32
+ while ((typeMatch = typePattern.exec(content)) !== null) {
33
+ const body = typeMatch[1];
34
+ const bodyStartLine = content.substring(0, typeMatch.index).split('\n').length;
35
+ const lines = body.split('\n');
36
+ for (let i = 0; i < lines.length; i++) {
37
+ const line = lines[i].trim();
38
+ if (!line || line.startsWith('#'))
39
+ continue;
40
+ // Field pattern: fieldName(arg1: Type1, arg2: Type2): ReturnType
41
+ const fieldMatch = line.match(/^(\w+)\s*(?:\(([^)]*)\))?\s*:\s*(.+?)(?:\s*@.*)?$/);
42
+ if (fieldMatch) {
43
+ const args = [];
44
+ if (fieldMatch[2]) {
45
+ const argParts = fieldMatch[2].split(',');
46
+ for (const part of argParts) {
47
+ const argMatch = part.trim().match(/(\w+)\s*:\s*(.+)/);
48
+ if (argMatch) {
49
+ args.push({ name: argMatch[1], type: argMatch[2].trim() });
50
+ }
51
+ }
52
+ }
53
+ fields.push({
54
+ name: fieldMatch[1],
55
+ operationType,
56
+ returnType: fieldMatch[3].trim(),
57
+ arguments: args,
58
+ line: bodyStartLine + i,
59
+ });
60
+ }
61
+ }
62
+ }
63
+ return fields;
64
+ }
65
+ function extractInputTypes(content) {
66
+ const inputs = [];
67
+ const inputPattern = /input\s+(\w+)\s*\{([\s\S]*?)\}/g;
68
+ let match;
69
+ while ((match = inputPattern.exec(content)) !== null) {
70
+ const name = match[1];
71
+ const body = match[2];
72
+ const line = content.substring(0, match.index).split('\n').length;
73
+ const fields = [];
74
+ const fieldLines = body.split('\n');
75
+ for (const fl of fieldLines) {
76
+ const trimmed = fl.trim();
77
+ if (!trimmed || trimmed.startsWith('#'))
78
+ continue;
79
+ const fieldMatch = trimmed.match(/(\w+)\s*:\s*(.+)/);
80
+ if (fieldMatch) {
81
+ fields.push({ name: fieldMatch[1], type: fieldMatch[2].trim() });
82
+ }
83
+ }
84
+ inputs.push({ name, fields, line });
85
+ }
86
+ return inputs;
87
+ }
88
+ /**
89
+ * Detect DGS data fetcher annotations from Java/Kotlin source.
90
+ */
91
+ function detectDgsAnnotations(sourceContent, filePath) {
92
+ const fetchers = [];
93
+ const lines = sourceContent.split('\n');
94
+ for (let i = 0; i < lines.length; i++) {
95
+ const line = lines[i];
96
+ // @DgsQuery or @DgsQuery(field = "articles")
97
+ const queryMatch = line.match(/@DgsQuery(?:\s*\(\s*(?:field\s*=\s*)?["']?(\w+)["']?\s*\))?/);
98
+ if (queryMatch) {
99
+ const methodName = findNextMethodName(lines, i);
100
+ if (methodName) {
101
+ fetchers.push({
102
+ methodName,
103
+ annotation: '@DgsQuery',
104
+ parentType: 'Query',
105
+ fieldName: queryMatch[1] || methodName,
106
+ sourceFile: filePath,
107
+ line: i + 1,
108
+ });
109
+ }
110
+ continue;
111
+ }
112
+ // @DgsMutation
113
+ const mutationMatch = line.match(/@DgsMutation(?:\s*\(\s*(?:field\s*=\s*)?["']?(\w+)["']?\s*\))?/);
114
+ if (mutationMatch) {
115
+ const methodName = findNextMethodName(lines, i);
116
+ if (methodName) {
117
+ fetchers.push({
118
+ methodName,
119
+ annotation: '@DgsMutation',
120
+ parentType: 'Mutation',
121
+ fieldName: mutationMatch[1] || methodName,
122
+ sourceFile: filePath,
123
+ line: i + 1,
124
+ });
125
+ }
126
+ continue;
127
+ }
128
+ // @DgsData(parentType = "Article", field = "author")
129
+ const dataMatch = line.match(/@DgsData\s*\(\s*parentType\s*=\s*["'](\w+)["'](?:\s*,\s*field\s*=\s*["'](\w+)["'])?\s*\)/);
130
+ if (dataMatch) {
131
+ const methodName = findNextMethodName(lines, i);
132
+ if (methodName) {
133
+ fetchers.push({
134
+ methodName,
135
+ annotation: '@DgsData',
136
+ parentType: dataMatch[1],
137
+ fieldName: dataMatch[2] || methodName,
138
+ sourceFile: filePath,
139
+ line: i + 1,
140
+ });
141
+ }
142
+ }
143
+ }
144
+ return fetchers;
145
+ }
146
+ function findNextMethodName(lines, fromLine) {
147
+ for (let j = fromLine + 1; j < Math.min(fromLine + 5, lines.length); j++) {
148
+ // Java: public List<Article> articles(...) or fun articles(...)
149
+ const methodMatch = lines[j].match(/(?:public|private|protected|fun)\s+\S+\s+(\w+)\s*\(/);
150
+ if (methodMatch)
151
+ return methodMatch[1];
152
+ // Kotlin shorthand: fun articles(
153
+ const kotlinMatch = lines[j].match(/fun\s+(\w+)\s*\(/);
154
+ if (kotlinMatch)
155
+ return kotlinMatch[1];
156
+ }
157
+ return undefined;
158
+ }
159
+ /**
160
+ * Check if a file is a GraphQL schema file.
161
+ */
162
+ function isGraphqlSchemaFile(filePath) {
163
+ return /\.graphqls?$/.test(filePath);
164
+ }
@@ -0,0 +1,52 @@
1
+ /**
2
+ * MyBatis XML mapper parser (Feature 27, Sub-PR 5)
3
+ *
4
+ * Parses MyBatis XML mapper files to extract:
5
+ * 1. <mapper namespace="..."> — linking to Java @Mapper interface
6
+ * 2. <select>, <insert>, <update>, <delete> — as repository-query nodes
7
+ * 3. <if>, <choose>/<when>/<otherwise>, <foreach> — as conditional branch nodes
8
+ * 4. <resultMap>, <association> — as DB schema evidence
9
+ */
10
+ export interface MyBatisQuery {
11
+ /** The SQL operation id (method name in the mapper interface) */
12
+ id: string;
13
+ /** SQL operation type */
14
+ type: 'select' | 'insert' | 'update' | 'delete';
15
+ /** The result type if specified */
16
+ resultType?: string;
17
+ /** The result map id if specified */
18
+ resultMap?: string;
19
+ /** The parameter type if specified */
20
+ parameterType?: string;
21
+ /** Whether the query has conditional branches (<if>, <choose>) */
22
+ hasConditionals: boolean;
23
+ /** Number of conditional branches */
24
+ conditionalCount: number;
25
+ line?: number;
26
+ }
27
+ export interface MyBatisResultMap {
28
+ id: string;
29
+ type: string;
30
+ associations: string[];
31
+ collections: string[];
32
+ }
33
+ export interface MyBatisMapperFile {
34
+ /** Java interface FQCN from namespace attribute */
35
+ namespace: string;
36
+ /** Path to the XML file */
37
+ filePath: string;
38
+ /** Parsed queries */
39
+ queries: MyBatisQuery[];
40
+ /** Parsed result maps */
41
+ resultMaps: MyBatisResultMap[];
42
+ }
43
+ /**
44
+ * Parse a MyBatis mapper XML file.
45
+ * Uses regex-based parsing since MyBatis XML is highly structured.
46
+ */
47
+ export declare function parseMyBatisMapper(xmlContent: string, filePath: string): MyBatisMapperFile | null;
48
+ /**
49
+ * Check if a file is a MyBatis XML mapper.
50
+ */
51
+ export declare function isMyBatisMapperXml(content: string): boolean;
52
+ //# sourceMappingURL=mybatisXmlParser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mybatisXmlParser.d.ts","sourceRoot":"","sources":["../../../../src/languages/java/mybatisXmlParser.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,WAAW,YAAY;IAC3B,iEAAiE;IACjE,EAAE,EAAE,MAAM,CAAC;IACX,yBAAyB;IACzB,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC;IAChD,mCAAmC;IACnC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qCAAqC;IACrC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,sCAAsC;IACtC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,kEAAkE;IAClE,eAAe,EAAE,OAAO,CAAC;IACzB,qCAAqC;IACrC,gBAAgB,EAAE,MAAM,CAAC;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,iBAAiB;IAChC,mDAAmD;IACnD,SAAS,EAAE,MAAM,CAAC;IAClB,2BAA2B;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,qBAAqB;IACrB,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,yBAAyB;IACzB,UAAU,EAAE,gBAAgB,EAAE,CAAC;CAChC;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,iBAAiB,GAAG,IAAI,CAUjG;AA0FD;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAI3D"}
@@ -0,0 +1,107 @@
1
+ "use strict";
2
+ /**
3
+ * MyBatis XML mapper parser (Feature 27, Sub-PR 5)
4
+ *
5
+ * Parses MyBatis XML mapper files to extract:
6
+ * 1. <mapper namespace="..."> — linking to Java @Mapper interface
7
+ * 2. <select>, <insert>, <update>, <delete> — as repository-query nodes
8
+ * 3. <if>, <choose>/<when>/<otherwise>, <foreach> — as conditional branch nodes
9
+ * 4. <resultMap>, <association> — as DB schema evidence
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.parseMyBatisMapper = parseMyBatisMapper;
13
+ exports.isMyBatisMapperXml = isMyBatisMapperXml;
14
+ /**
15
+ * Parse a MyBatis mapper XML file.
16
+ * Uses regex-based parsing since MyBatis XML is highly structured.
17
+ */
18
+ function parseMyBatisMapper(xmlContent, filePath) {
19
+ // Extract namespace
20
+ const namespaceMatch = xmlContent.match(/<mapper\s+namespace\s*=\s*["']([^"']+)["']/);
21
+ if (!namespaceMatch)
22
+ return null;
23
+ const namespace = namespaceMatch[1];
24
+ const queries = extractQueries(xmlContent);
25
+ const resultMaps = extractResultMaps(xmlContent);
26
+ return { namespace, filePath, queries, resultMaps };
27
+ }
28
+ function extractQueries(xml) {
29
+ const queries = [];
30
+ const queryTypes = ['select', 'insert', 'update', 'delete'];
31
+ for (const type of queryTypes) {
32
+ // Match opening tag with attributes
33
+ const tagPattern = new RegExp(`<${type}\\s+([^>]*)>([\\s\\S]*?)</${type}>`, 'gi');
34
+ let match;
35
+ while ((match = tagPattern.exec(xml)) !== null) {
36
+ const attrs = match[1];
37
+ const body = match[2];
38
+ const idMatch = attrs.match(/id\s*=\s*["']([^"']+)["']/);
39
+ if (!idMatch)
40
+ continue;
41
+ const resultTypeMatch = attrs.match(/resultType\s*=\s*["']([^"']+)["']/);
42
+ const resultMapMatch = attrs.match(/resultMap\s*=\s*["']([^"']+)["']/);
43
+ const paramTypeMatch = attrs.match(/parameterType\s*=\s*["']([^"']+)["']/);
44
+ // Count conditionals
45
+ const ifCount = (body.match(/<if\b/g) || []).length;
46
+ const chooseCount = (body.match(/<choose\b/g) || []).length;
47
+ const foreachCount = (body.match(/<foreach\b/g) || []).length;
48
+ const conditionalCount = ifCount + chooseCount + foreachCount;
49
+ // Compute approximate line number
50
+ const prefix = xml.substring(0, match.index);
51
+ const line = (prefix.match(/\n/g) || []).length + 1;
52
+ queries.push({
53
+ id: idMatch[1],
54
+ type,
55
+ resultType: resultTypeMatch === null || resultTypeMatch === void 0 ? void 0 : resultTypeMatch[1],
56
+ resultMap: resultMapMatch === null || resultMapMatch === void 0 ? void 0 : resultMapMatch[1],
57
+ parameterType: paramTypeMatch === null || paramTypeMatch === void 0 ? void 0 : paramTypeMatch[1],
58
+ hasConditionals: conditionalCount > 0,
59
+ conditionalCount,
60
+ line,
61
+ });
62
+ }
63
+ }
64
+ return queries;
65
+ }
66
+ function extractResultMaps(xml) {
67
+ const resultMaps = [];
68
+ const rmPattern = /<resultMap\s+([^>]*)>([\s\S]*?)<\/resultMap>/gi;
69
+ let match;
70
+ while ((match = rmPattern.exec(xml)) !== null) {
71
+ const attrs = match[1];
72
+ const body = match[2];
73
+ const idMatch = attrs.match(/id\s*=\s*["']([^"']+)["']/);
74
+ const typeMatch = attrs.match(/type\s*=\s*["']([^"']+)["']/);
75
+ if (!idMatch || !typeMatch)
76
+ continue;
77
+ // Extract associations
78
+ const associations = [];
79
+ const assocPattern = /javaType\s*=\s*["']([^"']+)["']/g;
80
+ let assocMatch;
81
+ while ((assocMatch = assocPattern.exec(body)) !== null) {
82
+ associations.push(assocMatch[1]);
83
+ }
84
+ // Extract collections
85
+ const collections = [];
86
+ const collPattern = /<collection[^>]*ofType\s*=\s*["']([^"']+)["']/g;
87
+ let collMatch;
88
+ while ((collMatch = collPattern.exec(body)) !== null) {
89
+ collections.push(collMatch[1]);
90
+ }
91
+ resultMaps.push({
92
+ id: idMatch[1],
93
+ type: typeMatch[1],
94
+ associations,
95
+ collections,
96
+ });
97
+ }
98
+ return resultMaps;
99
+ }
100
+ /**
101
+ * Check if a file is a MyBatis XML mapper.
102
+ */
103
+ function isMyBatisMapperXml(content) {
104
+ return /<mapper\s+namespace\s*=/.test(content) &&
105
+ (/<select\b/.test(content) || /<insert\b/.test(content) ||
106
+ /<update\b/.test(content) || /<delete\b/.test(content));
107
+ }