api-tests-coverage 1.0.15 → 1.0.17

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 (139) 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/src/ast/astTypes.d.ts +86 -0
  46. package/dist/src/ast/astTypes.d.ts.map +1 -1
  47. package/dist/src/discovery/fileClassifier.d.ts.map +1 -1
  48. package/dist/src/discovery/fileClassifier.js +15 -16
  49. package/dist/src/discovery/frameworkDetector.d.ts +28 -0
  50. package/dist/src/discovery/frameworkDetector.d.ts.map +1 -0
  51. package/dist/src/discovery/frameworkDetector.js +189 -0
  52. package/dist/src/discovery/projectDiscovery.d.ts +5 -1
  53. package/dist/src/discovery/projectDiscovery.d.ts.map +1 -1
  54. package/dist/src/discovery/projectDiscovery.js +8 -1
  55. package/dist/src/inference/routeInference.d.ts.map +1 -1
  56. package/dist/src/inference/routeInference.js +224 -1
  57. package/dist/src/languages/java/graphqlSchemaParser.d.ts +65 -0
  58. package/dist/src/languages/java/graphqlSchemaParser.d.ts.map +1 -0
  59. package/dist/src/languages/java/graphqlSchemaParser.js +164 -0
  60. package/dist/src/languages/java/mybatisXmlParser.d.ts +52 -0
  61. package/dist/src/languages/java/mybatisXmlParser.d.ts.map +1 -0
  62. package/dist/src/languages/java/mybatisXmlParser.js +107 -0
  63. package/dist/src/languages/java/semanticBuilder.d.ts.map +1 -1
  64. package/dist/src/languages/java/semanticBuilder.js +69 -12
  65. package/dist/src/languages/javascript/angularDetector.d.ts +74 -0
  66. package/dist/src/languages/javascript/angularDetector.d.ts.map +1 -0
  67. package/dist/src/languages/javascript/angularDetector.js +227 -0
  68. package/dist/src/languages/javascript/assertionResolver.js +6 -4
  69. package/dist/src/languages/javascript/hapiDetector.d.ts +40 -0
  70. package/dist/src/languages/javascript/hapiDetector.d.ts.map +1 -0
  71. package/dist/src/languages/javascript/hapiDetector.js +174 -0
  72. package/dist/src/languages/javascript/mongooseDetector.d.ts +65 -0
  73. package/dist/src/languages/javascript/mongooseDetector.d.ts.map +1 -0
  74. package/dist/src/languages/javascript/mongooseDetector.js +237 -0
  75. package/dist/src/languages/javascript/vueDetector.d.ts +42 -0
  76. package/dist/src/languages/javascript/vueDetector.d.ts.map +1 -0
  77. package/dist/src/languages/javascript/vueDetector.js +109 -0
  78. package/dist/src/languages/python/index.d.ts +6 -2
  79. package/dist/src/languages/python/index.d.ts.map +1 -1
  80. package/dist/src/languages/python/index.js +200 -5
  81. package/dist/src/languages/python/testPatternDetector.d.ts +70 -0
  82. package/dist/src/languages/python/testPatternDetector.d.ts.map +1 -0
  83. package/dist/src/languages/python/testPatternDetector.js +201 -0
  84. package/dist/src/pipeline/confidence.d.ts +6 -1
  85. package/dist/src/pipeline/confidence.d.ts.map +1 -1
  86. package/dist/src/pipeline/confidence.js +8 -3
  87. package/dist/src/pipeline/graph.d.ts.map +1 -1
  88. package/dist/src/pipeline/graph.js +16 -4
  89. package/dist/src/pipeline/stages/ast/astStage.d.ts.map +1 -1
  90. package/dist/src/pipeline/stages/ast/astStage.js +51 -1
  91. package/dist/src/pipeline/stages/ast/baseUrlComposer.d.ts +44 -0
  92. package/dist/src/pipeline/stages/ast/baseUrlComposer.d.ts.map +1 -0
  93. package/dist/src/pipeline/stages/ast/baseUrlComposer.js +97 -0
  94. package/dist/src/pipeline/stages/ast/crossFileResolutionPass.d.ts +54 -0
  95. package/dist/src/pipeline/stages/ast/crossFileResolutionPass.d.ts.map +1 -0
  96. package/dist/src/pipeline/stages/ast/crossFileResolutionPass.js +88 -0
  97. package/dist/src/pipeline/stages/ast/crossFileResolver.d.ts.map +1 -1
  98. package/dist/src/pipeline/stages/ast/crossFileResolver.js +39 -1
  99. package/dist/src/pipeline/stages/ast/graphBuilder.d.ts.map +1 -1
  100. package/dist/src/pipeline/stages/ast/graphBuilder.js +81 -0
  101. package/dist/src/pipeline/stages/ast/optionalAuthUnifier.d.ts +41 -0
  102. package/dist/src/pipeline/stages/ast/optionalAuthUnifier.d.ts.map +1 -0
  103. package/dist/src/pipeline/stages/ast/optionalAuthUnifier.js +101 -0
  104. package/dist/src/pipeline/stages/ast/resolvers/angularInjectionResolver.d.ts +18 -0
  105. package/dist/src/pipeline/stages/ast/resolvers/angularInjectionResolver.d.ts.map +1 -0
  106. package/dist/src/pipeline/stages/ast/resolvers/angularInjectionResolver.js +96 -0
  107. package/dist/src/pipeline/stages/ast/resolvers/dddLayerResolver.d.ts +46 -0
  108. package/dist/src/pipeline/stages/ast/resolvers/dddLayerResolver.d.ts.map +1 -0
  109. package/dist/src/pipeline/stages/ast/resolvers/dddLayerResolver.js +314 -0
  110. package/dist/src/pipeline/stages/ast/resolvers/expressRouterResolver.d.ts +17 -0
  111. package/dist/src/pipeline/stages/ast/resolvers/expressRouterResolver.d.ts.map +1 -0
  112. package/dist/src/pipeline/stages/ast/resolvers/expressRouterResolver.js +65 -0
  113. package/dist/src/pipeline/stages/ast/resolvers/flaskBlueprintResolver.d.ts +17 -0
  114. package/dist/src/pipeline/stages/ast/resolvers/flaskBlueprintResolver.d.ts.map +1 -0
  115. package/dist/src/pipeline/stages/ast/resolvers/flaskBlueprintResolver.js +114 -0
  116. package/dist/src/pipeline/stages/ast/resolvers/mybatisResolver.d.ts +27 -0
  117. package/dist/src/pipeline/stages/ast/resolvers/mybatisResolver.d.ts.map +1 -0
  118. package/dist/src/pipeline/stages/ast/resolvers/mybatisResolver.js +130 -0
  119. package/dist/src/pipeline/stages/ast/resolvers/vuexActionResolver.d.ts +17 -0
  120. package/dist/src/pipeline/stages/ast/resolvers/vuexActionResolver.d.ts.map +1 -0
  121. package/dist/src/pipeline/stages/ast/resolvers/vuexActionResolver.js +80 -0
  122. package/dist/src/pipeline/stages/ast/rulesEnforcer.d.ts +24 -0
  123. package/dist/src/pipeline/stages/ast/rulesEnforcer.d.ts.map +1 -0
  124. package/dist/src/pipeline/stages/ast/rulesEnforcer.js +411 -0
  125. package/dist/src/pipeline/stages/ast/types.d.ts +114 -1
  126. package/dist/src/pipeline/stages/ast/types.d.ts.map +1 -1
  127. package/dist/src/pipeline/stages/merge/conflictDetector.d.ts +2 -0
  128. package/dist/src/pipeline/stages/merge/conflictDetector.d.ts.map +1 -1
  129. package/dist/src/pipeline/stages/merge/conflictDetector.js +54 -2
  130. package/dist/src/pipeline/stages/merge/coverageMappingBuilder.d.ts.map +1 -1
  131. package/dist/src/pipeline/stages/merge/coverageMappingBuilder.js +67 -3
  132. package/dist/src/pipeline/stages/tia/mockBoundaryDetector.d.ts.map +1 -1
  133. package/dist/src/pipeline/stages/tia/mockBoundaryDetector.js +8 -1
  134. package/dist/src/pipeline/stages/tia/parameterizedTestExpander.js +8 -4
  135. package/dist/src/pipeline/stages/tia/testLayerClassifier.d.ts.map +1 -1
  136. package/dist/src/pipeline/stages/tia/testLayerClassifier.js +41 -10
  137. package/dist/src/pipeline/types.d.ts +1 -1
  138. package/dist/src/pipeline/types.d.ts.map +1 -1
  139. package/package.json +3 -3
