api-tests-coverage 1.0.13 → 1.0.15

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,416 @@
1
+ "use strict";
2
+ /**
3
+ * Dependency detector — parses manifest files to extract dependency names and versions.
4
+ *
5
+ * Supports:
6
+ * - package.json (JavaScript/TypeScript)
7
+ * - pom.xml (Java/Kotlin Maven)
8
+ * - build.gradle / build.gradle.kts (Java/Kotlin Gradle)
9
+ * - requirements.txt (Python)
10
+ * - pyproject.toml (Python)
11
+ * - Pipfile (Python)
12
+ * - Gemfile (Ruby)
13
+ * - go.mod (Go)
14
+ */
15
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
16
+ if (k2 === undefined) k2 = k;
17
+ var desc = Object.getOwnPropertyDescriptor(m, k);
18
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
19
+ desc = { enumerable: true, get: function() { return m[k]; } };
20
+ }
21
+ Object.defineProperty(o, k2, desc);
22
+ }) : (function(o, m, k, k2) {
23
+ if (k2 === undefined) k2 = k;
24
+ o[k2] = m[k];
25
+ }));
26
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
27
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
28
+ }) : function(o, v) {
29
+ o["default"] = v;
30
+ });
31
+ var __importStar = (this && this.__importStar) || (function () {
32
+ var ownKeys = function(o) {
33
+ ownKeys = Object.getOwnPropertyNames || function (o) {
34
+ var ar = [];
35
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
36
+ return ar;
37
+ };
38
+ return ownKeys(o);
39
+ };
40
+ return function (mod) {
41
+ if (mod && mod.__esModule) return mod;
42
+ var result = {};
43
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
44
+ __setModuleDefault(result, mod);
45
+ return result;
46
+ };
47
+ })();
48
+ Object.defineProperty(exports, "__esModule", { value: true });
49
+ exports.MANIFEST_FILES = void 0;
50
+ exports.detectDependencies = detectDependencies;
51
+ exports.parseManifest = parseManifest;
52
+ const fs = __importStar(require("fs"));
53
+ const path = __importStar(require("path"));
54
+ /** Manifest file names this detector handles. */
55
+ exports.MANIFEST_FILES = [
56
+ 'package.json',
57
+ 'pom.xml',
58
+ 'build.gradle',
59
+ 'build.gradle.kts',
60
+ 'requirements.txt',
61
+ 'pyproject.toml',
62
+ 'Pipfile',
63
+ 'Gemfile',
64
+ 'go.mod',
65
+ ];
66
+ /**
67
+ * Scan a project root for manifest files and parse all dependencies.
68
+ */
69
+ function detectDependencies(projectRoot) {
70
+ const dependencies = [];
71
+ const manifestFiles = [];
72
+ for (const manifest of exports.MANIFEST_FILES) {
73
+ const fullPath = path.join(projectRoot, manifest);
74
+ if (!fs.existsSync(fullPath))
75
+ continue;
76
+ manifestFiles.push(manifest);
77
+ let content;
78
+ try {
79
+ content = fs.readFileSync(fullPath, 'utf-8');
80
+ }
81
+ catch {
82
+ continue;
83
+ }
84
+ const parsed = parseManifest(manifest, content, manifest);
85
+ dependencies.push(...parsed);
86
+ }
87
+ return { dependencies, manifestFiles };
88
+ }
89
+ /**
90
+ * Parse a single manifest file and return dependency entries.
91
+ */
92
+ function parseManifest(fileName, content, sourceFile) {
93
+ const base = path.basename(fileName);
94
+ switch (base) {
95
+ case 'package.json':
96
+ return parsePackageJson(content, sourceFile);
97
+ case 'pom.xml':
98
+ return parsePomXml(content, sourceFile);
99
+ case 'build.gradle':
100
+ case 'build.gradle.kts':
101
+ return parseBuildGradle(content, sourceFile);
102
+ case 'requirements.txt':
103
+ return parseRequirementsTxt(content, sourceFile);
104
+ case 'pyproject.toml':
105
+ return parsePyprojectToml(content, sourceFile);
106
+ case 'Pipfile':
107
+ return parsePipfile(content, sourceFile);
108
+ case 'Gemfile':
109
+ return parseGemfile(content, sourceFile);
110
+ case 'go.mod':
111
+ return parseGoMod(content, sourceFile);
112
+ default:
113
+ return [];
114
+ }
115
+ }
116
+ // ─── package.json ───────────────────────────────────────────────────────────
117
+ function parsePackageJson(content, sourceFile) {
118
+ const deps = [];
119
+ let pkg;
120
+ try {
121
+ pkg = JSON.parse(content);
122
+ }
123
+ catch {
124
+ return deps;
125
+ }
126
+ const sections = [
127
+ { key: 'dependencies', scope: 'production' },
128
+ { key: 'devDependencies', scope: 'development' },
129
+ { key: 'peerDependencies', scope: 'production' },
130
+ { key: 'optionalDependencies', scope: 'production' },
131
+ ];
132
+ for (const { key, scope } of sections) {
133
+ const section = pkg[key];
134
+ if (!section || typeof section !== 'object')
135
+ continue;
136
+ for (const [name, version] of Object.entries(section)) {
137
+ if (typeof version === 'string') {
138
+ deps.push({ name, version, scope, sourceFile });
139
+ }
140
+ }
141
+ }
142
+ return deps;
143
+ }
144
+ // ─── pom.xml ────────────────────────────────────────────────────────────────
145
+ function parsePomXml(content, sourceFile) {
146
+ var _a;
147
+ const deps = [];
148
+ // Extract dependencies using regex (avoids XML parser dependency)
149
+ const depBlockRegex = /<dependency>([\s\S]*?)<\/dependency>/g;
150
+ let match;
151
+ while ((match = depBlockRegex.exec(content)) !== null) {
152
+ const block = match[1];
153
+ const groupId = extractXmlTag(block, 'groupId');
154
+ const artifactId = extractXmlTag(block, 'artifactId');
155
+ const version = (_a = extractXmlTag(block, 'version')) !== null && _a !== void 0 ? _a : 'managed';
156
+ const scopeTag = extractXmlTag(block, 'scope');
157
+ if (!artifactId)
158
+ continue;
159
+ const name = groupId ? `${groupId}:${artifactId}` : artifactId;
160
+ const scope = mapMavenScope(scopeTag);
161
+ deps.push({ name, version, scope, sourceFile });
162
+ }
163
+ return deps;
164
+ }
165
+ function extractXmlTag(xml, tag) {
166
+ const regex = new RegExp(`<${tag}>\\s*([^<]+?)\\s*</${tag}>`);
167
+ const match = regex.exec(xml);
168
+ return match === null || match === void 0 ? void 0 : match[1];
169
+ }
170
+ function mapMavenScope(scope) {
171
+ switch (scope) {
172
+ case 'test':
173
+ return 'test';
174
+ case 'provided':
175
+ case 'compile':
176
+ case 'runtime':
177
+ return 'production';
178
+ case 'system':
179
+ case 'import':
180
+ return 'build';
181
+ default:
182
+ return 'production'; // Maven default is compile
183
+ }
184
+ }
185
+ // ─── build.gradle / build.gradle.kts ────────────────────────────────────────
186
+ function parseBuildGradle(content, sourceFile) {
187
+ var _a, _b;
188
+ const deps = [];
189
+ // Match patterns like:
190
+ // implementation 'group:artifact:version'
191
+ // implementation "group:artifact:version"
192
+ // testImplementation("group:artifact:version")
193
+ // api "group:artifact:version"
194
+ const depRegex = /(?:implementation|testImplementation|testRuntimeOnly|runtimeOnly|compileOnly|api|annotationProcessor|kapt)\s*[\(]?\s*['"]([\w./-]+):([\w./-]+)(?::([\w.+\-]+))?['"]\s*[\)]?/g;
195
+ let match;
196
+ while ((match = depRegex.exec(content)) !== null) {
197
+ const group = match[1];
198
+ const artifact = match[2];
199
+ const version = (_a = match[3]) !== null && _a !== void 0 ? _a : 'unspecified';
200
+ const line = content.substring(0, match.index).split('\n').length - 1;
201
+ const configLine = (_b = content.split('\n')[line]) !== null && _b !== void 0 ? _b : '';
202
+ const name = `${group}:${artifact}`;
203
+ const scope = mapGradleScope(configLine);
204
+ deps.push({ name, version, scope, sourceFile });
205
+ }
206
+ return deps;
207
+ }
208
+ function mapGradleScope(line) {
209
+ const trimmed = line.trim();
210
+ if (trimmed.startsWith('testImplementation') || trimmed.startsWith('testRuntimeOnly')) {
211
+ return 'test';
212
+ }
213
+ if (trimmed.startsWith('annotationProcessor') || trimmed.startsWith('kapt')) {
214
+ return 'build';
215
+ }
216
+ return 'production';
217
+ }
218
+ // ─── requirements.txt ───────────────────────────────────────────────────────
219
+ function parseRequirementsTxt(content, sourceFile) {
220
+ var _a;
221
+ const deps = [];
222
+ for (const rawLine of content.split('\n')) {
223
+ const line = rawLine.trim();
224
+ // Skip comments, empty lines, -r/-e/--flags
225
+ if (!line || line.startsWith('#') || line.startsWith('-'))
226
+ continue;
227
+ // Handle extras: package[extra1,extra2]
228
+ // Match: package==version, package>=version, package~=version, package (no version)
229
+ const match = /^([a-zA-Z0-9_][a-zA-Z0-9_.+-]*)(?:\[[^\]]*\])?\s*(?:([=><~!]+)\s*(.+))?$/.exec(line);
230
+ if (!match)
231
+ continue;
232
+ const name = match[1];
233
+ const version = (_a = match[3]) !== null && _a !== void 0 ? _a : 'unspecified';
234
+ deps.push({ name, version, scope: 'unknown', sourceFile });
235
+ }
236
+ return deps;
237
+ }
238
+ // ─── pyproject.toml ─────────────────────────────────────────────────────────
239
+ function parsePyprojectToml(content, sourceFile) {
240
+ const deps = [];
241
+ // Extract [project.dependencies] section
242
+ const projectDepsMatch = /\[project\]\s[\s\S]*?dependencies\s*=\s*\[([\s\S]*?)\]/m.exec(content);
243
+ if (projectDepsMatch) {
244
+ parsePyprojectDependencyArray(projectDepsMatch[1], 'production', sourceFile, deps);
245
+ }
246
+ // Extract [project.optional-dependencies] sections
247
+ const optionalMatch = /\[project\.optional-dependencies\]\s*([\s\S]*?)(?=\n\[|\n$|$)/m.exec(content);
248
+ if (optionalMatch) {
249
+ const block = optionalMatch[1];
250
+ const sectionRegex = /(\w+)\s*=\s*\[([\s\S]*?)\]/g;
251
+ let sMatch;
252
+ while ((sMatch = sectionRegex.exec(block)) !== null) {
253
+ const sectionName = sMatch[1].toLowerCase();
254
+ const scope = sectionName === 'dev' || sectionName === 'test' || sectionName === 'testing'
255
+ ? 'development'
256
+ : 'production';
257
+ parsePyprojectDependencyArray(sMatch[2], scope, sourceFile, deps);
258
+ }
259
+ }
260
+ // Also try [tool.poetry.dependencies] for Poetry projects
261
+ const poetryDepsMatch = /\[tool\.poetry\.dependencies\]\s*([\s\S]*?)(?=\n\[|\n$|$)/m.exec(content);
262
+ if (poetryDepsMatch) {
263
+ parseTomlDependencyBlock(poetryDepsMatch[1], 'production', sourceFile, deps);
264
+ }
265
+ const poetryDevMatch = /\[tool\.poetry\.(?:dev-dependencies|group\.dev\.dependencies)\]\s*([\s\S]*?)(?=\n\[|\n$|$)/m.exec(content);
266
+ if (poetryDevMatch) {
267
+ parseTomlDependencyBlock(poetryDevMatch[1], 'development', sourceFile, deps);
268
+ }
269
+ return deps;
270
+ }
271
+ function parsePyprojectDependencyArray(block, scope, sourceFile, deps) {
272
+ var _a;
273
+ // Lines look like: "requests>=2.20", "fastapi~=0.100"
274
+ const lineRegex = /["']([a-zA-Z0-9_][a-zA-Z0-9_.+-]*)(?:\[[^\]]*\])?\s*(?:([=><~!]+)\s*([^"']+))?["']/g;
275
+ let match;
276
+ while ((match = lineRegex.exec(block)) !== null) {
277
+ deps.push({
278
+ name: match[1],
279
+ version: (_a = match[3]) !== null && _a !== void 0 ? _a : 'unspecified',
280
+ scope,
281
+ sourceFile,
282
+ });
283
+ }
284
+ }
285
+ function parseTomlDependencyBlock(block, scope, sourceFile, deps) {
286
+ // Lines look like: flask = "^2.0" or requests = {version = ">=2.20", optional = true}
287
+ for (const line of block.split('\n')) {
288
+ const trimmed = line.trim();
289
+ if (!trimmed || trimmed.startsWith('#') || trimmed.startsWith('['))
290
+ continue;
291
+ const kvMatch = /^([a-zA-Z0-9_][a-zA-Z0-9_.-]*)\s*=\s*(.+)$/.exec(trimmed);
292
+ if (!kvMatch)
293
+ continue;
294
+ const name = kvMatch[1];
295
+ if (name.toLowerCase() === 'python')
296
+ continue; // Skip python version spec
297
+ const valueStr = kvMatch[2].trim();
298
+ let version = 'unspecified';
299
+ if (valueStr.startsWith('"') || valueStr.startsWith("'")) {
300
+ // Simple version string
301
+ version = valueStr.replace(/["']/g, '');
302
+ }
303
+ else if (valueStr.startsWith('{')) {
304
+ // Table form: {version = ">=2.0", ...}
305
+ const versionMatch = /version\s*=\s*["']([^"']+)["']/.exec(valueStr);
306
+ if (versionMatch)
307
+ version = versionMatch[1];
308
+ }
309
+ deps.push({ name, version, scope, sourceFile });
310
+ }
311
+ }
312
+ // ─── Pipfile ─────────────────────────────────────────────────────────────────
313
+ function parsePipfile(content, sourceFile) {
314
+ const deps = [];
315
+ // Parse [packages] and [dev-packages] sections
316
+ const sections = [
317
+ { regex: /\[packages\]\s*([\s\S]*?)(?=\n\[|\n$|$)/m, scope: 'production' },
318
+ { regex: /\[dev-packages\]\s*([\s\S]*?)(?=\n\[|\n$|$)/m, scope: 'development' },
319
+ ];
320
+ for (const { regex, scope } of sections) {
321
+ const match = regex.exec(content);
322
+ if (!match)
323
+ continue;
324
+ for (const line of match[1].split('\n')) {
325
+ const trimmed = line.trim();
326
+ if (!trimmed || trimmed.startsWith('#') || trimmed.startsWith('['))
327
+ continue;
328
+ const kvMatch = /^([a-zA-Z0-9_][a-zA-Z0-9_.-]*)\s*=\s*(.+)$/.exec(trimmed);
329
+ if (!kvMatch)
330
+ continue;
331
+ const name = kvMatch[1];
332
+ const valueStr = kvMatch[2].trim();
333
+ let version = '*';
334
+ if (valueStr.startsWith('"') || valueStr.startsWith("'")) {
335
+ version = valueStr.replace(/["']/g, '');
336
+ }
337
+ else if (valueStr.startsWith('{')) {
338
+ const versionMatch = /version\s*=\s*["']([^"']+)["']/.exec(valueStr);
339
+ if (versionMatch)
340
+ version = versionMatch[1];
341
+ }
342
+ deps.push({ name, version, scope, sourceFile });
343
+ }
344
+ }
345
+ return deps;
346
+ }
347
+ // ─── Gemfile ────────────────────────────────────────────────────────────────
348
+ function parseGemfile(content, sourceFile) {
349
+ var _a;
350
+ const deps = [];
351
+ let currentGroup = 'production';
352
+ for (const rawLine of content.split('\n')) {
353
+ const line = rawLine.trim();
354
+ if (!line || line.startsWith('#'))
355
+ continue;
356
+ // Track group blocks: group :development do ... end
357
+ const groupMatch = /^group\s+:(\w+)/.exec(line);
358
+ if (groupMatch) {
359
+ const group = groupMatch[1];
360
+ currentGroup =
361
+ group === 'development' || group === 'test' ? 'development' : 'production';
362
+ continue;
363
+ }
364
+ if (line === 'end') {
365
+ currentGroup = 'production';
366
+ continue;
367
+ }
368
+ // Match: gem 'name', '~> version'
369
+ const gemMatch = /^gem\s+['"]([a-zA-Z0-9_.-]+)['"](?:\s*,\s*['"]([^'"]+)['"])?/.exec(line);
370
+ if (!gemMatch)
371
+ continue;
372
+ deps.push({
373
+ name: gemMatch[1],
374
+ version: (_a = gemMatch[2]) !== null && _a !== void 0 ? _a : '*',
375
+ scope: currentGroup,
376
+ sourceFile,
377
+ });
378
+ }
379
+ return deps;
380
+ }
381
+ // ─── go.mod ─────────────────────────────────────────────────────────────────
382
+ function parseGoMod(content, sourceFile) {
383
+ const deps = [];
384
+ // Match require block: require ( ... )
385
+ const requireBlockRegex = /require\s*\(([\s\S]*?)\)/g;
386
+ let blockMatch;
387
+ while ((blockMatch = requireBlockRegex.exec(content)) !== null) {
388
+ const block = blockMatch[1];
389
+ for (const line of block.split('\n')) {
390
+ const trimmed = line.trim();
391
+ if (!trimmed || trimmed.startsWith('//'))
392
+ continue;
393
+ const parts = trimmed.split(/\s+/);
394
+ if (parts.length >= 2) {
395
+ deps.push({
396
+ name: parts[0],
397
+ version: parts[1],
398
+ scope: 'production',
399
+ sourceFile,
400
+ });
401
+ }
402
+ }
403
+ }
404
+ // Match single-line require: require module/path v1.2.3
405
+ const singleRegex = /^require\s+(\S+)\s+(\S+)/gm;
406
+ let singleMatch;
407
+ while ((singleMatch = singleRegex.exec(content)) !== null) {
408
+ deps.push({
409
+ name: singleMatch[1],
410
+ version: singleMatch[2],
411
+ scope: 'production',
412
+ sourceFile,
413
+ });
414
+ }
415
+ return deps;
416
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * SCA (Software Composition Analysis) stage — Stage 1 of the coverage pipeline.
3
+ *
4
+ * Wraps the existing discovery engine and adds:
5
+ * - Manifest file parsing for dependency extraction
6
+ * - Dependency classification into categories
7
+ * - CI platform detection
8
+ * - Graph population with `dependency` nodes and `depends-on` edges
9
+ *
10
+ * This stage runs first so that downstream stages (AST, TIA) can use the
11
+ * ScaOutput to select correct analysis heuristics (e.g. which HTTP client
12
+ * patterns to detect, which assertion libraries to recognize).
13
+ */
14
+ import type { PipelineStage, PipelineContext } from '../../stageInterface';
15
+ import type { ScaOutput } from './types';
16
+ export declare class ScaStage implements PipelineStage<ScaOutput> {
17
+ readonly name: "sca";
18
+ readonly optional = false;
19
+ execute(context: PipelineContext): Promise<ScaOutput>;
20
+ }
21
+ //# sourceMappingURL=scaStage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scaStage.d.ts","sourceRoot":"","sources":["../../../../../src/pipeline/stages/sca/scaStage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAE3E,OAAO,KAAK,EAAE,SAAS,EAAoB,MAAM,SAAS,CAAC;AAM3D,qBAAa,QAAS,YAAW,aAAa,CAAC,SAAS,CAAC;IACvD,QAAQ,CAAC,IAAI,EAAG,KAAK,CAAU;IAC/B,QAAQ,CAAC,QAAQ,SAAS;IAEpB,OAAO,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,SAAS,CAAC;CAgL5D"}
@@ -0,0 +1,208 @@
1
+ "use strict";
2
+ /**
3
+ * SCA (Software Composition Analysis) stage — Stage 1 of the coverage pipeline.
4
+ *
5
+ * Wraps the existing discovery engine and adds:
6
+ * - Manifest file parsing for dependency extraction
7
+ * - Dependency classification into categories
8
+ * - CI platform detection
9
+ * - Graph population with `dependency` nodes and `depends-on` edges
10
+ *
11
+ * This stage runs first so that downstream stages (AST, TIA) can use the
12
+ * ScaOutput to select correct analysis heuristics (e.g. which HTTP client
13
+ * patterns to detect, which assertion libraries to recognize).
14
+ */
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.ScaStage = void 0;
17
+ const dependencyDetector_1 = require("./dependencyDetector");
18
+ const dependencyClassification_1 = require("./dependencyClassification");
19
+ const ciDetector_1 = require("./ciDetector");
20
+ const projectDiscovery_1 = require("../../../discovery/projectDiscovery");
21
+ class ScaStage {
22
+ constructor() {
23
+ this.name = 'sca';
24
+ this.optional = false;
25
+ }
26
+ async execute(context) {
27
+ const startTime = Date.now();
28
+ const filesScanned = [];
29
+ const filesSkipped = [];
30
+ // 1. Run the existing discovery engine
31
+ const artifacts = (0, projectDiscovery_1.discoverProject)({ rootDir: context.projectRoot });
32
+ // 2. Parse manifest files for dependencies
33
+ const depResult = (0, dependencyDetector_1.detectDependencies)(context.projectRoot);
34
+ filesScanned.push(...depResult.manifestFiles);
35
+ // 3. Classify dependencies by category
36
+ const httpClients = [];
37
+ const testFrameworks = [];
38
+ const assertionLibraries = [];
39
+ const mockingLibraries = [];
40
+ const securityLibraries = [];
41
+ const performanceTools = [];
42
+ const e2eFrameworks = [];
43
+ const frameworksDeps = [];
44
+ const dependencyVersions = {};
45
+ for (const dep of depResult.dependencies) {
46
+ // Store version (use short name for display, e.g. artifactId for Maven)
47
+ const shortName = getShortDependencyName(dep);
48
+ dependencyVersions[shortName] = dep.version;
49
+ // Classify
50
+ const category = (0, dependencyClassification_1.classifyDependency)(shortName);
51
+ switch (category) {
52
+ case 'httpClient':
53
+ addUnique(httpClients, shortName);
54
+ break;
55
+ case 'testFramework':
56
+ addUnique(testFrameworks, shortName);
57
+ break;
58
+ case 'assertionLibrary':
59
+ addUnique(assertionLibraries, shortName);
60
+ break;
61
+ case 'mockingLibrary':
62
+ addUnique(mockingLibraries, shortName);
63
+ break;
64
+ case 'securityLibrary':
65
+ addUnique(securityLibraries, shortName);
66
+ break;
67
+ case 'performanceTool':
68
+ addUnique(performanceTools, shortName);
69
+ break;
70
+ case 'e2eFramework':
71
+ addUnique(e2eFrameworks, shortName);
72
+ break;
73
+ case 'framework':
74
+ addUnique(frameworksDeps, shortName);
75
+ break;
76
+ }
77
+ }
78
+ // 4. Merge discovery-detected languages and frameworks with dependency-detected ones
79
+ // DetectedLanguage and DetectedFramework are string literal unions, not objects
80
+ const languages = artifacts.languages;
81
+ const frameworks = mergeStrings(artifacts.frameworks, frameworksDeps);
82
+ // Also enrich test frameworks from discovery's framework detection
83
+ for (const f of artifacts.frameworks) {
84
+ const name = f.toLowerCase();
85
+ if (['jest', 'mocha', 'pytest', 'unittest', 'junit', 'testng', 'rspec', 'minitest'].includes(name)) {
86
+ addUnique(testFrameworks, name);
87
+ }
88
+ if (['cypress', 'playwright', 'cucumber', 'selenium'].includes(name)) {
89
+ addUnique(e2eFrameworks, name);
90
+ }
91
+ }
92
+ // 5. Detect CI platform
93
+ const ciPlatform = (0, ciDetector_1.detectCiPlatform)(context.projectRoot);
94
+ // 6. Populate the graph with dependency nodes
95
+ for (const dep of depResult.dependencies) {
96
+ const shortName = getShortDependencyName(dep);
97
+ const nodeId = `dep:${shortName}`;
98
+ // Skip if already added (avoid duplicates from different manifest files)
99
+ if (context.graph.hasNode(nodeId))
100
+ continue;
101
+ const node = {
102
+ id: nodeId,
103
+ type: 'dependency',
104
+ label: shortName,
105
+ sourceStage: 'sca',
106
+ metadata: {
107
+ version: dep.version,
108
+ scope: dep.scope,
109
+ category: (0, dependencyClassification_1.classifyDependency)(shortName),
110
+ sourceFile: dep.sourceFile,
111
+ fullName: dep.name,
112
+ },
113
+ };
114
+ context.graph.addNode(node);
115
+ }
116
+ // Add file-level nodes for each manifest
117
+ for (const manifest of depResult.manifestFiles) {
118
+ const fileNodeId = `file:${manifest}`;
119
+ if (!context.graph.hasNode(fileNodeId)) {
120
+ const fileNode = {
121
+ id: fileNodeId,
122
+ type: 'file',
123
+ label: manifest,
124
+ sourceStage: 'sca',
125
+ filePath: manifest,
126
+ metadata: { fileType: 'manifest' },
127
+ };
128
+ context.graph.addNode(fileNode);
129
+ }
130
+ // Add depends-on edges from manifest to each dependency declared in it
131
+ for (const dep of depResult.dependencies) {
132
+ if (dep.sourceFile !== manifest)
133
+ continue;
134
+ const shortName = getShortDependencyName(dep);
135
+ const depNodeId = `dep:${shortName}`;
136
+ const edgeId = `${fileNodeId}->depends-on->${depNodeId}`;
137
+ if (!context.graph.hasEdge(edgeId)) {
138
+ const edge = {
139
+ id: edgeId,
140
+ type: 'depends-on',
141
+ sourceNodeId: fileNodeId,
142
+ targetNodeId: depNodeId,
143
+ sourceStage: 'sca',
144
+ metadata: { scope: dep.scope },
145
+ };
146
+ context.graph.addEdge(edge);
147
+ }
148
+ }
149
+ }
150
+ // 7. Record diagnostics
151
+ const diagnostics = {
152
+ stageName: 'sca',
153
+ filesScanned,
154
+ filesSkipped,
155
+ durationMs: Date.now() - startTime,
156
+ metadata: {
157
+ totalDependencies: depResult.dependencies.length,
158
+ manifestFiles: depResult.manifestFiles,
159
+ ciPlatform,
160
+ discoveredLanguages: languages,
161
+ discoveredFrameworks: frameworks,
162
+ },
163
+ };
164
+ context.diagnostics.set('sca', diagnostics);
165
+ // 8. Build and return output
166
+ const output = {
167
+ languages,
168
+ frameworks,
169
+ httpClients,
170
+ testFrameworks,
171
+ assertionLibraries,
172
+ securityLibraries,
173
+ performanceTools,
174
+ mockingLibraries,
175
+ e2eFrameworks,
176
+ dependencyVersions,
177
+ ciPlatform,
178
+ };
179
+ context.stageOutputs.set('sca', output);
180
+ return output;
181
+ }
182
+ }
183
+ exports.ScaStage = ScaStage;
184
+ /**
185
+ * Extract a short dependency name for classification and display.
186
+ * For Maven-style names (group:artifact), returns just the artifact.
187
+ * For npm scoped packages (@scope/name), returns the full scoped name.
188
+ */
189
+ function getShortDependencyName(dep) {
190
+ const name = dep.name;
191
+ // Maven: "org.mockito:mockito-core" → "mockito-core"
192
+ if (name.includes(':')) {
193
+ const parts = name.split(':');
194
+ return parts[parts.length - 1];
195
+ }
196
+ return name;
197
+ }
198
+ function addUnique(arr, item) {
199
+ if (!arr.includes(item)) {
200
+ arr.push(item);
201
+ }
202
+ }
203
+ function mergeStrings(a, b) {
204
+ const set = new Set(a);
205
+ for (const item of b)
206
+ set.add(item);
207
+ return Array.from(set);
208
+ }