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
@@ -47,11 +47,20 @@ Object.defineProperty(exports, "__esModule", { value: true });
47
47
  exports.AstStage = void 0;
48
48
  const astAnalysisOrchestrator_1 = require("../../../ast/astAnalysisOrchestrator");
49
49
  const crossFileResolver_1 = require("./crossFileResolver");
50
+ const crossFileResolutionPass_1 = require("./crossFileResolutionPass");
50
51
  const abstractLayerTraversal_1 = require("./abstractLayerTraversal");
51
52
  const graphBuilder_1 = require("./graphBuilder");
52
53
  const fileClassifier_1 = require("../../../discovery/fileClassifier");
53
54
  const projectDiscovery_1 = require("../../../discovery/projectDiscovery");
54
55
  const parserRegistry_1 = require("../../../ast/parserRegistry");
56
+ const rulesEnforcer_1 = require("./rulesEnforcer");
57
+ const optionalAuthUnifier_1 = require("./optionalAuthUnifier");
58
+ const expressRouterResolver_1 = require("./resolvers/expressRouterResolver");
59
+ const flaskBlueprintResolver_1 = require("./resolvers/flaskBlueprintResolver");
60
+ const angularInjectionResolver_1 = require("./resolvers/angularInjectionResolver");
61
+ const vuexActionResolver_1 = require("./resolvers/vuexActionResolver");
62
+ const mybatisResolver_1 = require("./resolvers/mybatisResolver");
63
+ const dddLayerResolver_1 = require("./resolvers/dddLayerResolver");
55
64
  const fs = __importStar(require("fs"));
56
65
  const path = __importStar(require("path"));