@@ -0,0 +1,101 @@
1
+ "use strict";
2
+ /**
3
+ * Optional auth unifier (Feature 27, Sub-PR 9)
4
+ *
5
+ * Normalizes all framework-specific auth classifications to a unified SecurityClassification.
6
+ * Maps framework-specific patterns:
7
+ * Flask @jwt_optional → { optional: true }
8
+ * Express auth.optional / credentialsRequired: false → { optional: true }
9
+ * HapiJS auth: { mode: 'try' } → { optional: true }
10
+ * Spring @PreAuthorize → { required: true }
11
+ * Slim PHP optionalAuth → { optional: true }, jwt/auth → { required: true }
12
+ * Angular canActivate:none → { optional: true } (interceptor-based)
13
+ * NestJS @UseGuards → { required: true }
14
+ */
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.unifyAuthClassification = unifyAuthClassification;
17
+ exports.detectAuthCoverageGaps = detectAuthCoverageGaps;
18
+ /**
19
+ * Framework-specific auth pattern → unified SecurityClassification.
20
+ */
21
+ function unifyAuthClassification(framework, pattern) {
22
+ const key = `${framework}:${pattern}`.toLowerCase();
23
+ // Flask patterns — use word-boundary regex to avoid matching e.g. "@jwt_required_custom"
24
+ if (/^flask:@?jwt_required\b/.test(key)) {
25
+ return { type: 'jwt', required: true, optional: false, sourcePattern: pattern };
26
+ }
27
+ if (/^flask:@?jwt_optional\b/.test(key)) {
28
+ return { type: 'jwt', required: false, optional: true, sourcePattern: pattern };
29
+ }
30
+ if (/^flask:@?login_required\b/.test(key)) {
31
+ return { type: 'session', required: true, optional: false, sourcePattern: pattern };
32
+ }
33
+ // Express patterns — use word-boundary regex for precise matching
34
+ if (/^express:auth\.required\b/.test(key) || /^express:credentialsrequired:\s*true\b/.test(key)) {
35
+ return { type: 'jwt', required: true, optional: false, sourcePattern: pattern };
36
+ }
37
+ if (/^express:auth\.optional\b/.test(key) || /^express:credentialsrequired:\s*false\b/.test(key)) {
38
+ return { type: 'jwt', required: false, optional: true, sourcePattern: pattern };
39
+ }
40
+ if (/^express:passport\.authenticate\b/.test(key)) {
41
+ return { type: 'jwt', required: true, optional: false, sourcePattern: pattern };
42
+ }
43
+ // HapiJS patterns (framework may be 'hapi' or 'hapijs')
44
+ const isHapi = /^hapi(?:js)?:/.test(key);
45
+ if (isHapi && /\bauth\b/.test(key) && /\bmode\b/.test(key) && (/\btry\b/.test(key) || /\boptional\b/.test(key))) {
46
+ return { type: 'jwt', required: false, optional: true, sourcePattern: pattern };
47
+ }
48
+ if (isHapi && /\bauth\b/.test(key) && !/\bfalse\b/.test(key) && !/\btry\b/.test(key) && !/\boptional\b/.test(key)) {
49
+ return { type: 'jwt', required: true, optional: false, sourcePattern: pattern };
50
+ }
51
+ // Spring patterns — include @RolesAllowed and @WithMockUser
52
+ if (/^spring:@?preauthorize\b/.test(key) || /^spring:@?secured\b/.test(key) || /^spring:@?rolesallowed\b/.test(key)) {
53
+ return { type: 'custom', required: true, optional: false, sourcePattern: pattern };
54
+ }
55
+ if (/^spring:@?withmockuser\b/.test(key)) {
56
+ return { type: 'custom', required: true, optional: false, sourcePattern: pattern };
57
+ }
58
+ // Slim PHP patterns — slim:optionalAuth is optional, slim:jwt or slim:auth is required
59
+ if (/^slim:optionalauth\b/.test(key)) {
60
+ return { type: 'jwt', required: false, optional: true, sourcePattern: pattern };
61
+ }
62
+ if (/^slim:(?:jwt|auth)\b/.test(key)) {
63
+ return { type: 'jwt', required: true, optional: false, sourcePattern: pattern };
64
+ }
65
+ // Angular patterns — canActivate:none (no guard) with interceptor tokens is optional
66
+ if (/^angular:canactivate:none\b/.test(key)) {
67
+ return { type: 'custom', required: false, optional: true, sourcePattern: pattern };
68
+ }
69
+ // NestJS patterns — @UseGuards is required auth
70
+ if (/^nestjs:@?useguards\b/.test(key)) {
71
+ return { type: 'custom', required: true, optional: false, sourcePattern: pattern };
72
+ }
73
+ return undefined;
74
+ }
75
+ /**
76
+ * Analyze auth coverage gaps in endpoints vs tests.
77
+ */
78
+ function detectAuthCoverageGaps(endpoints, testAssertionPaths) {
79
+ const gaps = [];
80
+ for (const ep of endpoints) {
81
+ if (!ep.security)
82
+ continue;
83
+ if (ep.security.required && !testAssertionPaths.has(ep.path)) {
84
+ gaps.push({
85
+ type: 'missing-401-test',
86
+ endpointPath: ep.path,
87
+ security: ep.security,
88
+ sourceFile: ep.sourceFile,
89
+ });
90
+ }
91
+ if (ep.security.optional && !testAssertionPaths.has(ep.path)) {
92
+ gaps.push({
93
+ type: 'missing-optional-dual-path',
94
+ endpointPath: ep.path,
95
+ security: ep.security,
96
+ sourceFile: ep.sourceFile,
97
+ });
98
+ }
99
+ }
100
+ return gaps;
101
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Angular injection cross-file resolver (Feature 27, Sub-PR 7)
3
+ *
4
+ * Resolves Angular dependency injection chains:
5
+ * 1. Constructor injection: private articlesService: ArticlesService → find ArticlesService file
6
+ * 2. inject(ArticlesService) → same resolution
7
+ * 3. Follow service methods to HttpClient calls → build chain: Component → Service → HTTP call
8
+ * 4. Resolve environment.api_url from environments/environment.ts
9
+ * 5. Link guards to routes they protect via route config canActivate arrays
10
+ */
11
+ import type { CrossFileResolver, CrossFileResolutionContext, CrossFileResolutionResult } from '../types';
12
+ import type { DetectedApiFramework } from '../../../../discovery/frameworkDetector';
13
+ export declare class AngularInjectionResolver implements CrossFileResolver {
14
+ readonly name = "angular-injection";
15
+ appliesTo(frameworks: DetectedApiFramework[]): boolean;
16
+ resolve(ctx: CrossFileResolutionContext): CrossFileResolutionResult;
17
+ }
18
+ //# sourceMappingURL=angularInjectionResolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"angularInjectionResolver.d.ts","sourceRoot":"","sources":["../../../../../../src/pipeline/stages/ast/resolvers/angularInjectionResolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EACV,iBAAiB,EACjB,0BAA0B,EAC1B,yBAAyB,EAC1B,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,yCAAyC,CAAC;AAEpF,qBAAa,wBAAyB,YAAW,iBAAiB;IAChE,QAAQ,CAAC,IAAI,uBAAuB;IAEpC,SAAS,CAAC,UAAU,EAAE,oBAAoB,EAAE,GAAG,OAAO;IAItD,OAAO,CAAC,GAAG,EAAE,0BAA0B,GAAG,yBAAyB;CAyCpE"}
@@ -0,0 +1,96 @@
1
+ "use strict";
2
+ /**
3
+ * Angular injection cross-file resolver (Feature 27, Sub-PR 7)
4
+ *
5
+ * Resolves Angular dependency injection chains:
6
+ * 1. Constructor injection: private articlesService: ArticlesService → find ArticlesService file
7
+ * 2. inject(ArticlesService) → same resolution
8
+ * 3. Follow service methods to HttpClient calls → build chain: Component → Service → HTTP call
9
+ * 4. Resolve environment.api_url from environments/environment.ts
10
+ * 5. Link guards to routes they protect via route config canActivate arrays
11
+ */
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.AngularInjectionResolver = void 0;
14
+ class AngularInjectionResolver {
15
+ constructor() {
16
+ this.name = 'angular-injection';
17
+ }
18
+ appliesTo(frameworks) {
19
+ return frameworks.some((f) => f.name === 'angular');
20
+ }
21
+ resolve(ctx) {
22
+ let entriesAdded = 0;
23
+ const diagnostics = [];
24
+ const unresolvedRefs = [];
25
+ // Build injection chains from class registry
26
+ for (const [className, classInfo] of ctx.symbolTable.classes) {
27
+ // Check any class that has a model — Angular services typically don't implement interfaces
28
+ // Look for classes that have methods making HTTP calls
29
+ const model = ctx.symbolTable.models.get(classInfo.filePath);
30
+ if (!model)
31
+ continue;
32
+ // Check if any function makes HTTP calls
33
+ for (const [, func] of model.functions) {
34
+ if (func.bodyHttpCalls.length > 0) {
35
+ // This is an API service — create injection chains for any class that injects it
36
+ const consumers = findConsumers(className, ctx);
37
+ for (const consumer of consumers) {
38
+ if (!ctx.symbolTable.injectionChains.has(consumer.file)) {
39
+ ctx.symbolTable.injectionChains.set(consumer.file, []);
40
+ }
41
+ ctx.symbolTable.injectionChains.get(consumer.file).push({
42
+ consumerFile: consumer.file,
43
+ consumerClass: consumer.className,
44
+ serviceClass: className,
45
+ serviceFile: classInfo.filePath,
46
+ injectionStyle: consumer.style,
47
+ });
48
+ entriesAdded++;
49
+ }
50
+ }
51
+ }
52
+ }
53
+ if (entriesAdded > 0) {
54
+ diagnostics.push(`Resolved ${entriesAdded} Angular injection chain(s)`);
55
+ }
56
+ return { entriesAdded, diagnostics, unresolvedRefs };
57
+ }
58
+ }
59
+ exports.AngularInjectionResolver = AngularInjectionResolver;
60
+ function findConsumers(serviceName, ctx) {
61
+ const consumers = [];
62
+ // Check existing injection chains (may have been populated by angularDetector)
63
+ for (const [file, chains] of ctx.symbolTable.injectionChains) {
64
+ for (const chain of chains) {
65
+ if (chain.serviceClass === serviceName) {
66
+ consumers.push({
67
+ className: chain.consumerClass,
68
+ file: chain.consumerFile,
69
+ style: chain.injectionStyle,
70
+ });
71
+ }
72
+ }
73
+ }
74
+ // Fallback: search class registry for classes that might inject this service
75
+ // (constructor injection detected by parameter name matching)
76
+ for (const [className, classInfo] of ctx.symbolTable.classes) {
77
+ // Skip if already found as a consumer
78
+ if (consumers.some((c) => c.className === className && c.file === classInfo.filePath))
79
+ continue;
80
+ // Check if this class's model has the service name in its calledFunctions
81
+ const model = ctx.symbolTable.models.get(classInfo.filePath);
82
+ if (!model)
83
+ continue;
84
+ for (const [, func] of model.functions) {
85
+ if (func.calledFunctions.some((f) => f.includes(serviceName))) {
86
+ consumers.push({
87
+ className,
88
+ file: classInfo.filePath,
89
+ style: 'constructor',
90
+ });
91
+ break;
92
+ }
93
+ }
94
+ }
95
+ return consumers;
96
+ }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * DDD / CQRS / Hexagonal layer resolver (Feature 27, Sub-PR 5)
3
+ *
4
+ * Resolves DDD-structured Java projects without requiring @Service/@Repository annotations.
5
+ * Detects:
6
+ * 1. Repository interfaces (findBy*, save, delete methods)
7
+ * 2. Interface → implementation mapping (implements InterfaceName)
8
+ * 3. CQRS command/query handlers (execute/handle/apply taking *Command/*Query)
9
+ * 4. Package-name layer hints (domain, application, infrastructure, adapter) as tie-breakers only
10
+ */
11
+ import type { CrossFileResolver, CrossFileResolutionContext, CrossFileResolutionResult } from '../types';
12
+ import type { DetectedApiFramework } from '../../../../discovery/frameworkDetector';
13
+ export interface DddRepositoryInterface {
14
+ interfaceName: string;
15
+ methods: string[];
16
+ sourceFile: string;
17
+ line?: number;
18
+ }
19
+ export interface DddCqrsHandler {
20
+ className: string;
21
+ handlerType: 'command' | 'query' | 'event';
22
+ handleMethodName: string;
23
+ parameterType: string;
24
+ sourceFile: string;
25
+ line?: number;
26
+ }
27
+ export declare class DddLayerResolver implements CrossFileResolver {
28
+ readonly name = "ddd-layer";
29
+ appliesTo(frameworks: DetectedApiFramework[]): boolean;
30
+ resolve(ctx: CrossFileResolutionContext): CrossFileResolutionResult;
31
+ }
32
+ /**
33
+ * Detect repository interfaces: interfaces with findBy*, save, delete, etc.
34
+ * No @Repository annotation required (RULE-SA03).
35
+ */
36
+ export declare function detectRepositoryInterfaces(source: string, filePath: string): DddRepositoryInterface[];
37
+ /**
38
+ * Detect CQRS command/query handlers.
39
+ * Looks for classes with execute/handle/apply methods that take *Command/*Query parameters.
40
+ */
41
+ export declare function detectCqrsHandlers(source: string, filePath: string): DddCqrsHandler[];
42
+ /**
43
+ * Detect implements clauses from Java/Kotlin source.
44
+ */
45
+ export declare function detectImplementsClauses(source: string, filePath: string): Array<[string, string]>;
46
+ //# sourceMappingURL=dddLayerResolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dddLayerResolver.d.ts","sourceRoot":"","sources":["../../../../../../src/pipeline/stages/ast/resolvers/dddLayerResolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EACV,iBAAiB,EACjB,0BAA0B,EAC1B,yBAAyB,EAC1B,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,yCAAyC,CAAC;AAEpF,MAAM,WAAW,sBAAsB;IACrC,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,SAAS,GAAG,OAAO,GAAG,OAAO,CAAC;IAC3C,gBAAgB,EAAE,MAAM,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,qBAAa,gBAAiB,YAAW,iBAAiB;IACxD,QAAQ,CAAC,IAAI,eAAe;IAE5B,SAAS,CAAC,UAAU,EAAE,oBAAoB,EAAE,GAAG,OAAO;IAMtD,OAAO,CAAC,GAAG,EAAE,0BAA0B,GAAG,yBAAyB;CA+JpE;AAED;;;GAGG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,sBAAsB,EAAE,CAgFrG;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,cAAc,EAAE,CAiCrF;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAcjG"}
@@ -0,0 +1,314 @@
1
+ "use strict";
2
+ /**
3
+ * DDD / CQRS / Hexagonal layer resolver (Feature 27, Sub-PR 5)
4
+ *
5
+ * Resolves DDD-structured Java projects without requiring @Service/@Repository annotations.
6
+ * Detects:
7
+ * 1. Repository interfaces (findBy*, save, delete methods)
8
+ * 2. Interface → implementation mapping (implements InterfaceName)
9
+ * 3. CQRS command/query handlers (execute/handle/apply taking *Command/*Query)
10
+ * 4. Package-name layer hints (domain, application, infrastructure, adapter) as tie-breakers only
11
+ */
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.DddLayerResolver = void 0;
14
+ exports.detectRepositoryInterfaces = detectRepositoryInterfaces;
15
+ exports.detectCqrsHandlers = detectCqrsHandlers;
16
+ exports.detectImplementsClauses = detectImplementsClauses;
17
+ class DddLayerResolver {
18
+ constructor() {
19
+ this.name = 'ddd-layer';
20
+ }
21
+ appliesTo(frameworks) {
22
+ return frameworks.some((f) => f.name === 'spring-boot' || f.name === 'spring-graphql' || f.name === 'dgs-framework');
23
+ }
24
+ resolve(ctx) {
25
+ let entriesAdded = 0;
26
+ const diagnostics = [];
27
+ const unresolvedRefs = [];
28
+ const repoInterfaces = [];
29
+ const cqrsHandlers = [];
30
+ const implementations = new Map();
31
+ // ----- Detect from class registry and semantic models -----
32
+ // 1. Detect repository interfaces from class registry
33
+ for (const [className, classDecl] of ctx.symbolTable.classes) {
34
+ const isRepoByName = /(?:Repository|Repo|Store|Gateway)$/.test(className);
35
+ const repoMethods = classDecl.methods.filter(m => /^(?:findBy\w+|find\w+|save|saveAll|delete|deleteById|existsBy\w+|countBy\w+|getBy\w+)$/.test(m));
36
+ if (isRepoByName && repoMethods.length > 0) {
37
+ repoInterfaces.push({
38
+ interfaceName: className,
39
+ methods: repoMethods,
40
+ sourceFile: classDecl.filePath,
41
+ line: classDecl.line,
42
+ });
43
+ }
44
+ else if (!isRepoByName && repoMethods.length >= 2) {
45
+ // Generic interface with enough repository-like methods
46
+ repoInterfaces.push({
47
+ interfaceName: className,
48
+ methods: repoMethods,
49
+ sourceFile: classDecl.filePath,
50
+ line: classDecl.line,
51
+ });
52
+ }
53
+ }
54
+ // 2. Detect CQRS handlers from class registry + semantic models
55
+ for (const [className, classDecl] of ctx.symbolTable.classes) {
56
+ const model = ctx.symbolTable.models.get(classDecl.filePath);
57
+ if (!model)
58
+ continue;
59
+ for (const methodName of classDecl.methods) {
60
+ if (methodName !== 'execute' && methodName !== 'handle' && methodName !== 'apply')
61
+ continue;
62
+ // Determine handler type from class name or method context
63
+ let handlerType = 'command';
64
+ if (/Query/.test(className))
65
+ handlerType = 'query';
66
+ else if (/Event/.test(className))
67
+ handlerType = 'event';
68
+ // Try to get parameter type from the function's annotations or the class name
69
+ const func = model.functions.get(methodName);
70
+ let parameterType = `${className.replace(/Handler$/, '')}`;
71
+ if (func === null || func === void 0 ? void 0 : func.annotations) {
72
+ // Check for annotations that hint at the command/query type
73
+ for (const ann of func.annotations) {
74
+ const typeMatch = ann.match(/(\w+(?:Command|Query|Event))/);
75
+ if (typeMatch) {
76
+ parameterType = typeMatch[1];
77
+ if (/Command$/.test(parameterType))
78
+ handlerType = 'command';
79
+ else if (/Query$/.test(parameterType))
80
+ handlerType = 'query';
81
+ else if (/Event$/.test(parameterType))
82
+ handlerType = 'event';
83
+ }
84
+ }
85
+ }
86
+ cqrsHandlers.push({
87
+ className,
88
+ handlerType,
89
+ handleMethodName: methodName,
90
+ parameterType,
91
+ sourceFile: classDecl.filePath,
92
+ line: classDecl.line,
93
+ });
94
+ }
95
+ }
96
+ // 3. Detect implements clauses from class registry (already parsed by tree-sitter)
97
+ for (const [className, classDecl] of ctx.symbolTable.classes) {
98
+ if (!classDecl.implementsInterfaces || classDecl.implementsInterfaces.length === 0)
99
+ continue;
100
+ for (const iface of classDecl.implementsInterfaces) {
101
+ if (!implementations.has(iface)) {
102
+ implementations.set(iface, []);
103
+ }
104
+ implementations.get(iface).push(className);
105
+ }
106
+ }
107
+ // Map interface → implementation in symbolTable
108
+ for (const [ifaceName, implNames] of implementations) {
109
+ for (const implName of implNames) {
110
+ const ifaceFile = findFileForClass(ifaceName, ctx);
111
+ const implFile = findFileForClass(implName, ctx);
112
+ if (ifaceFile && implFile) {
113
+ if (!ctx.symbolTable.interfaceImplementations.has(ifaceFile)) {
114
+ ctx.symbolTable.interfaceImplementations.set(ifaceFile, []);
115
+ }
116
+ ctx.symbolTable.interfaceImplementations.get(ifaceFile).push({
117
+ interfaceName: ifaceName,
118
+ interfaceFile: ifaceFile,
119
+ implName,
120
+ implFile,
121
+ });
122
+ entriesAdded++;
123
+ }
124
+ else if (!implFile) {
125
+ unresolvedRefs.push({
126
+ ref: implName,
127
+ reason: `Implementation class '${implName}' not found in parsed models`,
128
+ });
129
+ }
130
+ }
131
+ }
132
+ // Write repository interfaces into symbolTable (Section 5.3)
133
+ for (const repo of repoInterfaces) {
134
+ const key = repo.sourceFile;
135
+ if (!ctx.symbolTable.interfaceImplementations.has(key)) {
136
+ ctx.symbolTable.interfaceImplementations.set(key, []);
137
+ }
138
+ const existing = ctx.symbolTable.interfaceImplementations.get(key);
139
+ const alreadyMapped = existing.some(e => e.interfaceName === repo.interfaceName);
140
+ if (!alreadyMapped) {
141
+ existing.push({
142
+ interfaceName: repo.interfaceName,
143
+ interfaceFile: repo.sourceFile,
144
+ implName: '',
145
+ implFile: '',
146
+ });
147
+ entriesAdded++;
148
+ }
149
+ }
150
+ // Write CQRS handlers as service-layer entries into symbolTable (Section 5.2)
151
+ for (const handler of cqrsHandlers) {
152
+ const key = handler.sourceFile;
153
+ if (!ctx.symbolTable.interfaceImplementations.has(key)) {
154
+ ctx.symbolTable.interfaceImplementations.set(key, []);
155
+ }
156
+ ctx.symbolTable.interfaceImplementations.get(key).push({
157
+ interfaceName: handler.parameterType,
158
+ interfaceFile: handler.sourceFile,
159
+ implName: handler.className,
160
+ implFile: handler.sourceFile,
161
+ });
162
+ entriesAdded++;
163
+ }
164
+ if (repoInterfaces.length > 0) {
165
+ diagnostics.push(`Found ${repoInterfaces.length} repository interface(s)`);
166
+ }
167
+ if (cqrsHandlers.length > 0) {
168
+ diagnostics.push(`Found ${cqrsHandlers.length} CQRS handler(s)`);
169
+ }
170
+ if (implementations.size > 0) {
171
+ diagnostics.push(`Found ${implementations.size} interface→implementation mapping(s)`);
172
+ }
173
+ return { entriesAdded, diagnostics, unresolvedRefs };
174
+ }
175
+ }
176
+ exports.DddLayerResolver = DddLayerResolver;
177
+ /**
178
+ * Detect repository interfaces: interfaces with findBy*, save, delete, etc.
179
+ * No @Repository annotation required (RULE-SA03).
180
+ */
181
+ function detectRepositoryInterfaces(source, filePath) {
182
+ const repos = [];
183
+ const lines = source.split('\n');
184
+ // Find interfaces
185
+ const interfacePattern = /(?:public\s+)?interface\s+(\w+(?:Repository|Repo|Store|Gateway))\b/;
186
+ let currentInterface = null;
187
+ let braceDepth = 0;
188
+ for (let i = 0; i < lines.length; i++) {
189
+ const line = lines[i];
190
+ if (!currentInterface) {
191
+ const match = line.match(interfacePattern);
192
+ if (match) {
193
+ currentInterface = {
194
+ interfaceName: match[1],
195
+ methods: [],
196
+ sourceFile: filePath,
197
+ line: i + 1,
198
+ };
199
+ braceDepth = 0;
200
+ }
201
+ }
202
+ if (currentInterface) {
203
+ braceDepth += (line.match(/\{/g) || []).length;
204
+ braceDepth -= (line.match(/\}/g) || []).length;
205
+ // Detect repository methods: findByX, save, delete, existsBy, countBy
206
+ const methodMatch = line.match(/\b(findBy\w+|find\w+|save|saveAll|delete|deleteById|existsBy\w+|countBy\w+|getBy\w+)\s*\(/);
207
+ if (methodMatch) {
208
+ currentInterface.methods.push(methodMatch[1]);
209
+ }
210
+ if (braceDepth <= 0 && currentInterface.methods.length > 0) {
211
+ repos.push(currentInterface);
212
+ currentInterface = null;
213
+ }
214
+ else if (braceDepth <= 0) {
215
+ currentInterface = null;
216
+ }
217
+ }
218
+ }
219
+ // Also detect interfaces with standard CRUD method signatures without naming convention
220
+ const genericInterfacePattern = /(?:public\s+)?interface\s+(\w+)\b/g;
221
+ let genMatch;
222
+ while ((genMatch = genericInterfacePattern.exec(source)) !== null) {
223
+ const name = genMatch[1];
224
+ // Skip if already detected
225
+ if (repos.some((r) => r.interfaceName === name))
226
+ continue;
227
+ // Check if this interface has findBy, save, delete methods
228
+ const startIdx = genMatch.index;
229
+ const braceIdx = source.indexOf('{', startIdx);
230
+ if (braceIdx < 0)
231
+ continue;
232
+ let depth = 1;
233
+ let endIdx = braceIdx + 1;
234
+ while (endIdx < source.length && depth > 0) {
235
+ if (source[endIdx] === '{')
236
+ depth++;
237
+ if (source[endIdx] === '}')
238
+ depth--;
239
+ endIdx++;
240
+ }
241
+ const body = source.substring(braceIdx, endIdx);
242
+ const methods = [];
243
+ const repoMethodPattern = /\b(findBy\w+|save|delete|deleteById)\s*\(/g;
244
+ let rm;
245
+ while ((rm = repoMethodPattern.exec(body)) !== null) {
246
+ methods.push(rm[1]);
247
+ }
248
+ if (methods.length >= 2) {
249
+ const lineNum = source.substring(0, startIdx).split('\n').length;
250
+ repos.push({ interfaceName: name, methods, sourceFile: filePath, line: lineNum });
251
+ }
252
+ }
253
+ return repos;
254
+ }
255
+ /**
256
+ * Detect CQRS command/query handlers.
257
+ * Looks for classes with execute/handle/apply methods that take *Command/*Query parameters.
258
+ */
259
+ function detectCqrsHandlers(source, filePath) {
260
+ const handlers = [];
261
+ const lines = source.split('\n');
262
+ // Pattern: handle(CreateArticleCommand cmd) or execute(GetArticlesQuery query)
263
+ const handlerMethodPattern = /(?:public\s+\S+\s+)?(execute|handle|apply)\s*\(\s*(\w+(Command|Query|Event))\s+\w+\s*\)/;
264
+ // Find the enclosing class name
265
+ let currentClass = '';
266
+ for (let i = 0; i < lines.length; i++) {
267
+ const line = lines[i];
268
+ const classMatch = line.match(/class\s+(\w+)/);
269
+ if (classMatch) {
270
+ currentClass = classMatch[1];
271
+ }
272
+ const methodMatch = line.match(handlerMethodPattern);
273
+ if (methodMatch && currentClass) {
274
+ const handlerType = methodMatch[3].toLowerCase();
275
+ handlers.push({
276
+ className: currentClass,
277
+ handlerType,
278
+ handleMethodName: methodMatch[1],
279
+ parameterType: methodMatch[2],
280
+ sourceFile: filePath,
281
+ line: i + 1,
282
+ });
283
+ }
284
+ }
285
+ return handlers;
286
+ }
287
+ /**
288
+ * Detect implements clauses from Java/Kotlin source.
289
+ */
290
+ function detectImplementsClauses(source, filePath) {
291
+ const impls = [];
292
+ const pattern = /class\s+(\w+)(?:\s+extends\s+\w+)?\s+implements\s+([\w,\s]+)/g;
293
+ let match;
294
+ while ((match = pattern.exec(source)) !== null) {
295
+ const className = match[1];
296
+ const interfaces = match[2].split(',').map((s) => s.trim()).filter((s) => s.length > 0);
297
+ for (const iface of interfaces) {
298
+ impls.push([iface, className]);
299
+ }
300
+ }
301
+ return impls;
302
+ }
303
+ function findFileForClass(className, ctx) {
304
+ // Search through exported symbols and class registry
305
+ const classInfo = ctx.symbolTable.classes.get(className);
306
+ if (classInfo)
307
+ return classInfo.filePath;
308
+ // Search through models for any mention
309
+ for (const [filePath, model] of ctx.symbolTable.models) {
310
+ if (model.functions.has(className))
311
+ return filePath;
312
+ }
313
+ return undefined;
314
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Express router cross-file resolver (Feature 27, Sub-PR 6)
3
+ *
4
+ * Resolves Express `app.use('/prefix', router)` mount paths across files:
5
+ * 1. Find all app.use('/path', require('./routes')) calls → mount registry
6
+ * 2. Follow require('./routes') to resolve sub-router files
7
+ * 3. Propagate auth middleware through nested routers
8
+ * 4. Build complete URL: base mount + sub-router mount + route path
9
+ */
10
+ import type { CrossFileResolver, CrossFileResolutionContext, CrossFileResolutionResult } from '../types';
11
+ import type { DetectedApiFramework } from '../../../../discovery/frameworkDetector';
12
+ export declare class ExpressRouterResolver implements CrossFileResolver {
13
+ readonly name = "express-router";
14
+ appliesTo(frameworks: DetectedApiFramework[]): boolean;
15
+ resolve(ctx: CrossFileResolutionContext): CrossFileResolutionResult;
16
+ }
17
+ //# sourceMappingURL=expressRouterResolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"expressRouterResolver.d.ts","sourceRoot":"","sources":["../../../../../../src/pipeline/stages/ast/resolvers/expressRouterResolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EACV,iBAAiB,EACjB,0BAA0B,EAC1B,yBAAyB,EAC1B,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,yCAAyC,CAAC;AAEpF,qBAAa,qBAAsB,YAAW,iBAAiB;IAC7D,QAAQ,CAAC,IAAI,oBAAoB;IAEjC,SAAS,CAAC,UAAU,EAAE,oBAAoB,EAAE,GAAG,OAAO;IAMtD,OAAO,CAAC,GAAG,EAAE,0BAA0B,GAAG,yBAAyB;CAiDpE"}
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ /**
3
+ * Express router cross-file resolver (Feature 27, Sub-PR 6)
4
+ *
5
+ * Resolves Express `app.use('/prefix', router)` mount paths across files:
6
+ * 1. Find all app.use('/path', require('./routes')) calls → mount registry
7
+ * 2. Follow require('./routes') to resolve sub-router files
8
+ * 3. Propagate auth middleware through nested routers
9
+ * 4. Build complete URL: base mount + sub-router mount + route path
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.ExpressRouterResolver = void 0;
13
+ class ExpressRouterResolver {
14
+ constructor() {
15
+ this.name = 'express-router';
16
+ }
17
+ appliesTo(frameworks) {
18
+ return frameworks.some((f) => f.name === 'express' || f.name === 'nestjs' || f.name === 'koa' || f.name === 'fastify');
19
+ }
20
+ resolve(ctx) {
21
+ let entriesAdded = 0;
22
+ const diagnostics = [];
23
+ const unresolvedRefs = [];
24
+ // Scan models for Express router mount patterns
25
+ for (const [filePath, model] of ctx.symbolTable.models) {
26
+ if (!model.routeRegistrations)
27
+ continue;
28
+ for (const reg of model.routeRegistrations) {
29
+ // app.use('/prefix', require('./router'))
30
+ if (reg.targetModule && reg.path) {
31
+ if (!ctx.symbolTable.routerMounts.has(filePath)) {
32
+ ctx.symbolTable.routerMounts.set(filePath, []);
33
+ }
34
+ ctx.symbolTable.routerMounts.get(filePath).push({
35
+ prefix: reg.path,
36
+ targetModulePath: reg.targetModule,
37
+ middleware: [],
38
+ sourceFile: filePath,
39
+ line: reg.line,
40
+ });
41
+ entriesAdded++;
42
+ }
43
+ }
44
+ }
45
+ // Propagate middleware from router mounts to sub-routes
46
+ for (const [filePath, mounts] of ctx.symbolTable.routerMounts) {
47
+ for (const mount of mounts) {
48
+ // Check if auth middleware is applied to this mount
49
+ const middleware = ctx.symbolTable.middlewareInheritance.get(filePath);
50
+ if (middleware) {
51
+ for (const mw of middleware) {
52
+ if (mw.appliedTo === 'router') {
53
+ mount.middleware.push(mw);
54
+ }
55
+ }
56
+ }
57
+ }
58
+ }
59
+ if (entriesAdded > 0) {
60
+ diagnostics.push(`Resolved ${entriesAdded} Express router mount(s)`);
61
+ }
62
+ return { entriesAdded, diagnostics, unresolvedRefs };
63
+ }
64
+ }
65
+ exports.ExpressRouterResolver = ExpressRouterResolver;