api-tests-coverage 1.0.13 → 1.0.14

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 (100) hide show
  1. package/dist/src/pipeline/confidence.d.ts +70 -0
  2. package/dist/src/pipeline/confidence.d.ts.map +1 -0
  3. package/dist/src/pipeline/confidence.js +198 -0
  4. package/dist/src/pipeline/graph.d.ts +58 -0
  5. package/dist/src/pipeline/graph.d.ts.map +1 -0
  6. package/dist/src/pipeline/graph.js +199 -0
  7. package/dist/src/pipeline/index.d.ts +24 -0
  8. package/dist/src/pipeline/index.d.ts.map +1 -0
  9. package/dist/src/pipeline/index.js +41 -0
  10. package/dist/src/pipeline/orchestrator.d.ts +42 -0
  11. package/dist/src/pipeline/orchestrator.d.ts.map +1 -0
  12. package/dist/src/pipeline/orchestrator.js +115 -0
  13. package/dist/src/pipeline/stageInterface.d.ts +45 -0
  14. package/dist/src/pipeline/stageInterface.d.ts.map +1 -0
  15. package/dist/src/pipeline/stageInterface.js +17 -0
  16. package/dist/src/pipeline/stages/ast/abstractLayerTraversal.d.ts +38 -0
  17. package/dist/src/pipeline/stages/ast/abstractLayerTraversal.d.ts.map +1 -0
  18. package/dist/src/pipeline/stages/ast/abstractLayerTraversal.js +203 -0
  19. package/dist/src/pipeline/stages/ast/astStage.d.ts +19 -0
  20. package/dist/src/pipeline/stages/ast/astStage.d.ts.map +1 -0
  21. package/dist/src/pipeline/stages/ast/astStage.js +238 -0
  22. package/dist/src/pipeline/stages/ast/crossFileResolver.d.ts +23 -0
  23. package/dist/src/pipeline/stages/ast/crossFileResolver.d.ts.map +1 -0
  24. package/dist/src/pipeline/stages/ast/crossFileResolver.js +183 -0
  25. package/dist/src/pipeline/stages/ast/graphBuilder.d.ts +15 -0
  26. package/dist/src/pipeline/stages/ast/graphBuilder.d.ts.map +1 -0
  27. package/dist/src/pipeline/stages/ast/graphBuilder.js +268 -0
  28. package/dist/src/pipeline/stages/ast/importResolver.d.ts +22 -0
  29. package/dist/src/pipeline/stages/ast/importResolver.d.ts.map +1 -0
  30. package/dist/src/pipeline/stages/ast/importResolver.js +186 -0
  31. package/dist/src/pipeline/stages/ast/types.d.ts +85 -0
  32. package/dist/src/pipeline/stages/ast/types.d.ts.map +1 -0
  33. package/dist/src/pipeline/stages/ast/types.js +5 -0
  34. package/dist/src/pipeline/stages/dast/conflictEmitter.d.ts +25 -0
  35. package/dist/src/pipeline/stages/dast/conflictEmitter.d.ts.map +1 -0
  36. package/dist/src/pipeline/stages/dast/conflictEmitter.js +90 -0
  37. package/dist/src/pipeline/stages/dast/dastStage.d.ts +17 -0
  38. package/dist/src/pipeline/stages/dast/dastStage.d.ts.map +1 -0
  39. package/dist/src/pipeline/stages/dast/dastStage.js +203 -0
  40. package/dist/src/pipeline/stages/dast/types.d.ts +49 -0
  41. package/dist/src/pipeline/stages/dast/types.d.ts.map +1 -0
  42. package/dist/src/pipeline/stages/dast/types.js +9 -0
  43. package/dist/src/pipeline/stages/iast/iastStage.d.ts +17 -0
  44. package/dist/src/pipeline/stages/iast/iastStage.d.ts.map +1 -0
  45. package/dist/src/pipeline/stages/iast/iastStage.js +191 -0
  46. package/dist/src/pipeline/stages/iast/types.d.ts +48 -0
  47. package/dist/src/pipeline/stages/iast/types.d.ts.map +1 -0
  48. package/dist/src/pipeline/stages/iast/types.js +8 -0
  49. package/dist/src/pipeline/stages/merge/conflictDetector.d.ts +17 -0
  50. package/dist/src/pipeline/stages/merge/conflictDetector.d.ts.map +1 -0
  51. package/dist/src/pipeline/stages/merge/conflictDetector.js +60 -0
  52. package/dist/src/pipeline/stages/merge/coverageMappingBuilder.d.ts +15 -0
  53. package/dist/src/pipeline/stages/merge/coverageMappingBuilder.d.ts.map +1 -0
  54. package/dist/src/pipeline/stages/merge/coverageMappingBuilder.js +141 -0
  55. package/dist/src/pipeline/stages/merge/mergeRules.d.ts +39 -0
  56. package/dist/src/pipeline/stages/merge/mergeRules.d.ts.map +1 -0
  57. package/dist/src/pipeline/stages/merge/mergeRules.js +90 -0
  58. package/dist/src/pipeline/stages/merge/mergeStage.d.ts +20 -0
  59. package/dist/src/pipeline/stages/merge/mergeStage.d.ts.map +1 -0
  60. package/dist/src/pipeline/stages/merge/mergeStage.js +145 -0
  61. package/dist/src/pipeline/stages/merge/summaryComputer.d.ts +11 -0
  62. package/dist/src/pipeline/stages/merge/summaryComputer.d.ts.map +1 -0
  63. package/dist/src/pipeline/stages/merge/summaryComputer.js +46 -0
  64. package/dist/src/pipeline/stages/sca/ciDetector.d.ts +15 -0
  65. package/dist/src/pipeline/stages/sca/ciDetector.d.ts.map +1 -0
  66. package/dist/src/pipeline/stages/sca/ciDetector.js +87 -0
  67. package/dist/src/pipeline/stages/sca/dependencyClassification.d.ts +31 -0
  68. package/dist/src/pipeline/stages/sca/dependencyClassification.d.ts.map +1 -0
  69. package/dist/src/pipeline/stages/sca/dependencyClassification.js +296 -0
  70. package/dist/src/pipeline/stages/sca/dependencyDetector.d.ts +25 -0
  71. package/dist/src/pipeline/stages/sca/dependencyDetector.d.ts.map +1 -0
  72. package/dist/src/pipeline/stages/sca/dependencyDetector.js +416 -0
  73. package/dist/src/pipeline/stages/sca/scaStage.d.ts +21 -0
  74. package/dist/src/pipeline/stages/sca/scaStage.d.ts.map +1 -0
  75. package/dist/src/pipeline/stages/sca/scaStage.js +208 -0
  76. package/dist/src/pipeline/stages/sca/types.d.ts +61 -0
  77. package/dist/src/pipeline/stages/sca/types.d.ts.map +1 -0
  78. package/dist/src/pipeline/stages/sca/types.js +9 -0
  79. package/dist/src/pipeline/stages/tia/mockBoundaryDetector.d.ts +19 -0
  80. package/dist/src/pipeline/stages/tia/mockBoundaryDetector.d.ts.map +1 -0
  81. package/dist/src/pipeline/stages/tia/mockBoundaryDetector.js +118 -0
  82. package/dist/src/pipeline/stages/tia/parameterizedTestExpander.d.ts +20 -0
  83. package/dist/src/pipeline/stages/tia/parameterizedTestExpander.d.ts.map +1 -0
  84. package/dist/src/pipeline/stages/tia/parameterizedTestExpander.js +238 -0
  85. package/dist/src/pipeline/stages/tia/testEndpointMapper.d.ts +22 -0
  86. package/dist/src/pipeline/stages/tia/testEndpointMapper.d.ts.map +1 -0
  87. package/dist/src/pipeline/stages/tia/testEndpointMapper.js +134 -0
  88. package/dist/src/pipeline/stages/tia/testLayerClassifier.d.ts +16 -0
  89. package/dist/src/pipeline/stages/tia/testLayerClassifier.d.ts.map +1 -0
  90. package/dist/src/pipeline/stages/tia/testLayerClassifier.js +191 -0
  91. package/dist/src/pipeline/stages/tia/tiaStage.d.ts +20 -0
  92. package/dist/src/pipeline/stages/tia/tiaStage.d.ts.map +1 -0
  93. package/dist/src/pipeline/stages/tia/tiaStage.js +215 -0
  94. package/dist/src/pipeline/stages/tia/types.d.ts +52 -0
  95. package/dist/src/pipeline/stages/tia/types.d.ts.map +1 -0
  96. package/dist/src/pipeline/stages/tia/types.js +5 -0
  97. package/dist/src/pipeline/types.d.ts +128 -0
  98. package/dist/src/pipeline/types.d.ts.map +1 -0
  99. package/dist/src/pipeline/types.js +9 -0
  100. package/package.json +1 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conflictDetector.d.ts","sourceRoot":"","sources":["../../../../../src/pipeline/stages/merge/conflictDetector.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,YAAY,EAA2B,MAAM,aAAa,CAAC;AACzE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAC1D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEhD;;GAEG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,sBAAsB,EAC7B,UAAU,EAAE,UAAU,GAAG,SAAS,EAClC,UAAU,EAAE,UAAU,GAAG,SAAS,GACjC,YAAY,EAAE,CA0ChB"}
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ /**
3
+ * Conflict detector — detects stage disagreements and structural conflicts
4
+ * in the knowledge graph after all stages have run.
5
+ *
6
+ * Detects:
7
+ * - Runtime-unconfirmed: AST+TIA say covered, but IAST/DAST disagree
8
+ * - Stage disagreement: Two stages assign different types to the same node
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.detectConflicts = detectConflicts;
12
+ /**
13
+ * Detect conflicts across all stages.
14
+ */
15
+ function detectConflicts(graph, iastOutput, dastOutput) {
16
+ var _a, _b, _c, _d, _e;
17
+ const conflicts = [];
18
+ // 1. Runtime-unconfirmed: endpoints that AST found but neither IAST nor DAST confirmed
19
+ const endpointNodes = graph.getNodesByType('endpoint');
20
+ const iastConfirmed = new Set((_a = iastOutput === null || iastOutput === void 0 ? void 0 : iastOutput.confirmedEndpoints) !== null && _a !== void 0 ? _a : []);
21
+ const dastConfirmed = new Set((_b = dastOutput === null || dastOutput === void 0 ? void 0 : dastOutput.confirmedEndpoints) !== null && _b !== void 0 ? _b : []);
22
+ const dastUnreachable = new Set((_c = dastOutput === null || dastOutput === void 0 ? void 0 : dastOutput.unreachableEndpoints) !== null && _c !== void 0 ? _c : []);
23
+ const hasRuntimeData = ((_d = iastOutput === null || iastOutput === void 0 ? void 0 : iastOutput.events.length) !== null && _d !== void 0 ? _d : 0) > 0 || ((_e = dastOutput === null || dastOutput === void 0 ? void 0 : dastOutput.results.length) !== null && _e !== void 0 ? _e : 0) > 0;
24
+ if (hasRuntimeData) {
25
+ for (const node of endpointNodes) {
26
+ // Only check endpoints from static stages (AST/TIA)
27
+ if (node.sourceStage !== 'ast' && node.sourceStage !== 'sca')
28
+ continue;
29
+ const confirmed = iastConfirmed.has(node.id) || dastConfirmed.has(node.id);
30
+ const unreachable = dastUnreachable.has(node.id);
31
+ if (!confirmed && !unreachable) {
32
+ conflicts.push({
33
+ conflictId: `conflict:runtime-unconfirmed:${node.id}`,
34
+ nodeId: node.id,
35
+ type: 'runtime-unconfirmed',
36
+ stages: getRuntimeStages(iastOutput, dastOutput),
37
+ stageOutputs: {
38
+ ast: 'endpoint-declared',
39
+ iast: iastConfirmed.has(node.id) ? 'confirmed' : 'not-observed',
40
+ dast: dastConfirmed.has(node.id) ? 'confirmed' : 'not-probed',
41
+ },
42
+ detail: `${node.label} was found by static analysis but not confirmed by any runtime stage.`,
43
+ suggestedAction: 'Check if the endpoint is covered by integration/e2e tests that produce IAST events or DAST probes.',
44
+ severity: 'info',
45
+ });
46
+ }
47
+ }
48
+ }
49
+ // 2. Stage disagreement: same node ID added by multiple stages with different types
50
+ // (This is handled by the graph merge(), which already returns ConflictNode[])
51
+ return conflicts;
52
+ }
53
+ function getRuntimeStages(iastOutput, dastOutput) {
54
+ const stages = ['ast'];
55
+ if (iastOutput && iastOutput.events.length > 0)
56
+ stages.push('iast');
57
+ if (dastOutput && dastOutput.results.length > 0)
58
+ stages.push('dast');
59
+ return stages;
60
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Coverage mapping builder — constructs `CoverageMapping` entries for every
3
+ * endpoint in the graph by walking its connected test files, assertions,
4
+ * mock boundaries, and runtime confirmations.
5
+ */
6
+ import type { CoverageMapping } from '../../types';
7
+ import type { CoverageKnowledgeGraph } from '../../graph';
8
+ import type { TiaOutput } from '../tia/types';
9
+ import type { IastOutput } from '../iast/types';
10
+ import type { DastOutput } from '../dast/types';
11
+ /**
12
+ * Build coverage mappings for all endpoint-type nodes in the graph.
13
+ */
14
+ export declare function buildCoverageMappings(graph: CoverageKnowledgeGraph, tiaOutput: TiaOutput | undefined, iastOutput: IastOutput | undefined, dastOutput: DastOutput | undefined, isStaticOnlyMode: boolean): CoverageMapping[];
15
+ //# sourceMappingURL=coverageMappingBuilder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"coverageMappingBuilder.d.ts","sourceRoot":"","sources":["../../../../../src/pipeline/stages/merge/coverageMappingBuilder.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EACV,eAAe,EAMhB,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAC1D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAIhD;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,sBAAsB,EAC7B,SAAS,EAAE,SAAS,GAAG,SAAS,EAChC,UAAU,EAAE,UAAU,GAAG,SAAS,EAClC,UAAU,EAAE,UAAU,GAAG,SAAS,EAClC,gBAAgB,EAAE,OAAO,GACxB,eAAe,EAAE,CAuJnB"}
@@ -0,0 +1,141 @@
1
+ "use strict";
2
+ /**
3
+ * Coverage mapping builder — constructs `CoverageMapping` entries for every
4
+ * endpoint in the graph by walking its connected test files, assertions,
5
+ * mock boundaries, and runtime confirmations.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.buildCoverageMappings = buildCoverageMappings;
9
+ const confidence_1 = require("../../confidence");
10
+ const mergeRules_1 = require("./mergeRules");
11
+ /**
12
+ * Build coverage mappings for all endpoint-type nodes in the graph.
13
+ */
14
+ function buildCoverageMappings(graph, tiaOutput, iastOutput, dastOutput, isStaticOnlyMode) {
15
+ var _a, _b, _c, _d, _e, _f, _g, _h;
16
+ const mappings = [];
17
+ const endpointNodes = graph.getNodesByType('endpoint');
18
+ const iastConfirmed = new Set((_a = iastOutput === null || iastOutput === void 0 ? void 0 : iastOutput.confirmedEndpoints) !== null && _a !== void 0 ? _a : []);
19
+ const dastConfirmed = new Set((_b = dastOutput === null || dastOutput === void 0 ? void 0 : dastOutput.confirmedEndpoints) !== null && _b !== void 0 ? _b : []);
20
+ const dastUnreachable = new Set((_c = dastOutput === null || dastOutput === void 0 ? void 0 : dastOutput.unreachableEndpoints) !== null && _c !== void 0 ? _c : []);
21
+ // Build a lookup: endpointId → test classifications
22
+ const testLayerLookup = new Map();
23
+ if (tiaOutput) {
24
+ for (const classification of tiaOutput.classifications) {
25
+ testLayerLookup.set(classification.filePath, classification.layer);
26
+ }
27
+ }
28
+ // Build a lookup: endpointId → mock boundary test files
29
+ const mockBoundaryLookup = new Map();
30
+ if (tiaOutput) {
31
+ for (const boundary of tiaOutput.mockBoundaries) {
32
+ if (!mockBoundaryLookup.has(boundary.testFilePath)) {
33
+ mockBoundaryLookup.set(boundary.testFilePath, new Set());
34
+ }
35
+ mockBoundaryLookup.get(boundary.testFilePath).add(boundary.mockedTarget);
36
+ }
37
+ }
38
+ for (const endpoint of endpointNodes) {
39
+ // Find linked tests (incoming 'tests' and 'asserts' edges)
40
+ const incomingEdges = graph.getEdgesTo(endpoint.id);
41
+ const testEdges = incomingEdges.filter((e) => e.type === 'tests' || e.type === 'asserts');
42
+ const linkedTests = [];
43
+ const sourceStages = new Set([endpoint.sourceStage]);
44
+ let assertionConfirmed = false;
45
+ let assertionSource = 'unresolved';
46
+ let hasMockBoundary = false;
47
+ let bestTestLayer;
48
+ let urlResolution = 'literal';
49
+ let traversalDepth = 0;
50
+ for (const edge of testEdges) {
51
+ // Extract file path from source node ID (format: file:/path/to/test.ts)
52
+ const testFilePath = edge.sourceNodeId.replace(/^file:/, '');
53
+ if (!linkedTests.includes(testFilePath)) {
54
+ linkedTests.push(testFilePath);
55
+ }
56
+ sourceStages.add(edge.sourceStage);
57
+ // Check if this test has assertions
58
+ if (edge.type === 'asserts') {
59
+ assertionConfirmed = true;
60
+ assertionSource = 'direct';
61
+ }
62
+ // Check test layer
63
+ const layer = testLayerLookup.get(testFilePath);
64
+ if (layer) {
65
+ bestTestLayer = layer;
66
+ }
67
+ // Check for mock boundaries on this test file
68
+ if (mockBoundaryLookup.has(testFilePath)) {
69
+ hasMockBoundary = true;
70
+ }
71
+ }
72
+ // Check for assertion nodes linked to this file
73
+ if (!assertionConfirmed) {
74
+ for (const testFile of linkedTests) {
75
+ const assertionNodeId = `assertion:${testFile}`;
76
+ if (graph.hasNode(assertionNodeId)) {
77
+ const assertionNode = graph.getNode(assertionNodeId);
78
+ if (assertionNode) {
79
+ assertionConfirmed = true;
80
+ assertionSource = (_e = (_d = assertionNode.metadata) === null || _d === void 0 ? void 0 : _d.assertionSource) !== null && _e !== void 0 ? _e : 'direct';
81
+ traversalDepth = (_g = (_f = assertionNode.metadata) === null || _f === void 0 ? void 0 : _f.traversalDepth) !== null && _g !== void 0 ? _g : 0;
82
+ }
83
+ }
84
+ }
85
+ }
86
+ // Determine URL resolution type from endpoint metadata
87
+ const resolutionType = (_h = endpoint.metadata) === null || _h === void 0 ? void 0 : _h.resolutionType;
88
+ if (resolutionType === 'heuristic' || resolutionType === 'string-template' || resolutionType === 'interpolated-path') {
89
+ urlResolution = 'symbolic';
90
+ }
91
+ else if (resolutionType === 'direct' || resolutionType === 'constant' || resolutionType === 'enum') {
92
+ urlResolution = 'literal';
93
+ }
94
+ else if (!resolutionType) {
95
+ urlResolution = 'unresolved';
96
+ }
97
+ // Check mock boundaries on path
98
+ const pathNodeIds = [endpoint.id, ...linkedTests.map((t) => `file:${t}`)];
99
+ const mockBoundaryOnPath = hasMockBoundary;
100
+ // Build confidence evidence
101
+ const evidence = (0, confidence_1.buildConfidenceEvidence)(graph, pathNodeIds, isStaticOnlyMode);
102
+ // Compute confidence
103
+ const confidence = (0, confidence_1.computeConfidence)(evidence);
104
+ // Determine coverage class
105
+ const coverageClass = linkedTests.length > 0
106
+ ? (0, mergeRules_1.determineCoverageClass)(bestTestLayer, mockBoundaryOnPath)
107
+ : 'uncovered';
108
+ // Check DAST reachability
109
+ const dastReachable = dastConfirmed.has(endpoint.id)
110
+ ? true
111
+ : dastUnreachable.has(endpoint.id)
112
+ ? false
113
+ : 'not-probed';
114
+ // Check runtime confirmation
115
+ const runtimeConfirmed = iastConfirmed.has(endpoint.id) || dastConfirmed.has(endpoint.id);
116
+ // Collect conflict IDs linked to this endpoint
117
+ const conflictEdges = graph.getEdgesTo(endpoint.id).filter((e) => e.type === 'conflicts-with');
118
+ const conflicts = conflictEdges.map((e) => e.sourceNodeId);
119
+ mappings.push({
120
+ itemId: endpoint.id,
121
+ itemType: 'endpoint',
122
+ linkedTests,
123
+ sourceStages: Array.from(sourceStages),
124
+ confidence,
125
+ coverageClass,
126
+ mockBoundaries: mockBoundaryOnPath
127
+ ? linkedTests
128
+ .filter((t) => mockBoundaryLookup.has(t))
129
+ .flatMap((t) => Array.from(mockBoundaryLookup.get(t)))
130
+ : [],
131
+ assertionSource,
132
+ assertionConfirmed,
133
+ dastReachable,
134
+ runtimeConfirmed,
135
+ urlResolution,
136
+ conflicts,
137
+ traversalDepth,
138
+ });
139
+ }
140
+ return mappings;
141
+ }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Merge rules — defines how graph nodes and edges from different stages
3
+ * are reconciled during the final merge stage.
4
+ *
5
+ * Core principle (RULE-09): A later stage may raise a node's confidence
6
+ * or enrich its test evidence, but MUST NOT demote it.
7
+ */
8
+ import type { PipelineConfidence, CoverageClass, TestLayer, StageName } from '../../types';
9
+ /**
10
+ * RULE-09: Confidence can only be raised, never demoted.
11
+ *
12
+ * Returns the higher of two confidence levels.
13
+ */
14
+ export declare function mergeConfidence(existing: PipelineConfidence, incoming: PipelineConfidence): PipelineConfidence;
15
+ /**
16
+ * Merge test evidence arrays (de-duplicate).
17
+ */
18
+ export declare function mergeTestEvidence(existing: string[], incoming: string[]): string[];
19
+ /**
20
+ * Merge source stages (de-duplicate).
21
+ */
22
+ export declare function mergeSourceStages(existing: StageName[], incoming: StageName[]): StageName[];
23
+ /**
24
+ * Determine coverage class from test layer and mock boundary presence.
25
+ *
26
+ * RULE-03: If a mock boundary exists on the path, the coverage class
27
+ * is 'mock-covered' regardless of the test layer.
28
+ */
29
+ export declare function determineCoverageClass(testLayer: TestLayer | undefined, hasMockBoundary: boolean): CoverageClass;
30
+ /**
31
+ * Rank coverage classes for comparison (higher is better).
32
+ */
33
+ export declare function coverageClassRank(cls: CoverageClass): number;
34
+ /**
35
+ * Merge two coverage classes — keep the highest rank.
36
+ * RULE-09: Never demote coverage class.
37
+ */
38
+ export declare function mergeCoverageClass(existing: CoverageClass, incoming: CoverageClass): CoverageClass;
39
+ //# sourceMappingURL=mergeRules.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mergeRules.d.ts","sourceRoot":"","sources":["../../../../../src/pipeline/stages/merge/mergeRules.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,aAAa,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE3F;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,kBAAkB,EAC5B,QAAQ,EAAE,kBAAkB,GAC3B,kBAAkB,CAKpB;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAIlF;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,SAAS,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,SAAS,EAAE,CAI3F;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,SAAS,GAAG,SAAS,EAChC,eAAe,EAAE,OAAO,GACvB,aAAa,CAef;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAW5D;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,aAAa,EAAE,QAAQ,EAAE,aAAa,GAAG,aAAa,CAElG"}
@@ -0,0 +1,90 @@
1
+ "use strict";
2
+ /**
3
+ * Merge rules — defines how graph nodes and edges from different stages
4
+ * are reconciled during the final merge stage.
5
+ *
6
+ * Core principle (RULE-09): A later stage may raise a node's confidence
7
+ * or enrich its test evidence, but MUST NOT demote it.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.mergeConfidence = mergeConfidence;
11
+ exports.mergeTestEvidence = mergeTestEvidence;
12
+ exports.mergeSourceStages = mergeSourceStages;
13
+ exports.determineCoverageClass = determineCoverageClass;
14
+ exports.coverageClassRank = coverageClassRank;
15
+ exports.mergeCoverageClass = mergeCoverageClass;
16
+ /**
17
+ * RULE-09: Confidence can only be raised, never demoted.
18
+ *
19
+ * Returns the higher of two confidence levels.
20
+ */
21
+ function mergeConfidence(existing, incoming) {
22
+ const order = ['low', 'medium', 'high', 'verified'];
23
+ const existingIdx = order.indexOf(existing);
24
+ const incomingIdx = order.indexOf(incoming);
25
+ return existingIdx >= incomingIdx ? existing : incoming;
26
+ }
27
+ /**
28
+ * Merge test evidence arrays (de-duplicate).
29
+ */
30
+ function mergeTestEvidence(existing, incoming) {
31
+ const set = new Set(existing);
32
+ for (const item of incoming)
33
+ set.add(item);
34
+ return Array.from(set);
35
+ }
36
+ /**
37
+ * Merge source stages (de-duplicate).
38
+ */
39
+ function mergeSourceStages(existing, incoming) {
40
+ const set = new Set(existing);
41
+ for (const item of incoming)
42
+ set.add(item);
43
+ return Array.from(set);
44
+ }
45
+ /**
46
+ * Determine coverage class from test layer and mock boundary presence.
47
+ *
48
+ * RULE-03: If a mock boundary exists on the path, the coverage class
49
+ * is 'mock-covered' regardless of the test layer.
50
+ */
51
+ function determineCoverageClass(testLayer, hasMockBoundary) {
52
+ var _a;
53
+ if (hasMockBoundary)
54
+ return 'mock-covered';
55
+ if (!testLayer)
56
+ return 'uncovered';
57
+ const layerToCoverageClass = {
58
+ unit: 'unit-covered',
59
+ component: 'component-covered',
60
+ integration: 'integration-covered',
61
+ api: 'api-covered',
62
+ e2e: 'e2e-covered',
63
+ performance: 'api-covered', // Performance tests are API-level coverage
64
+ security: 'api-covered', // Security tests are API-level coverage
65
+ };
66
+ return (_a = layerToCoverageClass[testLayer]) !== null && _a !== void 0 ? _a : 'uncovered';
67
+ }
68
+ /**
69
+ * Rank coverage classes for comparison (higher is better).
70
+ */
71
+ function coverageClassRank(cls) {
72
+ var _a;
73
+ const ranks = {
74
+ uncovered: 0,
75
+ 'mock-covered': 1,
76
+ 'unit-covered': 2,
77
+ 'component-covered': 3,
78
+ 'integration-covered': 4,
79
+ 'api-covered': 5,
80
+ 'e2e-covered': 6,
81
+ };
82
+ return (_a = ranks[cls]) !== null && _a !== void 0 ? _a : 0;
83
+ }
84
+ /**
85
+ * Merge two coverage classes — keep the highest rank.
86
+ * RULE-09: Never demote coverage class.
87
+ */
88
+ function mergeCoverageClass(existing, incoming) {
89
+ return coverageClassRank(existing) >= coverageClassRank(incoming) ? existing : incoming;
90
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Merge Stage — Stage 6 (final) of the coverage pipeline.
3
+ *
4
+ * Responsibilities:
5
+ * 1. Run conflict detection across all stage outputs
6
+ * 2. Build coverage mappings for every endpoint
7
+ * 3. Compute the final pipeline summary
8
+ * 4. Produce the `PipelineOutput` with graph, sections, diagnostics, summary
9
+ */
10
+ import type { PipelineStage, PipelineContext } from '../../stageInterface';
11
+ import type { PipelineOutput } from '../../types';
12
+ /**
13
+ * Output of the merge stage is the full `PipelineOutput`.
14
+ */
15
+ export declare class MergeStage implements PipelineStage<PipelineOutput> {
16
+ readonly name: "merge";
17
+ readonly optional = false;
18
+ execute(context: PipelineContext): Promise<PipelineOutput>;
19
+ }
20
+ //# sourceMappingURL=mergeStage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mergeStage.d.ts","sourceRoot":"","sources":["../../../../../src/pipeline/stages/merge/mergeStage.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC3E,OAAO,KAAK,EAEV,cAAc,EAMf,MAAM,aAAa,CAAC;AAQrB;;GAEG;AACH,qBAAa,UAAW,YAAW,aAAa,CAAC,cAAc,CAAC;IAC9D,QAAQ,CAAC,IAAI,EAAG,OAAO,CAAU;IACjC,QAAQ,CAAC,QAAQ,SAAS;IAEpB,OAAO,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,cAAc,CAAC;CAmGjE"}
@@ -0,0 +1,145 @@
1
+ "use strict";
2
+ /**
3
+ * Merge Stage — Stage 6 (final) of the coverage pipeline.
4
+ *
5
+ * Responsibilities:
6
+ * 1. Run conflict detection across all stage outputs
7
+ * 2. Build coverage mappings for every endpoint
8
+ * 3. Compute the final pipeline summary
9
+ * 4. Produce the `PipelineOutput` with graph, sections, diagnostics, summary
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.MergeStage = void 0;
13
+ const conflictDetector_1 = require("./conflictDetector");
14
+ const coverageMappingBuilder_1 = require("./coverageMappingBuilder");
15
+ const summaryComputer_1 = require("./summaryComputer");
16
+ /**
17
+ * Output of the merge stage is the full `PipelineOutput`.
18
+ */
19
+ class MergeStage {
20
+ constructor() {
21
+ this.name = 'merge';
22
+ this.optional = false;
23
+ }
24
+ async execute(context) {
25
+ var _a, _b;
26
+ const startTime = Date.now();
27
+ // Retrieve upstream stage outputs
28
+ const tiaOutput = context.stageOutputs.get('tia');
29
+ const iastOutput = context.stageOutputs.get('iast');
30
+ const dastOutput = context.stageOutputs.get('dast');
31
+ // Determine if we're in static-only mode (no IAST/DAST data)
32
+ const isStaticOnlyMode = ((_a = iastOutput === null || iastOutput === void 0 ? void 0 : iastOutput.events.length) !== null && _a !== void 0 ? _a : 0) === 0 && ((_b = dastOutput === null || dastOutput === void 0 ? void 0 : dastOutput.results.length) !== null && _b !== void 0 ? _b : 0) === 0;
33
+ // 1. Detect conflicts
34
+ const conflicts = (0, conflictDetector_1.detectConflicts)(context.graph, iastOutput, dastOutput);
35
+ // Add conflict nodes to graph
36
+ for (const conflict of conflicts) {
37
+ if (!context.graph.hasNode(conflict.conflictId)) {
38
+ const node = {
39
+ id: conflict.conflictId,
40
+ type: 'conflict',
41
+ label: `Conflict: ${conflict.type}`,
42
+ sourceStage: 'merge',
43
+ metadata: {
44
+ conflictType: conflict.type,
45
+ stages: conflict.stages,
46
+ detail: conflict.detail,
47
+ suggestedAction: conflict.suggestedAction,
48
+ severity: conflict.severity,
49
+ },
50
+ };
51
+ context.graph.addNode(node);
52
+ // Link to affected node
53
+ if (context.graph.hasNode(conflict.nodeId)) {
54
+ context.graph.addEdge({
55
+ id: `${conflict.conflictId}->conflicts-with->${conflict.nodeId}`,
56
+ type: 'conflicts-with',
57
+ sourceNodeId: conflict.conflictId,
58
+ targetNodeId: conflict.nodeId,
59
+ sourceStage: 'merge',
60
+ metadata: {},
61
+ });
62
+ }
63
+ }
64
+ }
65
+ // 2. Build coverage mappings
66
+ const allMappings = (0, coverageMappingBuilder_1.buildCoverageMappings)(context.graph, tiaOutput, iastOutput, dastOutput, isStaticOnlyMode);
67
+ // 3. Organize into sections
68
+ const sections = organizeSections(allMappings, context.graph);
69
+ // 4. Compute summary
70
+ const summary = (0, summaryComputer_1.computeSummary)(allMappings, tiaOutput);
71
+ // 5. Serialize graph
72
+ const graphSerialized = context.graph.toSerializable();
73
+ // 6. Collect diagnostics
74
+ const diagnosticsRecord = {};
75
+ for (const [stageName, diag] of context.diagnostics) {
76
+ diagnosticsRecord[stageName] = diag;
77
+ }
78
+ // Add merge diagnostics
79
+ const mergeDiagnostics = {
80
+ stageName: 'merge',
81
+ filesScanned: [],
82
+ filesSkipped: [],
83
+ durationMs: Date.now() - startTime,
84
+ metadata: {
85
+ totalMappings: allMappings.length,
86
+ conflictsDetected: conflicts.length,
87
+ isStaticOnlyMode,
88
+ graphNodeCount: graphSerialized.nodes.length,
89
+ graphEdgeCount: graphSerialized.edges.length,
90
+ },
91
+ };
92
+ context.diagnostics.set('merge', mergeDiagnostics);
93
+ diagnosticsRecord['merge'] = mergeDiagnostics;
94
+ // 7. Build final output
95
+ const output = {
96
+ graph: graphSerialized,
97
+ sections,
98
+ diagnostics: diagnosticsRecord,
99
+ summary,
100
+ };
101
+ context.stageOutputs.set('merge', output);
102
+ return output;
103
+ }
104
+ }
105
+ exports.MergeStage = MergeStage;
106
+ /**
107
+ * Organize coverage mappings into the 6 output sections.
108
+ */
109
+ function organizeSections(mappings, graph) {
110
+ const endpoints = [];
111
+ const parameters = [];
112
+ const integrationFlows = [];
113
+ const security = [];
114
+ const errorHandling = [];
115
+ const performance = [];
116
+ for (const mapping of mappings) {
117
+ switch (mapping.itemType) {
118
+ case 'endpoint':
119
+ endpoints.push(mapping);
120
+ break;
121
+ case 'service':
122
+ integrationFlows.push(mapping);
123
+ break;
124
+ case 'error-branch':
125
+ errorHandling.push(mapping);
126
+ break;
127
+ case 'security-path':
128
+ security.push(mapping);
129
+ break;
130
+ case 'validation-rule':
131
+ parameters.push(mapping);
132
+ break;
133
+ default:
134
+ endpoints.push(mapping);
135
+ }
136
+ }
137
+ return {
138
+ endpoints,
139
+ parameters,
140
+ integrationFlows,
141
+ security,
142
+ errorHandling,
143
+ performance,
144
+ };
145
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Summary computer — computes the `PipelineSummary` from the final
3
+ * set of coverage mappings.
4
+ */
5
+ import type { PipelineSummary, CoverageMapping } from '../../types';
6
+ import type { TiaOutput } from '../tia/types';
7
+ /**
8
+ * Compute the pipeline summary from coverage mappings.
9
+ */
10
+ export declare function computeSummary(mappings: CoverageMapping[], tiaOutput: TiaOutput | undefined): PipelineSummary;
11
+ //# sourceMappingURL=summaryComputer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"summaryComputer.d.ts","sourceRoot":"","sources":["../../../../../src/pipeline/stages/merge/summaryComputer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,eAAe,EAAa,MAAM,aAAa,CAAC;AAC/E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C;;GAEG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,eAAe,EAAE,EAC3B,SAAS,EAAE,SAAS,GAAG,SAAS,GAC/B,eAAe,CAwCjB"}
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ /**
3
+ * Summary computer — computes the `PipelineSummary` from the final
4
+ * set of coverage mappings.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.computeSummary = computeSummary;
8
+ /**
9
+ * Compute the pipeline summary from coverage mappings.
10
+ */
11
+ function computeSummary(mappings, tiaOutput) {
12
+ var _a;
13
+ const endpointMappings = mappings.filter((m) => m.itemType === 'endpoint');
14
+ const totalEndpoints = endpointMappings.length;
15
+ const coveredEndpoints = endpointMappings.filter((m) => m.coverageClass !== 'uncovered').length;
16
+ const verifiedEndpoints = endpointMappings.filter((m) => m.confidence === 'verified').length;
17
+ const uncoveredEndpoints = endpointMappings.filter((m) => m.coverageClass === 'uncovered').length;
18
+ const mockLimitedPaths = endpointMappings.filter((m) => m.coverageClass === 'mock-covered').length;
19
+ const unresolvedAbstractions = endpointMappings.filter((m) => m.assertionSource === 'unresolved' && m.linkedTests.length > 0).length;
20
+ const conflictCount = endpointMappings.reduce((sum, m) => sum + m.conflicts.length, 0);
21
+ // Coverage by layer from TIA classifications
22
+ const coverageByLayer = {
23
+ unit: 0,
24
+ component: 0,
25
+ integration: 0,
26
+ api: 0,
27
+ e2e: 0,
28
+ performance: 0,
29
+ security: 0,
30
+ };
31
+ if (tiaOutput) {
32
+ for (const classification of tiaOutput.classifications) {
33
+ coverageByLayer[classification.layer] = ((_a = coverageByLayer[classification.layer]) !== null && _a !== void 0 ? _a : 0) + 1;
34
+ }
35
+ }
36
+ return {
37
+ totalEndpoints,
38
+ coveredEndpoints,
39
+ verifiedEndpoints,
40
+ uncoveredEndpoints,
41
+ mockLimitedPaths,
42
+ unresolvedAbstractions,
43
+ conflictCount,
44
+ coverageByLayer,
45
+ };
46
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * CI platform detection.
3
+ *
4
+ * Detects the CI platform from project structure by checking
5
+ * for well-known CI configuration files.
6
+ */
7
+ import type { CiPlatform } from './types';
8
+ /**
9
+ * Detect the CI platform from project structure.
10
+ *
11
+ * Returns the first matching platform, or 'none' if no CI configuration is found.
12
+ * Checks directories with `fs.existsSync` which handles both files and directories.
13
+ */
14
+ export declare function detectCiPlatform(projectRoot: string): CiPlatform;
15
+ //# sourceMappingURL=ciDetector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ciDetector.d.ts","sourceRoot":"","sources":["../../../../../src/pipeline/stages/sca/ciDetector.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAmC1C;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,UAAU,CAUhE"}