57
66
  class AstStage {
@@ -70,7 +79,8 @@ class AstStage {
70
79
  const scaOutput = context.stageOutputs.get('sca');
71
80
  // Build analysis context
72
81
  const analysisContext = (0, astAnalysisOrchestrator_1.buildAnalysisContext)(context.config.astConfig);
73
- // Discover all files
82
+ // Discover all files — XML, GraphQL, and PHP files are included in serviceFiles
83
+ // via SERVICE_CODE_EXTS in fileClassifier.ts (RULE-SA05, SA06)
74
84
  const artifacts = (0, projectDiscovery_1.discoverProject)({ rootDir: context.projectRoot });
75
85
  const allSourceFiles = [
76
86
  ...artifacts.testFiles,
@@ -130,6 +140,33 @@ class AstStage {
130
140
  }
131
141
  // Phase 2: Build cross-file symbol table
132
142
  const crossFileTable = (0, crossFileResolver_1.buildCrossFileSymbolTable)(models, context.projectRoot);
143
+ // Phase 2b: Register and run cross-file resolvers (Feature 27)
144
+ // Clear any stale registrations from prior runs, then register all 6 resolvers
145
+ (0, crossFileResolutionPass_1.clearCrossFileResolvers)();
146
+ (0, crossFileResolutionPass_1.registerCrossFileResolver)(new expressRouterResolver_1.ExpressRouterResolver());
147
+ (0, crossFileResolutionPass_1.registerCrossFileResolver)(new flaskBlueprintResolver_1.FlaskBlueprintResolver());
148
+ (0, crossFileResolutionPass_1.registerCrossFileResolver)(new angularInjectionResolver_1.AngularInjectionResolver());
149
+ (0, crossFileResolutionPass_1.registerCrossFileResolver)(new vuexActionResolver_1.VuexActionResolver());
150
+ (0, crossFileResolutionPass_1.registerCrossFileResolver)(new mybatisResolver_1.MyBatisResolver());
151
+ (0, crossFileResolutionPass_1.registerCrossFileResolver)(new dddLayerResolver_1.DddLayerResolver());
152
+ const crossFileResolutionResult = (0, crossFileResolutionPass_1.runCrossFileResolution)(crossFileTable, context.projectRoot, artifacts.apiFrameworks, allSourceFiles);
153
+ // Phase 2c: Detect auth coverage gaps (Feature 27)
154
+ // Collect all endpoints with their security classifications for gap analysis
155
+ const endpointsForAuthGaps = [];
156
+ for (const [filePath, model] of crossFileTable.models) {
157
+ if (!model.routeRegistrations)
158
+ continue;
159
+ for (const reg of model.routeRegistrations) {
160
+ endpointsForAuthGaps.push({
161
+ path: reg.path,
162
+ security: reg.security,
163
+ sourceFile: filePath,
164
+ });
165
+ }
166
+ }
167
+ const authCoverageGaps = (0, optionalAuthUnifier_1.detectAuthCoverageGaps)(endpointsForAuthGaps, new Set());
168
+ // Phase 2d: Enforce structure-agnostic rules (Feature 27)
169
+ const rulesResult = (0, rulesEnforcer_1.enforceStructureAgnosticRules)(crossFileTable, context.projectRoot);
133
170
  // Phase 3: Run abstract layer traversal for test files
134
171
  const traversalResults = new Map();
135
172
  const depthCap = context.config.traversalDepthCap;
@@ -199,6 +236,12 @@ class AstStage {
199
236
  traversalResultCount: traversalResults.size,
200
237
  graphNodesAdded: nodes.length,
201
238
  graphEdgesAdded: edges.length,
239
+ crossFileResolversRun: crossFileResolutionResult.resolverResults.length,
240
+ crossFileEntriesAdded: crossFileResolutionResult.totalEntriesAdded,
241
+ rulesChecked: rulesResult.rulesChecked,
242
+ rulesPassed: rulesResult.rulesPassed,
243
+ ruleViolations: rulesResult.violations.length,
244
+ authCoverageGaps: authCoverageGaps.length,
202
245
  },
203
246
  };
204
247
  context.diagnostics.set('ast', diagnostics);
@@ -209,6 +252,7 @@ class AstStage {
209
252
  traversalResults,
210
253
  analyzedFiles: filesScanned,
211
254
  skippedFiles: filesSkipped,
255
+ crossFileResolutionDiagnostics: crossFileResolutionResult.resolverResults,
212
256
  };
213
257
  context.stageOutputs.set('ast', output);
214
258
  return output;
@@ -232,7 +276,13 @@ function detectLanguage(filePath, scaOutput) {
232
276
  '.kts': 'kotlin',
233
277
  '.py': 'python',
234
278
  '.rb': 'ruby',
279
+ '.php': 'php',
235
280
  '.feature': 'cucumber',
236
281
  };
282
+ // Handle GraphQL schema files and XML as special-case languages
283
+ if (ext === '.graphqls' || ext === '.graphql')
284
+ return 'graphql';
285
+ if (ext === '.xml')
286
+ return 'xml';
237
287
  return extensionMap[ext];
238
288
  }
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Base URL composer (Feature 27, Sub-PR 9)
3
+ *
4
+ * Unified URL composition across all frameworks:
5
+ * - Flask: Blueprint url_prefix + route path
6
+ * - Express: app.use mount prefix + router path + route path
7
+ * - Spring: @RequestMapping class-level + method-level
8
+ * - Angular: environment.apiUrl from environments/environment.ts
9
+ * - Vue: axios.defaults.baseURL
10
+ * - Slim PHP: $app->group prefix + route path
11
+ * - HapiJS: explicit path (no composition needed)
12
+ *
13
+ * Normalizes: remove double slashes, ensure single leading slash.
14
+ */
15
+ /**
16
+ * Compose a full URL from multiple path segments.
17
+ * Handles: double slashes, missing leading slashes, trailing slashes.
18
+ */
19
+ export declare function composeUrl(...segments: (string | undefined)[]): string;
20
+ /**
21
+ * Normalize a path template: ensure leading slash, remove double slashes,
22
+ * standardize parameter syntax.
23
+ */
24
+ export declare function normalizePath(path: string): string;
25
+ /**
26
+ * Convert framework-specific parameter syntax to OpenAPI-style {param}.
27
+ *
28
+ * Flask: <param>, <int:param> → {param}
29
+ * Express: :param → {param}
30
+ * Slim PHP: {param} (already correct)
31
+ * HapiJS: {param} (already correct)
32
+ * Spring: {param} (already correct)
33
+ */
34
+ export declare function normalizePathParams(path: string): string;
35
+ /**
36
+ * Compose full URL for a specific framework's route.
37
+ */
38
+ export declare function composeFrameworkUrl(framework: string, parts: {
39
+ baseUrl?: string;
40
+ classPrefix?: string;
41
+ mountPrefix?: string;
42
+ routePath: string;
43
+ }): string;
44
+ //# sourceMappingURL=baseUrlComposer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"baseUrlComposer.d.ts","sourceRoot":"","sources":["../../../../../src/pipeline/stages/ast/baseUrlComposer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH;;;GAGG;AACH,wBAAgB,UAAU,CAAC,GAAG,QAAQ,EAAE,CAAC,MAAM,GAAG,SAAS,CAAC,EAAE,GAAG,MAAM,CAyBtE;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAkBlD;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAQxD;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE;IACL,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;CACnB,GACA,MAAM,CAUR"}
@@ -0,0 +1,97 @@
1
+ "use strict";
2
+ /**
3
+ * Base URL composer (Feature 27, Sub-PR 9)
4
+ *
5
+ * Unified URL composition across all frameworks:
6
+ * - Flask: Blueprint url_prefix + route path
7
+ * - Express: app.use mount prefix + router path + route path
8
+ * - Spring: @RequestMapping class-level + method-level
9
+ * - Angular: environment.apiUrl from environments/environment.ts
10
+ * - Vue: axios.defaults.baseURL
11
+ * - Slim PHP: $app->group prefix + route path
12
+ * - HapiJS: explicit path (no composition needed)
13
+ *
14
+ * Normalizes: remove double slashes, ensure single leading slash.
15
+ */
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.composeUrl = composeUrl;
18
+ exports.normalizePath = normalizePath;
19
+ exports.normalizePathParams = normalizePathParams;
20
+ exports.composeFrameworkUrl = composeFrameworkUrl;
21
+ /**
22
+ * Compose a full URL from multiple path segments.
23
+ * Handles: double slashes, missing leading slashes, trailing slashes.
24
+ */
25
+ function composeUrl(...segments) {
26
+ const parts = segments
27
+ .filter((s) => typeof s === 'string' && s.length > 0);
28
+ if (parts.length === 0)
29
+ return '/';
30
+ // Detect protocol in the first segment (e.g. http://, https://)
31
+ const protocolMatch = parts[0].match(/^(\w+:\/\/)/);
32
+ let protocol = '';
33
+ if (protocolMatch) {
34
+ protocol = protocolMatch[1];
35
+ parts[0] = parts[0].slice(protocol.length);
36
+ }
37
+ const stripped = parts.map((s) => s.replace(/^\/+|\/+$/g, ''));
38
+ const joined = stripped.join('/');
39
+ const deduped = joined.replace(/\/+/g, '/');
40
+ if (protocol) {
41
+ const normalized = protocol + deduped;
42
+ return normalized.replace(/\/+$/, '') || protocol;
43
+ }
44
+ const normalized = '/' + deduped;
45
+ return normalized === '/' ? '/' : normalized.replace(/\/+$/, '');
46
+ }
47
+ /**
48
+ * Normalize a path template: ensure leading slash, remove double slashes,
49
+ * standardize parameter syntax.
50
+ */
51
+ function normalizePath(path) {
52
+ if (!path)
53
+ return '/';
54
+ // Ensure leading slash
55
+ let normalized = path.startsWith('/') ? path : '/' + path;
56
+ // Remove double slashes (but not from protocol like http://)
57
+ // First handle leading double slashes
58
+ normalized = normalized.replace(/^\/\/+/, '/');
59
+ // Then handle double slashes in the middle
60
+ normalized = normalized.replace(/([^:])\/\/+/g, '$1/');
61
+ // Remove trailing slash (unless it's just "/")
62
+ if (normalized.length > 1) {
63
+ normalized = normalized.replace(/\/+$/, '');
64
+ }
65
+ return normalized;
66
+ }
67
+ /**
68
+ * Convert framework-specific parameter syntax to OpenAPI-style {param}.
69
+ *
70
+ * Flask: <param>, <int:param> → {param}
71
+ * Express: :param → {param}
72
+ * Slim PHP: {param} (already correct)
73
+ * HapiJS: {param} (already correct)
74
+ * Spring: {param} (already correct)
75
+ */
76
+ function normalizePathParams(path) {
77
+ // Flask/Python: <param> or <type:param>
78
+ let normalized = path.replace(/<(?:\w+:)?(\w+)>/g, '{$1}');
79
+ // Express: :param (but not :// from URLs)
80
+ normalized = normalized.replace(/(?<=\/):([\w]+)/g, '{$1}');
81
+ return normalized;
82
+ }
83
+ /**
84
+ * Compose full URL for a specific framework's route.
85
+ */
86
+ function composeFrameworkUrl(framework, parts) {
87
+ const segments = [];
88
+ if (parts.baseUrl)
89
+ segments.push(parts.baseUrl);
90
+ if (parts.classPrefix)
91
+ segments.push(parts.classPrefix);
92
+ if (parts.mountPrefix)
93
+ segments.push(parts.mountPrefix);
94
+ segments.push(parts.routePath);
95
+ const raw = composeUrl(...segments);
96
+ return normalizePathParams(raw);
97
+ }
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Cross-file resolution pass (Feature 27)
3
+ *
4
+ * Orchestrates all registered cross-file resolvers after the initial AST
5
+ * parsing pass. Each resolver enriches the CrossFileSymbolTable with
6
+ * framework-specific resolution data (Blueprint prefixes, Angular injection
7
+ * chains, DDD interface mappings, etc.).
8
+ *
9
+ * Resolvers are registered via `registerCrossFileResolver()` and discovered
10
+ * via `getCrossFileResolvers()`. Each resolver implements the `CrossFileResolver`
11
+ * interface and self-declares which API frameworks it applies to.
12
+ */
13
+ import type { CrossFileResolver, CrossFileSymbolTable } from './types';
14
+ import type { DetectedApiFramework } from '../../../discovery/frameworkDetector';
15
+ /**
16
+ * Register a cross-file resolver. Resolvers are run in registration order.
17
+ */
18
+ export declare function registerCrossFileResolver(resolver: CrossFileResolver): void;
19
+ /**
20
+ * Return all registered cross-file resolvers.
21
+ */
22
+ export declare function getCrossFileResolvers(): readonly CrossFileResolver[];
23
+ /**
24
+ * Clear all registered resolvers (for testing).
25
+ */
26
+ export declare function clearCrossFileResolvers(): void;
27
+ export interface CrossFileResolutionPassResult {
28
+ /** Per-resolver diagnostics */
29
+ resolverResults: Array<{
30
+ resolverName: string;
31
+ entriesAdded: number;
32
+ diagnostics: string[];
33
+ unresolvedRefs: Array<{
34
+ ref: string;
35
+ reason: string;
36
+ }>;
37
+ }>;
38
+ /** Total entries added across all resolvers */
39
+ totalEntriesAdded: number;
40
+ }
41
+ /**
42
+ * Run all applicable cross-file resolvers against the symbol table.
43
+ *
44
+ * Each resolver mutates the symbol table in place, adding router mounts,
45
+ * injection chains, interface implementations, and middleware inheritance data.
46
+ *
47
+ * @param symbolTable - The cross-file symbol table to enrich
48
+ * @param projectRoot - Project root directory
49
+ * @param apiFrameworks - Detected API frameworks
50
+ * @param allSourceFiles - All source file paths
51
+ * @returns Aggregated diagnostics from all resolvers
52
+ */
53
+ export declare function runCrossFileResolution(symbolTable: CrossFileSymbolTable, projectRoot: string, apiFrameworks: DetectedApiFramework[], allSourceFiles: string[]): CrossFileResolutionPassResult;
54
+ //# sourceMappingURL=crossFileResolutionPass.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crossFileResolutionPass.d.ts","sourceRoot":"","sources":["../../../../../src/pipeline/stages/ast/crossFileResolutionPass.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EACV,iBAAiB,EAGjB,oBAAoB,EACrB,MAAM,SAAS,CAAC;AACjB,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sCAAsC,CAAC;AAMjF;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,iBAAiB,GAAG,IAAI,CAK3E;AAED;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,SAAS,iBAAiB,EAAE,CAEpE;AAED;;GAEG;AACH,wBAAgB,uBAAuB,IAAI,IAAI,CAE9C;AAID,MAAM,WAAW,6BAA6B;IAC5C,+BAA+B;IAC/B,eAAe,EAAE,KAAK,CAAC;QACrB,YAAY,EAAE,MAAM,CAAC;QACrB,YAAY,EAAE,MAAM,CAAC;QACrB,WAAW,EAAE,MAAM,EAAE,CAAC;QACtB,cAAc,EAAE,KAAK,CAAC;YAAE,GAAG,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;KACxD,CAAC,CAAC;IACH,+CAA+C;IAC/C,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,sBAAsB,CACpC,WAAW,EAAE,oBAAoB,EACjC,WAAW,EAAE,MAAM,EACnB,aAAa,EAAE,oBAAoB,EAAE,EACrC,cAAc,EAAE,MAAM,EAAE,GACvB,6BAA6B,CAoC/B"}
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ /**
3
+ * Cross-file resolution pass (Feature 27)
4
+ *
5
+ * Orchestrates all registered cross-file resolvers after the initial AST
6
+ * parsing pass. Each resolver enriches the CrossFileSymbolTable with
7
+ * framework-specific resolution data (Blueprint prefixes, Angular injection
8
+ * chains, DDD interface mappings, etc.).
9
+ *
10
+ * Resolvers are registered via `registerCrossFileResolver()` and discovered
11
+ * via `getCrossFileResolvers()`. Each resolver implements the `CrossFileResolver`
12
+ * interface and self-declares which API frameworks it applies to.
13
+ */
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.registerCrossFileResolver = registerCrossFileResolver;
16
+ exports.getCrossFileResolvers = getCrossFileResolvers;
17
+ exports.clearCrossFileResolvers = clearCrossFileResolvers;
18
+ exports.runCrossFileResolution = runCrossFileResolution;
19
+ // ─── Resolver registry ────────────────────────────────────────────────────────
20
+ const resolverRegistry = [];
21
+ /**
22
+ * Register a cross-file resolver. Resolvers are run in registration order.
23
+ */
24
+ function registerCrossFileResolver(resolver) {
25
+ // Avoid duplicate registrations
26
+ if (!resolverRegistry.some((r) => r.name === resolver.name)) {
27
+ resolverRegistry.push(resolver);
28
+ }
29
+ }
30
+ /**
31
+ * Return all registered cross-file resolvers.
32
+ */
33
+ function getCrossFileResolvers() {
34
+ return resolverRegistry;
35
+ }
36
+ /**
37
+ * Clear all registered resolvers (for testing).
38
+ */
39
+ function clearCrossFileResolvers() {
40
+ resolverRegistry.length = 0;
41
+ }
42
+ /**
43
+ * Run all applicable cross-file resolvers against the symbol table.
44
+ *
45
+ * Each resolver mutates the symbol table in place, adding router mounts,
46
+ * injection chains, interface implementations, and middleware inheritance data.
47
+ *
48
+ * @param symbolTable - The cross-file symbol table to enrich
49
+ * @param projectRoot - Project root directory
50
+ * @param apiFrameworks - Detected API frameworks
51
+ * @param allSourceFiles - All source file paths
52
+ * @returns Aggregated diagnostics from all resolvers
53
+ */
54
+ function runCrossFileResolution(symbolTable, projectRoot, apiFrameworks, allSourceFiles) {
55
+ const ctx = {
56
+ symbolTable,
57
+ projectRoot,
58
+ apiFrameworks,
59
+ allSourceFiles,
60
+ };
61
+ const resolverResults = [];
62
+ let totalEntriesAdded = 0;
63
+ for (const resolver of resolverRegistry) {
64
+ // Only run resolvers that apply to the detected frameworks
65
+ if (!resolver.appliesTo(apiFrameworks))
66
+ continue;
67
+ try {
68
+ const result = resolver.resolve(ctx);
69
+ resolverResults.push({
70
+ resolverName: resolver.name,
71
+ entriesAdded: result.entriesAdded,
72
+ diagnostics: result.diagnostics,
73
+ unresolvedRefs: result.unresolvedRefs,
74
+ });
75
+ totalEntriesAdded += result.entriesAdded;
76
+ }
77
+ catch (err) {
78
+ // Never let a resolver crash the pipeline
79
+ resolverResults.push({
80
+ resolverName: resolver.name,
81
+ entriesAdded: 0,
82
+ diagnostics: [`resolver-error: ${err instanceof Error ? err.message : String(err)}`],
83
+ unresolvedRefs: [],
84
+ });
85
+ }
86
+ }
87
+ return { resolverResults, totalEntriesAdded };
88
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"crossFileResolver.d.ts","sourceRoot":"","sources":["../../../../../src/pipeline/stages/ast/crossFileResolver.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAuC,MAAM,uBAAuB,CAAC;AAChG,OAAO,KAAK,EAAE,oBAAoB,EAAuC,MAAM,SAAS,CAAC;AAGzF;;;;;GAKG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,EAClC,WAAW,EAAE,MAAM,GAClB,oBAAoB,CAuDtB;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,oBAAoB,GAC1B,MAAM,GAAG,SAAS,CA4BpB"}
1
+ {"version":3,"file":"crossFileResolver.d.ts","sourceRoot":"","sources":["../../../../../src/pipeline/stages/ast/crossFileResolver.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAuC,MAAM,uBAAuB,CAAC;AAChG,OAAO,KAAK,EAAE,oBAAoB,EAAuC,MAAM,SAAS,CAAC;AAGzF;;;;;GAKG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,EAClC,WAAW,EAAE,MAAM,GAClB,oBAAoB,CAgEtB;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,oBAAoB,GAC1B,MAAM,GAAG,SAAS,CA4BpB"}
@@ -63,7 +63,16 @@ function buildCrossFileSymbolTable(models, projectRoot) {
63
63
  }
64
64
  importGraph.set(filePath, resolvedPaths);
65
65
  }
66
- return { models, exportedSymbols, classes, importGraph };
66
+ return {
67
+ models,
68
+ exportedSymbols,
69
+ classes,
70
+ importGraph,
71
+ routerMounts: new Map(),
72
+ injectionChains: new Map(),
73
+ interfaceImplementations: new Map(),
74
+ middlewareInheritance: new Map(),
75
+ };
67
76
  }
68
77
  /**
69
78
  * Resolve a symbol name using the cross-file symbol table.
@@ -98,12 +107,31 @@ function resolveSymbolCrossFile(symbolName, fromFile, table) {
98
107
  }
99
108
  return undefined;
100
109
  }
110
+ /** Built-in objects that should never be treated as import module names */
111
+ const BUILTIN_OBJECTS = new Set([
112
+ 'console', 'math', 'json', 'object', 'array', 'promise', 'date',
113
+ 'string', 'number', 'boolean', 'symbol', 'regexp', 'error', 'map',
114
+ 'set', 'weakmap', 'weakset', 'proxy', 'reflect', 'intl',
115
+ 'arraybuffer', 'sharedarraybuffer', 'atomics', 'dataview',
116
+ 'this', 'self', 'super', 'window', 'document', 'process',
117
+ 'global', 'globalthis', 'module', 'exports', 'require',
118
+ '__dirname', '__filename',
119
+ ]);
101
120
  /**
102
121
  * Extract import declarations from a semantic model.
103
122
  * This is a heuristic extraction — real import extraction would come from the AST.
104
123
  */
105
124
  function extractImportsFromModel(filePath, model, projectRoot) {
106
125
  const imports = [];
126
+ // Build a set of known identifiers from the model (constants and local variables)
127
+ // These are the names that could plausibly be import aliases
128
+ const knownIdentifiers = new Set();
129
+ for (const [name] of model.constants) {
130
+ knownIdentifiers.add(name);
131
+ }
132
+ for (const [name] of model.localVariables) {
133
+ knownIdentifiers.add(name);
134
+ }
107
135
  // Use functions' calledFunctions to infer cross-file references
108
136
  for (const [, func] of model.functions) {
109
137
  for (const calledName of func.calledFunctions) {
@@ -111,6 +139,16 @@ function extractImportsFromModel(filePath, model, projectRoot) {
111
139
  const dotIndex = calledName.indexOf('.');
112
140
  if (dotIndex > 0) {
113
141
  const modulePart = calledName.substring(0, dotIndex);
142
+ // Skip common built-in objects that are never import sources
143
+ if (BUILTIN_OBJECTS.has(modulePart.toLowerCase())) {
144
+ continue;
145
+ }
146
+ // Only treat as an import if the prefix matches a known identifier
147
+ // (constant or local variable) in this file, which is how import
148
+ // aliases typically appear in the semantic model
149
+ if (!knownIdentifiers.has(modulePart)) {
150
+ continue;
151
+ }
114
152
  const resolvedPath = (0, importResolver_1.resolveImportPath)(`./${modulePart}`, filePath, projectRoot, model.language);
115
153
  if (resolvedPath) {
116
154
  imports.push({
@@ -1 +1 @@
1
- {"version":3,"file":"graphBuilder.d.ts","sourceRoot":"","sources":["../../../../../src/pipeline/stages/ast/graphBuilder.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAoB,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AACtG,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,KAAK,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAIrE;;GAEG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,EAClC,cAAc,EAAE,oBAAoB,EACpC,gBAAgB,EAAE,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,EAC9C,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,uBAAuB,EAAE,CAAC,GACnD;IAAE,KAAK,EAAE,SAAS,EAAE,CAAC;IAAC,KAAK,EAAE,SAAS,EAAE,CAAA;CAAE,CAsN5C"}
1
+ {"version":3,"file":"graphBuilder.d.ts","sourceRoot":"","sources":["../../../../../src/pipeline/stages/ast/graphBuilder.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAoB,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AACtG,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAiB,MAAM,aAAa,CAAC;AACvE,OAAO,KAAK,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAIrE;;GAEG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,EAClC,cAAc,EAAE,oBAAoB,EACpC,gBAAgB,EAAE,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,EAC9C,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,uBAAuB,EAAE,CAAC,GACnD;IAAE,KAAK,EAAE,SAAS,EAAE,CAAC;IAAC,KAAK,EAAE,SAAS,EAAE,CAAA;CAAE,CA4S5C"}
@@ -60,6 +60,28 @@ function buildAstGraph(models, crossFileTable, traversalResults, interactions) {
60
60
  addedEdgeIds.add(edge.id);
61
61
  edges.push(edge);
62
62
  }
63
+ /**
64
+ * Ensure a node exists in the graph. If the node ID is not yet present,
65
+ * create a stub node flagged as cross-file-unresolved (spec §2.3).
66
+ * Confidence on stub nodes is capped at 'low'.
67
+ */
68
+ function ensureNodeExists(nodeId, missingFilePath, nodeType) {
69
+ if (addedNodeIds.has(nodeId))
70
+ return nodeId;
71
+ addNode({
72
+ id: nodeId,
73
+ type: nodeType,
74
+ label: `[unresolved] ${path.basename(missingFilePath)}`,
75
+ sourceStage: 'ast',
76
+ filePath: missingFilePath,
77
+ metadata: {
78
+ resolution: 'cross-file-unresolved',
79
+ confidence: 'low',
80
+ diagnostic: `Cross-file resolution failed: file "${missingFilePath}" was not found in parsed models`,
81
+ },
82
+ });
83
+ return nodeId;
84
+ }
63
85
  // 1. Create file nodes for every parsed file
64
86
  for (const [filePath, model] of models) {
65
87
  const basename = path.basename(filePath);
@@ -241,6 +263,65 @@ function buildAstGraph(models, crossFileTable, traversalResults, interactions) {
241
263
  });
242
264
  }
243
265
  }
266
+ // 7. Feature 27: Create router mount edges
267
+ for (const [filePath, mounts] of crossFileTable.routerMounts) {
268
+ const sourceId = `file:${filePath}`;
269
+ if (!addedNodeIds.has(sourceId))
270
+ continue;
271
+ for (const mount of mounts) {
272
+ const targetId = `file:${mount.targetModulePath}`;
273
+ // Create edge even if target not parsed (creates stub node for missing files)
274
+ addEdge({
275
+ id: `${sourceId}->mounts->${mount.prefix}:${mount.targetModulePath}`,
276
+ type: 'router-mount',
277
+ sourceNodeId: sourceId,
278
+ targetNodeId: ensureNodeExists(targetId, mount.targetModulePath, 'file'),
279
+ sourceStage: 'ast',
280
+ metadata: {
281
+ prefix: mount.prefix,
282
+ targetModule: mount.targetModulePath,
283
+ middlewareCount: mount.middleware.length,
284
+ },
285
+ });
286
+ }
287
+ }
288
+ // 8. Feature 27: Create injection chain edges
289
+ for (const [consumerFile, chains] of crossFileTable.injectionChains) {
290
+ for (const chain of chains) {
291
+ const consumerNodeId = `file:${chain.consumerFile}`;
292
+ const serviceNodeId = `file:${chain.serviceFile}`;
293
+ addEdge({
294
+ id: `${consumerNodeId}->injects->${chain.serviceClass}:${chain.serviceFile}`,
295
+ type: 'injects',
296
+ sourceNodeId: ensureNodeExists(consumerNodeId, chain.consumerFile, 'file'),
297
+ targetNodeId: ensureNodeExists(serviceNodeId, chain.serviceFile, 'file'),
298
+ sourceStage: 'ast',
299
+ metadata: {
300
+ consumerClass: chain.consumerClass,
301
+ serviceClass: chain.serviceClass,
302
+ injectionStyle: chain.injectionStyle,
303
+ },
304
+ });
305
+ }
306
+ }
307
+ // 9. Feature 27: Create interface implementation edges
308
+ for (const [ifaceFile, impls] of crossFileTable.interfaceImplementations) {
309
+ for (const impl of impls) {
310
+ const ifaceNodeId = `file:${impl.interfaceFile}`;
311
+ const implNodeId = `file:${impl.implFile}`;
312
+ addEdge({
313
+ id: `${implNodeId}->implements->${impl.interfaceName}:${impl.interfaceFile}`,
314
+ type: 'implements',
315
+ sourceNodeId: ensureNodeExists(implNodeId, impl.implFile, 'file'),
316
+ targetNodeId: ensureNodeExists(ifaceNodeId, impl.interfaceFile, 'file'),
317
+ sourceStage: 'ast',
318
+ metadata: {
319
+ interfaceName: impl.interfaceName,
320
+ implName: impl.implName,
321
+ },
322
+ });
323
+ }
324
+ }
244
325
  return { nodes, edges };
245
326
  }
246
327
  /**
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Optional auth unifier (Feature 27, Sub-PR 9)
3
+ *
4
+ * Normalizes all framework-specific auth classifications to a unified SecurityClassification.
5
+ * Maps framework-specific patterns:
6
+ * Flask @jwt_optional → { optional: true }
7
+ * Express auth.optional / credentialsRequired: false → { optional: true }
8
+ * HapiJS auth: { mode: 'try' } → { optional: true }
9
+ * Spring @PreAuthorize → { required: true }
10
+ * Slim PHP optionalAuth → { optional: true }, jwt/auth → { required: true }
11
+ * Angular canActivate:none → { optional: true } (interceptor-based)
12
+ * NestJS @UseGuards → { required: true }
13
+ */
14
+ import type { SecurityClassification } from '../../../ast/astTypes';
15
+ export interface AuthClassificationEntry {
16
+ framework: string;
17
+ sourcePattern: string;
18
+ classification: SecurityClassification;
19
+ }
20
+ /**
21
+ * Framework-specific auth pattern → unified SecurityClassification.
22
+ */
23
+ export declare function unifyAuthClassification(framework: string, pattern: string): SecurityClassification | undefined;
24
+ /**
25
+ * Detect auth coverage gaps.
26
+ */
27
+ export interface AuthCoverageGap {
28
+ type: 'missing-401-test' | 'missing-optional-dual-path';
29
+ endpointPath: string;
30
+ security: SecurityClassification;
31
+ sourceFile: string;
32
+ }
33
+ /**
34
+ * Analyze auth coverage gaps in endpoints vs tests.
35
+ */
36
+ export declare function detectAuthCoverageGaps(endpoints: Array<{
37
+ path: string;
38
+ security?: SecurityClassification;
39
+ sourceFile: string;
40
+ }>, testAssertionPaths: Set<string>): AuthCoverageGap[];
41
+ //# sourceMappingURL=optionalAuthUnifier.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"optionalAuthUnifier.d.ts","sourceRoot":"","sources":["../../../../../src/pipeline/stages/ast/optionalAuthUnifier.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAEpE,MAAM,WAAW,uBAAuB;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,sBAAsB,CAAC;CACxC;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,GACd,sBAAsB,GAAG,SAAS,CA6DpC;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,kBAAkB,GAAG,4BAA4B,CAAC;IACxD,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,sBAAsB,CAAC;IACjC,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,sBAAsB,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC,EACzF,kBAAkB,EAAE,GAAG,CAAC,MAAM,CAAC,GAC9B,eAAe,EAAE,CA0BnB"}