api-tests-coverage 1.0.23 → 1.0.24
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.
- package/README.md +39 -0
- package/config.yaml.example +35 -0
- package/dist/dashboard/dist/assets/_basePickBy-D4Hl8chy.js +1 -0
- package/dist/dashboard/dist/assets/_baseUniq-BSUUnV_V.js +1 -0
- package/dist/dashboard/dist/assets/arc-DhDluTY5.js +1 -0
- package/dist/dashboard/dist/assets/architectureDiagram-VXUJARFQ-DGlUU7dC.js +36 -0
- package/dist/dashboard/dist/assets/blockDiagram-VD42YOAC-CgXi3kEZ.js +122 -0
- package/dist/dashboard/dist/assets/c4Diagram-YG6GDRKO-Cr3xB15y.js +10 -0
- package/dist/dashboard/dist/assets/channel-DYAie-7m.js +1 -0
- package/dist/dashboard/dist/assets/chunk-4BX2VUAB-BaW3__pI.js +1 -0
- package/dist/dashboard/dist/assets/chunk-55IACEB6-DyYevfEQ.js +1 -0
- package/dist/dashboard/dist/assets/chunk-B4BG7PRW-C2bwZFec.js +165 -0
- package/dist/dashboard/dist/assets/chunk-DI55MBZ5-DO0T2xne.js +220 -0
- package/dist/dashboard/dist/assets/chunk-FMBD7UC4-CCYA4j_f.js +15 -0
- package/dist/dashboard/dist/assets/chunk-QN33PNHL-Cdhqs7xo.js +1 -0
- package/dist/dashboard/dist/assets/chunk-QZHKN3VN-BzHw38Ki.js +1 -0
- package/dist/dashboard/dist/assets/chunk-TZMSLE5B-dkJ0rsgF.js +1 -0
- package/dist/dashboard/dist/assets/classDiagram-2ON5EDUG-DiIv5Pho.js +1 -0
- package/dist/dashboard/dist/assets/classDiagram-v2-WZHVMYZB-DiIv5Pho.js +1 -0
- package/dist/dashboard/dist/assets/clone-B4LorrSy.js +1 -0
- package/dist/dashboard/dist/assets/cose-bilkent-S5V4N54A-jzGbyPIS.js +1 -0
- package/dist/dashboard/dist/assets/dagre-6UL2VRFP-D7rgvBx1.js +4 -0
- package/dist/dashboard/dist/assets/diagram-PSM6KHXK-2rYklqon.js +24 -0
- package/dist/dashboard/dist/assets/diagram-QEK2KX5R-CGrvALqm.js +43 -0
- package/dist/dashboard/dist/assets/diagram-S2PKOQOG-DA3c-QP4.js +24 -0
- package/dist/dashboard/dist/assets/erDiagram-Q2GNP2WA-BsYH8cLH.js +60 -0
- package/dist/dashboard/dist/assets/flowDiagram-NV44I4VS-Da_JhBCy.js +162 -0
- package/dist/dashboard/dist/assets/ganttDiagram-JELNMOA3-D8FTswNn.js +267 -0
- package/dist/dashboard/dist/assets/gitGraphDiagram-V2S2FVAM-BFJR-ITH.js +65 -0
- package/dist/dashboard/dist/assets/graph-CIvnjOQQ.js +1 -0
- package/dist/dashboard/dist/assets/index-BWX0sSZn.css +1 -0
- package/dist/dashboard/dist/assets/index-CbAFWEor.js +777 -0
- package/dist/dashboard/dist/assets/infoDiagram-HS3SLOUP-OcK0Lxgi.js +2 -0
- package/dist/dashboard/dist/assets/journeyDiagram-XKPGCS4Q-DTJukVOY.js +139 -0
- package/dist/dashboard/dist/assets/kanban-definition-3W4ZIXB7-Di65fNuD.js +89 -0
- package/dist/dashboard/dist/assets/layout-DAt24RVX.js +1 -0
- package/dist/dashboard/dist/assets/mindmap-definition-VGOIOE7T-DxI8MXCF.js +68 -0
- package/dist/dashboard/dist/assets/pieDiagram-ADFJNKIX-BafKx3_Y.js +30 -0
- package/dist/dashboard/dist/assets/quadrantDiagram-AYHSOK5B-BcZsArkk.js +7 -0
- package/dist/dashboard/dist/assets/requirementDiagram-UZGBJVZJ-CqFAO2t6.js +64 -0
- package/dist/dashboard/dist/assets/sankeyDiagram-TZEHDZUN-CqSaCg-3.js +10 -0
- package/dist/dashboard/dist/assets/sequenceDiagram-WL72ISMW-6IXD1uqW.js +145 -0
- package/dist/dashboard/dist/assets/stateDiagram-FKZM4ZOC-DvSVQAfp.js +1 -0
- package/dist/dashboard/dist/assets/stateDiagram-v2-4FDKWEC3-BMFdt0QQ.js +1 -0
- package/dist/dashboard/dist/assets/timeline-definition-IT6M3QCI-Cll7Nvth.js +61 -0
- package/dist/dashboard/dist/assets/treemap-GDKQZRPO-DtqX8zNC.js +162 -0
- package/dist/dashboard/dist/assets/xychartDiagram-PRI3JC2R-zxwS9i0A.js +7 -0
- package/dist/dashboard/dist/index.html +2 -2
- package/dist/dashboard/dist/reports/coverage-summary.json +75 -1
- package/dist/dashboard/dist/reports/security-full.json +157 -0
- package/dist/src/compatibilityCoverage.d.ts +34 -15
- package/dist/src/compatibilityCoverage.d.ts.map +1 -1
- package/dist/src/compatibilityCoverage.js +387 -85
- package/dist/src/config/defaultConfig.d.ts.map +1 -1
- package/dist/src/config/defaultConfig.js +62 -0
- package/dist/src/config/schema.d.ts.map +1 -1
- package/dist/src/config/schema.js +1 -1
- package/dist/src/config/types.d.ts +81 -1
- package/dist/src/config/types.d.ts.map +1 -1
- package/dist/src/config/validateConfig.d.ts.map +1 -1
- package/dist/src/config/validateConfig.js +126 -0
- package/dist/src/contracts/compatibilityMatrix.d.ts +20 -0
- package/dist/src/contracts/compatibilityMatrix.d.ts.map +1 -0
- package/dist/src/contracts/compatibilityMatrix.js +198 -0
- package/dist/src/contracts/pactBrokerClient.d.ts +10 -0
- package/dist/src/contracts/pactBrokerClient.d.ts.map +1 -0
- package/dist/src/contracts/pactBrokerClient.js +117 -0
- package/dist/src/contracts/schemaEvolutionChecker.d.ts +17 -0
- package/dist/src/contracts/schemaEvolutionChecker.d.ts.map +1 -0
- package/dist/src/contracts/schemaEvolutionChecker.js +95 -0
- package/dist/src/contracts/springCloudContractParser.d.ts +10 -0
- package/dist/src/contracts/springCloudContractParser.d.ts.map +1 -0
- package/dist/src/contracts/springCloudContractParser.js +144 -0
- package/dist/src/discovery/fileClassifier.d.ts.map +1 -1
- package/dist/src/discovery/fileClassifier.js +25 -0
- package/dist/src/discovery/projectDiscovery.d.ts +2 -0
- package/dist/src/discovery/projectDiscovery.d.ts.map +1 -1
- package/dist/src/discovery/projectDiscovery.js +25 -25
- package/dist/src/index.js +233 -16
- package/dist/src/inference/routeInference.d.ts +10 -2
- package/dist/src/inference/routeInference.d.ts.map +1 -1
- package/dist/src/inference/routeInference.js +363 -62
- package/dist/src/languageDetection.d.ts.map +1 -1
- package/dist/src/languageDetection.js +21 -4
- package/dist/src/lib/index.d.ts +3 -0
- package/dist/src/lib/index.d.ts.map +1 -1
- package/dist/src/lib/index.js +3 -1
- package/dist/src/pipeline/stages/tia/parameterizedTestExpander.js +152 -79
- package/dist/src/pipeline/stages/tia/testEndpointMapper.d.ts +5 -1
- package/dist/src/pipeline/stages/tia/testEndpointMapper.d.ts.map +1 -1
- package/dist/src/pipeline/stages/tia/testEndpointMapper.js +356 -42
- package/dist/src/pipeline/stages/tia/testLayerClassifier.d.ts.map +1 -1
- package/dist/src/pipeline/stages/tia/testLayerClassifier.js +20 -5
- package/dist/src/pipeline/stages/tia/tiaStage.d.ts.map +1 -1
- package/dist/src/pipeline/stages/tia/tiaStage.js +3 -1
- package/dist/src/pipeline/stages/tia/types.d.ts +11 -2
- package/dist/src/pipeline/stages/tia/types.d.ts.map +1 -1
- package/dist/src/projectDefaults.d.ts +6 -0
- package/dist/src/projectDefaults.d.ts.map +1 -0
- package/dist/src/projectDefaults.js +43 -0
- package/dist/src/security/hub.d.ts +81 -0
- package/dist/src/security/hub.d.ts.map +1 -0
- package/dist/src/security/hub.js +420 -0
- package/dist/src/security/index.d.ts +1 -0
- package/dist/src/security/index.d.ts.map +1 -1
- package/dist/src/security/index.js +8 -2
- package/dist/src/security/normalizers/gitleaks.d.ts +7 -0
- package/dist/src/security/normalizers/gitleaks.d.ts.map +1 -0
- package/dist/src/security/normalizers/gitleaks.js +32 -0
- package/dist/src/security/scanners/gitleaks.d.ts +3 -0
- package/dist/src/security/scanners/gitleaks.d.ts.map +1 -0
- package/dist/src/security/scanners/gitleaks.js +105 -0
- package/dist/src/security/scanners/semgrep.d.ts.map +1 -1
- package/dist/src/security/scanners/semgrep.js +24 -2
- package/dist/src/security/scanners/trivy.d.ts.map +1 -1
- package/dist/src/security/scanners/trivy.js +24 -2
- package/dist/src/security/scanners/zap.d.ts.map +1 -1
- package/dist/src/security/scanners/zap.js +27 -2
- package/dist/src/security/types.d.ts +15 -1
- package/dist/src/security/types.d.ts.map +1 -1
- package/dist/src/streaming/schema/index.d.ts +23 -0
- package/dist/src/streaming/schema/index.d.ts.map +1 -0
- package/dist/src/streaming/schema/index.js +196 -0
- package/dist/src/summary/markdownRenderer.d.ts.map +1 -1
- package/dist/src/summary/markdownRenderer.js +15 -1
- package/dist/src/summary/summaryTypes.d.ts.map +1 -1
- package/dist/src/summary/summaryTypes.js +1 -0
- package/dist/src/unitAnalysis.d.ts +145 -0
- package/dist/src/unitAnalysis.d.ts.map +1 -0
- package/dist/src/unitAnalysis.js +1392 -0
- package/package.json +1 -1
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.fetchContractsFromBroker = fetchContractsFromBroker;
|
|
4
|
+
exports.publishVerificationResults = publishVerificationResults;
|
|
5
|
+
function buildHeaders(config) {
|
|
6
|
+
return {
|
|
7
|
+
Accept: 'application/json',
|
|
8
|
+
'Content-Type': 'application/json',
|
|
9
|
+
...(config.token ? { Authorization: `Bearer ${config.token}`, 'Pact-Token': config.token } : {}),
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
function parsePactPayload(raw, filePath) {
|
|
13
|
+
var _a, _b;
|
|
14
|
+
if (!raw || typeof raw !== 'object')
|
|
15
|
+
return null;
|
|
16
|
+
const obj = raw;
|
|
17
|
+
if (!Array.isArray(obj['interactions']))
|
|
18
|
+
return null;
|
|
19
|
+
const interactions = obj['interactions']
|
|
20
|
+
.filter((entry) => !!entry && typeof entry === 'object')
|
|
21
|
+
.map((interaction) => {
|
|
22
|
+
var _a, _b;
|
|
23
|
+
const request = ((_a = interaction['request']) !== null && _a !== void 0 ? _a : {});
|
|
24
|
+
const response = ((_b = interaction['response']) !== null && _b !== void 0 ? _b : {});
|
|
25
|
+
return {
|
|
26
|
+
description: typeof interaction['description'] === 'string' ? interaction['description'] : '',
|
|
27
|
+
method: typeof request['method'] === 'string' ? request['method'].toLowerCase() : '',
|
|
28
|
+
path: typeof request['path'] === 'string' ? request['path'] : '',
|
|
29
|
+
requestBody: request['body'],
|
|
30
|
+
expectedStatus: typeof response['status'] === 'number' ? response['status'] : 200,
|
|
31
|
+
expectedBody: response['body'],
|
|
32
|
+
};
|
|
33
|
+
})
|
|
34
|
+
.filter((interaction) => interaction.method && interaction.path);
|
|
35
|
+
return {
|
|
36
|
+
consumer: typeof obj['consumer'] === 'object' && obj['consumer'] !== null
|
|
37
|
+
? String((_a = obj['consumer']['name']) !== null && _a !== void 0 ? _a : 'unknown')
|
|
38
|
+
: 'unknown',
|
|
39
|
+
provider: typeof obj['provider'] === 'object' && obj['provider'] !== null
|
|
40
|
+
? String((_b = obj['provider']['name']) !== null && _b !== void 0 ? _b : 'unknown')
|
|
41
|
+
: 'unknown',
|
|
42
|
+
interactions,
|
|
43
|
+
filePath,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function extractContracts(payload, sourceUrl) {
|
|
47
|
+
var _a;
|
|
48
|
+
if (!payload || typeof payload !== 'object')
|
|
49
|
+
return [];
|
|
50
|
+
const obj = payload;
|
|
51
|
+
if (Array.isArray(obj['interactions'])) {
|
|
52
|
+
const single = parsePactPayload(obj, sourceUrl);
|
|
53
|
+
return single ? [single] : [];
|
|
54
|
+
}
|
|
55
|
+
const embedded = ((_a = obj['_embedded']) !== null && _a !== void 0 ? _a : {});
|
|
56
|
+
const pacts = Array.isArray(embedded['pacts']) ? embedded['pacts'] : [];
|
|
57
|
+
return pacts
|
|
58
|
+
.map((entry, index) => parsePactPayload(entry, `${sourceUrl}#${index}`))
|
|
59
|
+
.filter((entry) => entry !== null);
|
|
60
|
+
}
|
|
61
|
+
async function fetchContractsFromBroker(config) {
|
|
62
|
+
if (!config.url || !config.providerName || typeof fetch !== 'function') {
|
|
63
|
+
return [];
|
|
64
|
+
}
|
|
65
|
+
const baseUrl = config.url.replace(/\/$/, '');
|
|
66
|
+
const candidateUrls = [
|
|
67
|
+
`${baseUrl}/pacts/provider/${encodeURIComponent(config.providerName)}/latest`,
|
|
68
|
+
`${baseUrl}/pacts/provider/${encodeURIComponent(config.providerName)}/latest?tag=main`,
|
|
69
|
+
];
|
|
70
|
+
for (const url of candidateUrls) {
|
|
71
|
+
try {
|
|
72
|
+
const response = await fetch(url, { headers: buildHeaders(config) });
|
|
73
|
+
if (!response.ok)
|
|
74
|
+
continue;
|
|
75
|
+
const payload = (await response.json());
|
|
76
|
+
const contracts = extractContracts(payload, url);
|
|
77
|
+
if (contracts.length > 0)
|
|
78
|
+
return contracts;
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
console.warn(`Unable to fetch Pact Broker contracts from ${url}: ${error instanceof Error ? error.message : String(error)}`);
|
|
82
|
+
return [];
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return [];
|
|
86
|
+
}
|
|
87
|
+
async function publishVerificationResults(config, results) {
|
|
88
|
+
if (!config.publishResults || !config.url || typeof fetch !== 'function') {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
const baseUrl = config.url.replace(/\/$/, '');
|
|
92
|
+
const url = `${baseUrl}/pacts/provider/${encodeURIComponent(config.providerName)}/verification-results`;
|
|
93
|
+
try {
|
|
94
|
+
await fetch(url, {
|
|
95
|
+
method: 'POST',
|
|
96
|
+
headers: buildHeaders(config),
|
|
97
|
+
body: JSON.stringify({
|
|
98
|
+
providerName: config.providerName,
|
|
99
|
+
results: results.map((result) => ({
|
|
100
|
+
consumer: result.contract.consumer,
|
|
101
|
+
provider: result.contract.provider,
|
|
102
|
+
passed: result.passed,
|
|
103
|
+
interactions: result.interactionResults.map((interactionResult) => ({
|
|
104
|
+
description: interactionResult.interaction.description,
|
|
105
|
+
method: interactionResult.interaction.method,
|
|
106
|
+
path: interactionResult.interaction.path,
|
|
107
|
+
passed: interactionResult.passed,
|
|
108
|
+
reason: interactionResult.reason,
|
|
109
|
+
})),
|
|
110
|
+
})),
|
|
111
|
+
}),
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
console.warn(`Unable to publish Pact verification results to ${url}: ${error instanceof Error ? error.message : String(error)}`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ParsedSchema } from '../streaming/schema';
|
|
2
|
+
export type SchemaCompatibilityType = 'BACKWARD' | 'FORWARD' | 'FULL' | 'NONE';
|
|
3
|
+
export interface SchemaEvolutionResult {
|
|
4
|
+
topicOrQueue: string;
|
|
5
|
+
format: 'avro' | 'protobuf' | 'json-schema';
|
|
6
|
+
isBackwardCompatible: boolean;
|
|
7
|
+
isForwardCompatible: boolean;
|
|
8
|
+
overallRisk: 'NONE' | 'LOW' | 'HIGH' | 'BREAKING';
|
|
9
|
+
changes: Array<{
|
|
10
|
+
field: string;
|
|
11
|
+
changeType: 'added' | 'removed' | 'type-changed' | 'enum-changed';
|
|
12
|
+
breaking: boolean;
|
|
13
|
+
fixHint: string;
|
|
14
|
+
}>;
|
|
15
|
+
}
|
|
16
|
+
export declare function checkSchemaEvolution(oldSchema: ParsedSchema, newSchema: ParsedSchema, topicOrQueue: string): SchemaEvolutionResult;
|
|
17
|
+
//# sourceMappingURL=schemaEvolutionChecker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schemaEvolutionChecker.d.ts","sourceRoot":"","sources":["../../../src/contracts/schemaEvolutionChecker.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAqB,MAAM,qBAAqB,CAAC;AAE3E,MAAM,MAAM,uBAAuB,GAAG,UAAU,GAAG,SAAS,GAAG,MAAM,GAAG,MAAM,CAAC;AAE/E,MAAM,WAAW,qBAAqB;IACpC,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,GAAG,UAAU,GAAG,aAAa,CAAC;IAC5C,oBAAoB,EAAE,OAAO,CAAC;IAC9B,mBAAmB,EAAE,OAAO,CAAC;IAC7B,WAAW,EAAE,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG,UAAU,CAAC;IAClD,OAAO,EAAE,KAAK,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;QACd,UAAU,EAAE,OAAO,GAAG,SAAS,GAAG,cAAc,GAAG,cAAc,CAAC;QAClE,QAAQ,EAAE,OAAO,CAAC;QAClB,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC,CAAC;CACJ;AAkBD,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,YAAY,EACvB,SAAS,EAAE,YAAY,EACvB,YAAY,EAAE,MAAM,GACnB,qBAAqB,CAgFvB"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.checkSchemaEvolution = checkSchemaEvolution;
|
|
4
|
+
function flattenFields(fields, prefix = '') {
|
|
5
|
+
const result = [];
|
|
6
|
+
for (const field of fields) {
|
|
7
|
+
const qualifiedName = prefix ? `${prefix}.${field.name}` : field.name;
|
|
8
|
+
result.push({ ...field, name: qualifiedName });
|
|
9
|
+
if (field.fields && field.fields.length > 0) {
|
|
10
|
+
result.push(...flattenFields(field.fields, qualifiedName));
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
return result;
|
|
14
|
+
}
|
|
15
|
+
function enumSignature(field) {
|
|
16
|
+
var _a;
|
|
17
|
+
return ((_a = field.enumValues) !== null && _a !== void 0 ? _a : []).join('|');
|
|
18
|
+
}
|
|
19
|
+
function checkSchemaEvolution(oldSchema, newSchema, topicOrQueue) {
|
|
20
|
+
var _a, _b;
|
|
21
|
+
const oldFields = flattenFields(oldSchema.fields);
|
|
22
|
+
const newFields = flattenFields(newSchema.fields);
|
|
23
|
+
const oldMap = new Map(oldFields.map((field) => [field.name, field]));
|
|
24
|
+
const newMap = new Map(newFields.map((field) => [field.name, field]));
|
|
25
|
+
const changes = [];
|
|
26
|
+
for (const [name, oldField] of oldMap) {
|
|
27
|
+
const newField = newMap.get(name);
|
|
28
|
+
if (!newField) {
|
|
29
|
+
const breaking = oldField.required && !oldField.defaultValue;
|
|
30
|
+
changes.push({
|
|
31
|
+
field: name,
|
|
32
|
+
changeType: 'removed',
|
|
33
|
+
breaking,
|
|
34
|
+
fixHint: breaking
|
|
35
|
+
? 'Restore the field or keep it optional with a default value to preserve backward compatibility.'
|
|
36
|
+
: 'Optional field removal is usually safe, but keep consumers in sync.',
|
|
37
|
+
});
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
if (oldField.type !== newField.type) {
|
|
41
|
+
changes.push({
|
|
42
|
+
field: name,
|
|
43
|
+
changeType: 'type-changed',
|
|
44
|
+
breaking: true,
|
|
45
|
+
fixHint: 'Avoid changing field types in-place; add a new field and deprecate the old one instead.',
|
|
46
|
+
});
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
if (enumSignature(oldField) !== enumSignature(newField)) {
|
|
50
|
+
const oldValues = new Set((_a = oldField.enumValues) !== null && _a !== void 0 ? _a : []);
|
|
51
|
+
const newValues = new Set((_b = newField.enumValues) !== null && _b !== void 0 ? _b : []);
|
|
52
|
+
const removedValues = [...oldValues].filter((value) => !newValues.has(value));
|
|
53
|
+
const breaking = removedValues.length > 0;
|
|
54
|
+
changes.push({
|
|
55
|
+
field: name,
|
|
56
|
+
changeType: 'enum-changed',
|
|
57
|
+
breaking,
|
|
58
|
+
fixHint: breaking
|
|
59
|
+
? 'Do not remove enum values that existing consumers may still emit or expect.'
|
|
60
|
+
: 'Adding enum values is safe when consumers tolerate unknown values.',
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
for (const [name, newField] of newMap) {
|
|
65
|
+
if (oldMap.has(name))
|
|
66
|
+
continue;
|
|
67
|
+
const breaking = newField.required && !newField.defaultValue && !newField.nullable;
|
|
68
|
+
changes.push({
|
|
69
|
+
field: name,
|
|
70
|
+
changeType: 'added',
|
|
71
|
+
breaking,
|
|
72
|
+
fixHint: breaking
|
|
73
|
+
? 'Make the new field optional or give it a default value for backward compatibility.'
|
|
74
|
+
: 'Optional fields are backward compatible.',
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
const isBackwardCompatible = !changes.some((change) => change.breaking);
|
|
78
|
+
// Treat any detected breaking schema change as both backward and forward incompatible for deploy safety.
|
|
79
|
+
const isForwardCompatible = !changes.some((change) => change.breaking);
|
|
80
|
+
const overallRisk = changes.some((change) => change.breaking)
|
|
81
|
+
? 'BREAKING'
|
|
82
|
+
: changes.length === 0
|
|
83
|
+
? 'NONE'
|
|
84
|
+
: changes.some((change) => change.changeType === 'enum-changed')
|
|
85
|
+
? 'HIGH'
|
|
86
|
+
: 'LOW';
|
|
87
|
+
return {
|
|
88
|
+
topicOrQueue,
|
|
89
|
+
format: newSchema.format,
|
|
90
|
+
isBackwardCompatible,
|
|
91
|
+
isForwardCompatible,
|
|
92
|
+
overallRisk,
|
|
93
|
+
changes,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ContractInteraction } from '../compatibilityCoverage';
|
|
2
|
+
export interface SpringCloudContract {
|
|
3
|
+
filePath: string;
|
|
4
|
+
contractName: string;
|
|
5
|
+
interaction: ContractInteraction;
|
|
6
|
+
format: 'groovy' | 'yaml';
|
|
7
|
+
}
|
|
8
|
+
export declare function inferSpringConsumerName(filePath: string): string;
|
|
9
|
+
export declare function parseSpringCloudContracts(globPattern: string): Promise<SpringCloudContract[]>;
|
|
10
|
+
//# sourceMappingURL=springCloudContractParser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"springCloudContractParser.d.ts","sourceRoot":"","sources":["../../../src/contracts/springCloudContractParser.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAEpE,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,mBAAmB,CAAC;IACjC,MAAM,EAAE,QAAQ,GAAG,MAAM,CAAC;CAC3B;AAwED,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAShE;AAED,wBAAsB,yBAAyB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAqBnG"}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.inferSpringConsumerName = inferSpringConsumerName;
|
|
40
|
+
exports.parseSpringCloudContracts = parseSpringCloudContracts;
|
|
41
|
+
const fs = __importStar(require("fs"));
|
|
42
|
+
const path = __importStar(require("path"));
|
|
43
|
+
const fast_glob_1 = __importDefault(require("fast-glob"));
|
|
44
|
+
const yaml = __importStar(require("js-yaml"));
|
|
45
|
+
const DEFAULT_PATTERNS = [
|
|
46
|
+
'**/src/test/resources/contracts/**/*.groovy',
|
|
47
|
+
'**/src/test/resources/contracts/**/*.yml',
|
|
48
|
+
'**/contracts/**/*.groovy',
|
|
49
|
+
'**/contracts/**/*.yml',
|
|
50
|
+
];
|
|
51
|
+
function parseBodyFields(bodyContent) {
|
|
52
|
+
return bodyContent
|
|
53
|
+
.split(',')
|
|
54
|
+
.map((part) => part.trim())
|
|
55
|
+
.filter(Boolean)
|
|
56
|
+
.reduce((acc, entry) => {
|
|
57
|
+
const match = entry.match(/([A-Za-z0-9_\-]+)\s*:\s*['"]?([^,'"]+)['"]?/);
|
|
58
|
+
if (match)
|
|
59
|
+
acc[match[1]] = match[2];
|
|
60
|
+
return acc;
|
|
61
|
+
}, {});
|
|
62
|
+
}
|
|
63
|
+
function parseGroovyContract(content, filePath) {
|
|
64
|
+
var _a;
|
|
65
|
+
const methodMatch = content.match(/method\s+(\w+)\(\)/i);
|
|
66
|
+
const urlMatch = content.match(/url\s+["']([^"']+)["']/i);
|
|
67
|
+
const statusMatch = content.match(/status\s+(\w+)\(\)/i);
|
|
68
|
+
const bodyMatch = content.match(/body\(\[([^\]]+)\]\)/i);
|
|
69
|
+
if (!methodMatch || !urlMatch)
|
|
70
|
+
return null;
|
|
71
|
+
const statusToken = (_a = statusMatch === null || statusMatch === void 0 ? void 0 : statusMatch[1]) !== null && _a !== void 0 ? _a : '200';
|
|
72
|
+
const status = Number.parseInt(statusToken, 10);
|
|
73
|
+
const expectedStatus = Number.isFinite(status) ? status : 200;
|
|
74
|
+
const expectedBody = bodyMatch ? parseBodyFields(bodyMatch[1]) : undefined;
|
|
75
|
+
return {
|
|
76
|
+
filePath,
|
|
77
|
+
contractName: path.basename(filePath, path.extname(filePath)),
|
|
78
|
+
format: 'groovy',
|
|
79
|
+
interaction: {
|
|
80
|
+
description: path.basename(filePath),
|
|
81
|
+
method: methodMatch[1].toLowerCase(),
|
|
82
|
+
path: urlMatch[1],
|
|
83
|
+
expectedStatus,
|
|
84
|
+
expectedBody,
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
function parseYamlContract(content, filePath) {
|
|
89
|
+
var _a, _b;
|
|
90
|
+
const parsed = yaml.load(content);
|
|
91
|
+
if (!parsed || typeof parsed !== 'object')
|
|
92
|
+
return null;
|
|
93
|
+
const request = ((_a = parsed['request']) !== null && _a !== void 0 ? _a : {});
|
|
94
|
+
const response = ((_b = parsed['response']) !== null && _b !== void 0 ? _b : {});
|
|
95
|
+
const method = typeof request['method'] === 'string' ? request['method'].toLowerCase() : '';
|
|
96
|
+
const url = typeof request['url'] === 'string' ? request['url'] : '';
|
|
97
|
+
if (!method || !url)
|
|
98
|
+
return null;
|
|
99
|
+
return {
|
|
100
|
+
filePath,
|
|
101
|
+
contractName: path.basename(filePath, path.extname(filePath)),
|
|
102
|
+
format: 'yaml',
|
|
103
|
+
interaction: {
|
|
104
|
+
description: path.basename(filePath),
|
|
105
|
+
method,
|
|
106
|
+
path: url,
|
|
107
|
+
expectedStatus: typeof response['status'] === 'number' ? response['status'] : 200,
|
|
108
|
+
expectedBody: response['body'],
|
|
109
|
+
requestBody: request['body'],
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
function inferSpringConsumerName(filePath) {
|
|
114
|
+
const normalized = filePath.replace(/\\/g, '/');
|
|
115
|
+
const contractsIndex = normalized.lastIndexOf('/contracts/');
|
|
116
|
+
if (contractsIndex >= 0) {
|
|
117
|
+
const rest = normalized.slice(contractsIndex + '/contracts/'.length);
|
|
118
|
+
const segment = rest.split('/')[0];
|
|
119
|
+
if (segment && !segment.includes('.'))
|
|
120
|
+
return segment;
|
|
121
|
+
}
|
|
122
|
+
return path.basename(path.dirname(filePath));
|
|
123
|
+
}
|
|
124
|
+
async function parseSpringCloudContracts(globPattern) {
|
|
125
|
+
const patterns = globPattern.trim().length > 0
|
|
126
|
+
? globPattern.split(',').map((entry) => entry.trim()).filter(Boolean)
|
|
127
|
+
: DEFAULT_PATTERNS;
|
|
128
|
+
const files = await (0, fast_glob_1.default)(patterns, { absolute: true, onlyFiles: true, unique: true });
|
|
129
|
+
const contracts = [];
|
|
130
|
+
for (const filePath of files) {
|
|
131
|
+
try {
|
|
132
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
133
|
+
const parsed = filePath.endsWith('.groovy')
|
|
134
|
+
? parseGroovyContract(content, filePath)
|
|
135
|
+
: parseYamlContract(content, filePath);
|
|
136
|
+
if (parsed)
|
|
137
|
+
contracts.push(parsed);
|
|
138
|
+
}
|
|
139
|
+
catch (error) {
|
|
140
|
+
console.warn(`Skipping Spring Cloud Contract file ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return contracts;
|
|
144
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fileClassifier.d.ts","sourceRoot":"","sources":["../../../src/discovery/fileClassifier.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;
|
|
1
|
+
{"version":3,"file":"fileClassifier.d.ts","sourceRoot":"","sources":["../../../src/discovery/fileClassifier.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAOH,MAAM,MAAM,YAAY,GACpB,eAAe,GACf,cAAc,GACd,WAAW,GACX,eAAe,GACf,WAAW,GACX,aAAa,GACb,kBAAkB,GAClB,eAAe,GACf,UAAU,GACV,SAAS,CAAC;AAEd,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,YAAY,CAAC;IACvB,4DAA4D;IAC5D,kBAAkB,EAAE,OAAO,CAAC;CAC7B;AAED,4DAA4D;AAC5D,eAAO,MAAM,4BAA4B,EAAE,WAAW,CAAC,YAAY,CAGjE,CAAC;AAoEH;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,cAAc,CAqG7D;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,cAAc,EAAE,CAEnE;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,cAAc,EAAE,GAAG,cAAc,EAAE,CAEhF;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAepD"}
|
|
@@ -48,6 +48,7 @@ exports.classifyFiles = classifyFiles;
|
|
|
48
48
|
exports.filterCoverageEvidence = filterCoverageEvidence;
|
|
49
49
|
exports.isTestFile = isTestFile;
|
|
50
50
|
const path = __importStar(require("path"));
|
|
51
|
+
const projectDefaults_1 = require("../projectDefaults");
|
|
51
52
|
/** Files that must NEVER contribute to coverage evidence */
|
|
52
53
|
exports.COVERAGE_EVIDENCE_CATEGORIES = new Set([
|
|
53
54
|
'test_code',
|
|
@@ -100,6 +101,7 @@ const CONFIG_BASENAMES = new Set([
|
|
|
100
101
|
const EXTENSION_MAP = {
|
|
101
102
|
'.feature': 'bdd_scenarios',
|
|
102
103
|
};
|
|
104
|
+
const GENERATED_SOURCE_SEGMENTS = new Set(projectDefaults_1.DEFAULT_GENERATED_SOURCE_PATH_SEGMENTS);
|
|
103
105
|
/** Glob-style path-segment patterns for security reports */
|
|
104
106
|
const SECURITY_REPORT_DIRS = ['zap', 'trivy', 'semgrep'];
|
|
105
107
|
/** Glob-style path-segment patterns for performance artifacts */
|
|
@@ -167,6 +169,25 @@ function classifyFile(filePath) {
|
|
|
167
169
|
return make(filePath, 'performance');
|
|
168
170
|
}
|
|
169
171
|
}
|
|
172
|
+
// Generated sources should be treated as service code only, even if their
|
|
173
|
+
// filenames look like tests (e.g. generated OpenAPI stubs).
|
|
174
|
+
if (segments.some((segment) => GENERATED_SOURCE_SEGMENTS.has(segment))) {
|
|
175
|
+
const GENERATED_SOURCE_EXTS = new Set([
|
|
176
|
+
'.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs',
|
|
177
|
+
'.java', '.kt', '.kts', '.groovy',
|
|
178
|
+
'.py', '.rb', '.go', '.cs',
|
|
179
|
+
]);
|
|
180
|
+
if (GENERATED_SOURCE_EXTS.has(ext)) {
|
|
181
|
+
return make(filePath, 'service_code');
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
// Java/Kotlin/Groovy multi-module test sources live under **/src/test/<lang>/.
|
|
185
|
+
if ((normalizedPath.includes('/src/test/java/') ||
|
|
186
|
+
normalizedPath.includes('/src/test/kotlin/') ||
|
|
187
|
+
normalizedPath.includes('/src/test/groovy/')) &&
|
|
188
|
+
['.java', '.kt', '.kts', '.groovy'].includes(ext)) {
|
|
189
|
+
return make(filePath, 'test_code');
|
|
190
|
+
}
|
|
170
191
|
// 8. Test code — matches *.test.*, *.spec.*, *Test.*, *Tests.*, *Spec.*
|
|
171
192
|
const basenameOriginal = path.basename(filePath);
|
|
172
193
|
if (isTestFile(basenameOriginal)) {
|
|
@@ -216,6 +237,10 @@ function isTestFile(basename) {
|
|
|
216
237
|
return true;
|
|
217
238
|
if (/Tests\.(java|kt|kts)$/.test(basename))
|
|
218
239
|
return true;
|
|
240
|
+
if (/IT\.(java|kt|kts)$/.test(basename))
|
|
241
|
+
return true;
|
|
242
|
+
if (/IntegrationTest\.(java|kt|kts)$/.test(basename))
|
|
243
|
+
return true;
|
|
219
244
|
if (/Spec\.(java|kt|kts|rb|ts|js)$/.test(basename))
|
|
220
245
|
return true;
|
|
221
246
|
// Python conventions
|
|
@@ -58,6 +58,8 @@ export interface DiscoveryOptions {
|
|
|
58
58
|
explicitTests?: string[];
|
|
59
59
|
/** If provided, these paths override the auto-discovered contract files */
|
|
60
60
|
explicitContracts?: string[];
|
|
61
|
+
/** Generated source directories that should be scanned even under excluded build/target dirs */
|
|
62
|
+
generatedSourceDirs?: string[];
|
|
61
63
|
}
|
|
62
64
|
/**
|
|
63
65
|
* Discover all project artifacts from `rootDir`.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"projectDiscovery.d.ts","sourceRoot":"","sources":["../../../src/discovery/projectDiscovery.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,OAAO,EAAgB,cAAc,EAAgB,MAAM,kBAAkB,CAAC;AAC9E,OAAO,EAAuB,oBAAoB,EAAoB,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"projectDiscovery.d.ts","sourceRoot":"","sources":["../../../src/discovery/projectDiscovery.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,OAAO,EAAgB,cAAc,EAAgB,MAAM,kBAAkB,CAAC;AAC9E,OAAO,EAAuB,oBAAoB,EAAoB,MAAM,qBAAqB,CAAC;AAMlG,YAAY,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAIlF,MAAM,MAAM,gBAAgB,GACxB,MAAM,GACN,QAAQ,GACR,QAAQ,GACR,MAAM,GACN,YAAY,GACZ,YAAY,GACZ,IAAI,GACJ,QAAQ,CAAC;AAEb,MAAM,MAAM,iBAAiB,GAEzB,OAAO,GACP,QAAQ,GACR,aAAa,GAEb,QAAQ,GACR,UAAU,GAEV,OAAO,GACP,UAAU,GAEV,MAAM,GACN,OAAO,GACP,SAAS,GACT,YAAY,GAEZ,UAAU,GACV,SAAS,CAAC;AAEd,MAAM,WAAW,mBAAmB;IAClC,4BAA4B;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,gDAAgD;IAChD,QAAQ,EAAE,cAAc,EAAE,CAAC;IAC3B,yCAAyC;IACzC,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,oDAAoD;IACpD,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,qCAAqC;IACrC,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,kCAAkC;IAClC,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,uDAAuD;IACvD,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,iCAAiC;IACjC,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,gCAAgC;IAChC,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,qCAAqC;IACrC,SAAS,EAAE,gBAAgB,EAAE,CAAC;IAC9B,+BAA+B;IAC/B,UAAU,EAAE,iBAAiB,EAAE,CAAC;IAChC,0EAA0E;IAC1E,aAAa,EAAE,oBAAoB,EAAE,CAAC;IACtC,8EAA8E;IAC9E,eAAe,EAAE,MAAM,CAAC,MAAM,IAAI,CAAC,mBAAmB,EAAE,UAAU,GAAG,aAAa,GAAG,iBAAiB,GAAG,WAAW,GAAG,YAAY,GAAG,eAAe,CAAC,EAAE,UAAU,GAAG,YAAY,CAAC,CAAC;CACpL;AAED,MAAM,WAAW,gBAAgB;IAC/B,wDAAwD;IACxD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,uDAAuD;IACvD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,uEAAuE;IACvE,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,uEAAuE;IACvE,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,2EAA2E;IAC3E,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,gGAAgG;IAChG,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;CAChC;AAkGD;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,OAAO,GAAE,gBAAqB,GAAG,mBAAmB,CAsFnF"}
|
|
@@ -49,23 +49,9 @@ const fs = __importStar(require("fs"));
|
|
|
49
49
|
const path = __importStar(require("path"));
|
|
50
50
|
const fileClassifier_1 = require("./fileClassifier");
|
|
51
51
|
const frameworkDetector_1 = require("./frameworkDetector");
|
|
52
|
+
const projectDefaults_1 = require("../projectDefaults");
|
|
52
53
|
// ─── Default exclude list ─────────────────────────────────────────────────────
|
|
53
|
-
const DEFAULT_EXCLUDE_DIRS = new Set(
|
|
54
|
-
'node_modules',
|
|
55
|
-
'.git',
|
|
56
|
-
'dist',
|
|
57
|
-
'build',
|
|
58
|
-
'target',
|
|
59
|
-
'.cache',
|
|
60
|
-
'coverage',
|
|
61
|
-
'.nyc_output',
|
|
62
|
-
'__pycache__',
|
|
63
|
-
'.pytest_cache',
|
|
64
|
-
'.tox',
|
|
65
|
-
'vendor',
|
|
66
|
-
'.bundle',
|
|
67
|
-
'.gradle',
|
|
68
|
-
]);
|
|
54
|
+
const DEFAULT_EXCLUDE_DIRS = new Set(projectDefaults_1.DEFAULT_DISCOVERY_EXCLUDE_DIRS);
|
|
69
55
|
// ─── Language detection ───────────────────────────────────────────────────────
|
|
70
56
|
/** Map build-file basenames to language */
|
|
71
57
|
const BUILD_FILE_LANGUAGE_MAP = {
|
|
@@ -169,15 +155,19 @@ function scanDirectory(dir, excludeDirs, maxDepth, currentDepth = 0) {
|
|
|
169
155
|
* "always discovers" behaviour while respecting explicit configuration.
|
|
170
156
|
*/
|
|
171
157
|
function discoverProject(options = {}) {
|
|
172
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
158
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
|
|
173
159
|
const rootDir = (_a = options.rootDir) !== null && _a !== void 0 ? _a : process.cwd();
|
|
174
160
|
const excludeDirs = new Set([
|
|
175
161
|
...DEFAULT_EXCLUDE_DIRS,
|
|
176
162
|
...((_b = options.excludeDirs) !== null && _b !== void 0 ? _b : []),
|
|
177
163
|
]);
|
|
178
164
|
const maxDepth = (_c = options.maxDepth) !== null && _c !== void 0 ? _c : 20;
|
|
165
|
+
const generatedSourceDirs = (_d = options.generatedSourceDirs) !== null && _d !== void 0 ? _d : projectDefaults_1.DEFAULT_GENERATED_SOURCE_DIRS;
|
|
179
166
|
// ── Scan + classify ────────────────────────────────────────────────────────
|
|
180
|
-
const rawPaths =
|
|
167
|
+
const rawPaths = Array.from(new Set([
|
|
168
|
+
...scanDirectory(rootDir, excludeDirs, maxDepth),
|
|
169
|
+
...scanGeneratedSourceDirs(rootDir, generatedSourceDirs, maxDepth),
|
|
170
|
+
]));
|
|
181
171
|
const allFiles = rawPaths.map(fileClassifier_1.classifyFile);
|
|
182
172
|
const byCategory = groupByCategory(allFiles);
|
|
183
173
|
// ── Artifact resolution ────────────────────────────────────────────────────
|
|
@@ -190,14 +180,14 @@ function discoverProject(options = {}) {
|
|
|
190
180
|
securityReportFiles: 'discovered',
|
|
191
181
|
serviceFiles: 'discovered',
|
|
192
182
|
};
|
|
193
|
-
let specs = paths((
|
|
183
|
+
let specs = paths((_e = byCategory.specification) !== null && _e !== void 0 ? _e : []);
|
|
194
184
|
if (options.explicitSpecs && options.explicitSpecs.length > 0) {
|
|
195
185
|
specs = options.explicitSpecs;
|
|
196
186
|
discoverySource.specs = 'explicit';
|
|
197
187
|
}
|
|
198
|
-
const featureFiles = paths((
|
|
188
|
+
const featureFiles = paths((_f = byCategory.bdd_scenarios) !== null && _f !== void 0 ? _f : []);
|
|
199
189
|
let testFiles = [
|
|
200
|
-
...paths((
|
|
190
|
+
...paths((_g = byCategory.test_code) !== null && _g !== void 0 ? _g : []),
|
|
201
191
|
...featureFiles,
|
|
202
192
|
];
|
|
203
193
|
if (options.explicitTests && options.explicitTests.length > 0) {
|
|
@@ -205,14 +195,14 @@ function discoverProject(options = {}) {
|
|
|
205
195
|
discoverySource.testFiles = 'explicit';
|
|
206
196
|
discoverySource.featureFiles = 'explicit';
|
|
207
197
|
}
|
|
208
|
-
let contractFiles = paths((
|
|
198
|
+
let contractFiles = paths((_h = byCategory.contracts) !== null && _h !== void 0 ? _h : []);
|
|
209
199
|
if (options.explicitContracts && options.explicitContracts.length > 0) {
|
|
210
200
|
contractFiles = options.explicitContracts;
|
|
211
201
|
discoverySource.contractFiles = 'explicit';
|
|
212
202
|
}
|
|
213
|
-
const performanceFiles = paths((
|
|
214
|
-
const securityReportFiles = paths((
|
|
215
|
-
const serviceFiles = paths((
|
|
203
|
+
const performanceFiles = paths((_j = byCategory.performance) !== null && _j !== void 0 ? _j : []);
|
|
204
|
+
const securityReportFiles = paths((_k = byCategory.security_reports) !== null && _k !== void 0 ? _k : []);
|
|
205
|
+
const serviceFiles = paths((_l = byCategory.service_code) !== null && _l !== void 0 ? _l : []);
|
|
216
206
|
// ── Language detection ─────────────────────────────────────────────────────
|
|
217
207
|
const languages = detectLanguages(rawPaths);
|
|
218
208
|
// ── Framework detection ────────────────────────────────────────────────────
|
|
@@ -239,6 +229,16 @@ function discoverProject(options = {}) {
|
|
|
239
229
|
discoverySource,
|
|
240
230
|
};
|
|
241
231
|
}
|
|
232
|
+
function scanGeneratedSourceDirs(rootDir, generatedSourceDirs, maxDepth) {
|
|
233
|
+
const results = [];
|
|
234
|
+
for (const relDir of generatedSourceDirs) {
|
|
235
|
+
const fullPath = path.join(rootDir, relDir);
|
|
236
|
+
if (!fs.existsSync(fullPath))
|
|
237
|
+
continue;
|
|
238
|
+
results.push(...scanDirectory(fullPath, new Set(['node_modules', '.git', '.gradle']), maxDepth));
|
|
239
|
+
}
|
|
240
|
+
return results;
|
|
241
|
+
}
|
|
242
242
|
// ─── Language detection helpers ───────────────────────────────────────────────
|
|
243
243
|
function detectLanguages(filePaths) {
|
|
244
244
|
const found = new Set();
|