@mitre/hdf-diff 2.0.0
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/LICENSE.md +55 -0
- package/README.md +87 -0
- package/dist/diff.d.ts +49 -0
- package/dist/diff.d.ts.map +1 -0
- package/dist/diff.js +791 -0
- package/dist/diff.js.map +1 -0
- package/dist/exit-codes.d.ts +42 -0
- package/dist/exit-codes.d.ts.map +1 -0
- package/dist/exit-codes.js +74 -0
- package/dist/exit-codes.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/matching/cci-match.d.ts +14 -0
- package/dist/matching/cci-match.d.ts.map +1 -0
- package/dist/matching/cci-match.js +102 -0
- package/dist/matching/cci-match.js.map +1 -0
- package/dist/matching/exact-id.d.ts +9 -0
- package/dist/matching/exact-id.d.ts.map +1 -0
- package/dist/matching/exact-id.js +88 -0
- package/dist/matching/exact-id.js.map +1 -0
- package/dist/matching/fuzzy-match.d.ts +26 -0
- package/dist/matching/fuzzy-match.d.ts.map +1 -0
- package/dist/matching/fuzzy-match.js +145 -0
- package/dist/matching/fuzzy-match.js.map +1 -0
- package/dist/matching/index.d.ts +36 -0
- package/dist/matching/index.d.ts.map +1 -0
- package/dist/matching/index.js +71 -0
- package/dist/matching/index.js.map +1 -0
- package/dist/matching/mapped-id.d.ts +13 -0
- package/dist/matching/mapped-id.d.ts.map +1 -0
- package/dist/matching/mapped-id.js +101 -0
- package/dist/matching/mapped-id.js.map +1 -0
- package/dist/matching/types.d.ts +34 -0
- package/dist/matching/types.d.ts.map +1 -0
- package/dist/matching/types.js +2 -0
- package/dist/matching/types.js.map +1 -0
- package/dist/normalize.d.ts +20 -0
- package/dist/normalize.d.ts.map +1 -0
- package/dist/normalize.js +120 -0
- package/dist/normalize.js.map +1 -0
- package/dist/renderers/csv.d.ts +15 -0
- package/dist/renderers/csv.d.ts.map +1 -0
- package/dist/renderers/csv.js +77 -0
- package/dist/renderers/csv.js.map +1 -0
- package/dist/renderers/filter.d.ts +11 -0
- package/dist/renderers/filter.d.ts.map +1 -0
- package/dist/renderers/filter.js +29 -0
- package/dist/renderers/filter.js.map +1 -0
- package/dist/renderers/index.d.ts +17 -0
- package/dist/renderers/index.d.ts.map +1 -0
- package/dist/renderers/index.js +29 -0
- package/dist/renderers/index.js.map +1 -0
- package/dist/renderers/json.d.ts +13 -0
- package/dist/renderers/json.d.ts.map +1 -0
- package/dist/renderers/json.js +41 -0
- package/dist/renderers/json.js.map +1 -0
- package/dist/renderers/markdown.d.ts +13 -0
- package/dist/renderers/markdown.d.ts.map +1 -0
- package/dist/renderers/markdown.js +133 -0
- package/dist/renderers/markdown.js.map +1 -0
- package/dist/renderers/terminal.d.ts +14 -0
- package/dist/renderers/terminal.d.ts.map +1 -0
- package/dist/renderers/terminal.js +173 -0
- package/dist/renderers/terminal.js.map +1 -0
- package/dist/renderers/types.d.ts +12 -0
- package/dist/renderers/types.d.ts.map +1 -0
- package/dist/renderers/types.js +2 -0
- package/dist/renderers/types.js.map +1 -0
- package/dist/sbom.d.ts +42 -0
- package/dist/sbom.d.ts.map +1 -0
- package/dist/sbom.js +203 -0
- package/dist/sbom.js.map +1 -0
- package/dist/status.d.ts +27 -0
- package/dist/status.d.ts.map +1 -0
- package/dist/status.js +137 -0
- package/dist/status.js.map +1 -0
- package/dist/summary.d.ts +6 -0
- package/dist/summary.d.ts.map +1 -0
- package/dist/summary.js +47 -0
- package/dist/summary.js.map +1 -0
- package/dist/types.d.ts +208 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/validate.d.ts +20 -0
- package/dist/validate.d.ts.map +1 -0
- package/dist/validate.js +108 -0
- package/dist/validate.js.map +1 -0
- package/package.json +54 -0
package/dist/sbom.d.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SBOM comparison for the HDF diff engine.
|
|
3
|
+
*
|
|
4
|
+
* Compares two SBOM documents (CycloneDX or SPDX JSON) and produces
|
|
5
|
+
* package-level diffs: added, removed, updated, unchanged packages.
|
|
6
|
+
*
|
|
7
|
+
* Format auto-detection:
|
|
8
|
+
* - CycloneDX: has `bomFormat: "CycloneDX"`, packages in `components[]`
|
|
9
|
+
* - SPDX: has `spdxVersion`, packages in `packages[]`
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* The comparison result for a single package between two SBOMs.
|
|
13
|
+
*/
|
|
14
|
+
export interface PackageDiff {
|
|
15
|
+
purl: string;
|
|
16
|
+
name: string;
|
|
17
|
+
state: 'added' | 'removed' | 'updated' | 'unchanged';
|
|
18
|
+
oldVersion?: string;
|
|
19
|
+
newVersion?: string;
|
|
20
|
+
licenses?: string[];
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* The complete result of comparing two SBOM documents.
|
|
24
|
+
*/
|
|
25
|
+
export interface SbomDiffResult {
|
|
26
|
+
packageDiffs: PackageDiff[];
|
|
27
|
+
added: number;
|
|
28
|
+
removed: number;
|
|
29
|
+
updated: number;
|
|
30
|
+
unchanged: number;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Compare two SBOM documents (CycloneDX or SPDX JSON strings) and return
|
|
34
|
+
* package-level diffs.
|
|
35
|
+
*
|
|
36
|
+
* @param oldJson - JSON string of the old SBOM document
|
|
37
|
+
* @param newJson - JSON string of the new SBOM document
|
|
38
|
+
* @returns Structured diff result with per-package state and aggregate counts
|
|
39
|
+
* @throws Error if either input is not valid JSON or not a recognized SBOM format
|
|
40
|
+
*/
|
|
41
|
+
export declare function diffSboms(oldJson: string, newJson: string): SbomDiffResult;
|
|
42
|
+
//# sourceMappingURL=sbom.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sbom.d.ts","sourceRoot":"","sources":["../src/sbom.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,OAAO,GAAG,SAAS,GAAG,SAAS,GAAG,WAAW,CAAC;IACrD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,YAAY,EAAE,WAAW,EAAE,CAAC;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAYD;;;;;;;;GAQG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,cAAc,CA2E1E"}
|
package/dist/sbom.js
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SBOM comparison for the HDF diff engine.
|
|
3
|
+
*
|
|
4
|
+
* Compares two SBOM documents (CycloneDX or SPDX JSON) and produces
|
|
5
|
+
* package-level diffs: added, removed, updated, unchanged packages.
|
|
6
|
+
*
|
|
7
|
+
* Format auto-detection:
|
|
8
|
+
* - CycloneDX: has `bomFormat: "CycloneDX"`, packages in `components[]`
|
|
9
|
+
* - SPDX: has `spdxVersion`, packages in `packages[]`
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Compare two SBOM documents (CycloneDX or SPDX JSON strings) and return
|
|
13
|
+
* package-level diffs.
|
|
14
|
+
*
|
|
15
|
+
* @param oldJson - JSON string of the old SBOM document
|
|
16
|
+
* @param newJson - JSON string of the new SBOM document
|
|
17
|
+
* @returns Structured diff result with per-package state and aggregate counts
|
|
18
|
+
* @throws Error if either input is not valid JSON or not a recognized SBOM format
|
|
19
|
+
*/
|
|
20
|
+
export function diffSboms(oldJson, newJson) {
|
|
21
|
+
const oldParsed = JSON.parse(oldJson);
|
|
22
|
+
const newParsed = JSON.parse(newJson);
|
|
23
|
+
const oldPackages = extractPackages(oldParsed);
|
|
24
|
+
const newPackages = extractPackages(newParsed);
|
|
25
|
+
const oldMap = buildPackageMap(oldPackages);
|
|
26
|
+
const newMap = buildPackageMap(newPackages);
|
|
27
|
+
const diffs = [];
|
|
28
|
+
const seen = new Set();
|
|
29
|
+
// Check old packages against new
|
|
30
|
+
for (const [key, oldPkg] of oldMap) {
|
|
31
|
+
seen.add(key);
|
|
32
|
+
const newPkg = newMap.get(key);
|
|
33
|
+
if (newPkg) {
|
|
34
|
+
if (oldPkg.version !== newPkg.version) {
|
|
35
|
+
diffs.push({
|
|
36
|
+
purl: newPkg.purl || newPkg.name,
|
|
37
|
+
name: newPkg.name,
|
|
38
|
+
state: 'updated',
|
|
39
|
+
oldVersion: oldPkg.version,
|
|
40
|
+
newVersion: newPkg.version,
|
|
41
|
+
licenses: newPkg.licenses.length > 0 ? newPkg.licenses : undefined,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
diffs.push({
|
|
46
|
+
purl: oldPkg.purl || oldPkg.name,
|
|
47
|
+
name: oldPkg.name,
|
|
48
|
+
state: 'unchanged',
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
diffs.push({
|
|
54
|
+
purl: oldPkg.purl || oldPkg.name,
|
|
55
|
+
name: oldPkg.name,
|
|
56
|
+
state: 'removed',
|
|
57
|
+
oldVersion: oldPkg.version,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
// Check for added packages
|
|
62
|
+
for (const [key, newPkg] of newMap) {
|
|
63
|
+
if (!seen.has(key)) {
|
|
64
|
+
diffs.push({
|
|
65
|
+
purl: newPkg.purl || newPkg.name,
|
|
66
|
+
name: newPkg.name,
|
|
67
|
+
state: 'added',
|
|
68
|
+
newVersion: newPkg.version,
|
|
69
|
+
licenses: newPkg.licenses.length > 0 ? newPkg.licenses : undefined,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// Sort by name for deterministic output
|
|
74
|
+
diffs.sort((a, b) => a.name.localeCompare(b.name));
|
|
75
|
+
// Count states
|
|
76
|
+
let added = 0;
|
|
77
|
+
let removed = 0;
|
|
78
|
+
let updated = 0;
|
|
79
|
+
let unchanged = 0;
|
|
80
|
+
for (const d of diffs) {
|
|
81
|
+
switch (d.state) {
|
|
82
|
+
case 'added':
|
|
83
|
+
added++;
|
|
84
|
+
break;
|
|
85
|
+
case 'removed':
|
|
86
|
+
removed++;
|
|
87
|
+
break;
|
|
88
|
+
case 'updated':
|
|
89
|
+
updated++;
|
|
90
|
+
break;
|
|
91
|
+
case 'unchanged':
|
|
92
|
+
unchanged++;
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return { packageDiffs: diffs, added, removed, updated, unchanged };
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Extract packages from an SBOM document, auto-detecting the format.
|
|
100
|
+
*/
|
|
101
|
+
function extractPackages(doc) {
|
|
102
|
+
if (doc['bomFormat'] === 'CycloneDX') {
|
|
103
|
+
return extractCycloneDXPackages(doc);
|
|
104
|
+
}
|
|
105
|
+
if (typeof doc['spdxVersion'] === 'string') {
|
|
106
|
+
return extractSPDXPackages(doc);
|
|
107
|
+
}
|
|
108
|
+
throw new Error('Unrecognized SBOM format: expected CycloneDX (bomFormat) or SPDX (spdxVersion)');
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Extract packages from a CycloneDX document.
|
|
112
|
+
* Reads components[] with purl, name, version fields.
|
|
113
|
+
*/
|
|
114
|
+
function extractCycloneDXPackages(doc) {
|
|
115
|
+
const components = doc['components'];
|
|
116
|
+
if (!Array.isArray(components)) {
|
|
117
|
+
return [];
|
|
118
|
+
}
|
|
119
|
+
return components.map((comp) => {
|
|
120
|
+
const licenses = extractCycloneDXLicenses(comp);
|
|
121
|
+
return {
|
|
122
|
+
purl: comp['purl'] ?? '',
|
|
123
|
+
name: comp['name'] ?? '',
|
|
124
|
+
version: comp['version'] ?? '',
|
|
125
|
+
licenses,
|
|
126
|
+
};
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Extract license strings from a CycloneDX component.
|
|
131
|
+
* CycloneDX licenses can be in `licenses[].license.id` or `licenses[].license.name`.
|
|
132
|
+
*/
|
|
133
|
+
function extractCycloneDXLicenses(comp) {
|
|
134
|
+
const licensesArr = comp['licenses'];
|
|
135
|
+
if (!Array.isArray(licensesArr)) {
|
|
136
|
+
return [];
|
|
137
|
+
}
|
|
138
|
+
const result = [];
|
|
139
|
+
for (const entry of licensesArr) {
|
|
140
|
+
const license = entry['license'];
|
|
141
|
+
if (license) {
|
|
142
|
+
const id = license['id'];
|
|
143
|
+
const name = license['name'];
|
|
144
|
+
if (id) {
|
|
145
|
+
result.push(id);
|
|
146
|
+
}
|
|
147
|
+
else if (name) {
|
|
148
|
+
result.push(name);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return result;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Extract packages from an SPDX document.
|
|
156
|
+
* Reads packages[] with name, versionInfo, and externalRefs (for PURL).
|
|
157
|
+
*/
|
|
158
|
+
function extractSPDXPackages(doc) {
|
|
159
|
+
const packages = doc['packages'];
|
|
160
|
+
if (!Array.isArray(packages)) {
|
|
161
|
+
return [];
|
|
162
|
+
}
|
|
163
|
+
return packages.map((pkg) => {
|
|
164
|
+
const purl = extractSPDXPurl(pkg);
|
|
165
|
+
const license = pkg['licenseConcluded'] ?? pkg['licenseDeclared'] ?? '';
|
|
166
|
+
return {
|
|
167
|
+
purl,
|
|
168
|
+
name: pkg['name'] ?? '',
|
|
169
|
+
version: pkg['versionInfo'] ?? '',
|
|
170
|
+
licenses: license && license !== 'NOASSERTION' ? [license] : [],
|
|
171
|
+
};
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Extract a PURL from an SPDX package's externalRefs array.
|
|
176
|
+
*/
|
|
177
|
+
function extractSPDXPurl(pkg) {
|
|
178
|
+
const refs = pkg['externalRefs'];
|
|
179
|
+
if (!Array.isArray(refs)) {
|
|
180
|
+
return '';
|
|
181
|
+
}
|
|
182
|
+
for (const ref of refs) {
|
|
183
|
+
const refType = ref['referenceType'];
|
|
184
|
+
if (refType === 'purl') {
|
|
185
|
+
return ref['referenceLocator'] ?? '';
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return '';
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Build a lookup map of packages indexed by name (version-independent key).
|
|
192
|
+
*/
|
|
193
|
+
function buildPackageMap(packages) {
|
|
194
|
+
const map = new Map();
|
|
195
|
+
for (const pkg of packages) {
|
|
196
|
+
const key = pkg.name || pkg.purl;
|
|
197
|
+
if (key) {
|
|
198
|
+
map.set(key, pkg);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return map;
|
|
202
|
+
}
|
|
203
|
+
//# sourceMappingURL=sbom.js.map
|
package/dist/sbom.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sbom.js","sourceRoot":"","sources":["../src/sbom.ts"],"names":[],"mappings":"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"}
|
package/dist/status.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { ChangeReason, RequirementState } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Determine the effective status of a requirement from its results and overrides.
|
|
4
|
+
*
|
|
5
|
+
* Priority:
|
|
6
|
+
* 1. impact === 0 → notApplicable (regardless of results)
|
|
7
|
+
* 2. effectiveStatus field set (and no statusOverrides) → use it
|
|
8
|
+
* 3. Non-expired statusOverrides → use first non-expired
|
|
9
|
+
* 4. Aggregate results using worst-wins
|
|
10
|
+
* 5. Empty results → notReviewed
|
|
11
|
+
*/
|
|
12
|
+
export declare function computeEffectiveStatus(requirement: Record<string, unknown>, referenceTimestamp?: string): string;
|
|
13
|
+
/**
|
|
14
|
+
* Classify why the status changed between two requirements.
|
|
15
|
+
* Returns an array of change reasons (a status change can have multiple causes).
|
|
16
|
+
*/
|
|
17
|
+
export declare function classifyChangeReasons(oldReq: Record<string, unknown>, newReq: Record<string, unknown>, oldTimestamp?: string, newTimestamp?: string): ChangeReason[];
|
|
18
|
+
/**
|
|
19
|
+
* Classify the overall diff status based on old and new effective statuses.
|
|
20
|
+
*
|
|
21
|
+
* - If old is failing and new is passing → 'fixed'
|
|
22
|
+
* - If old is passing and new is failing → 'regressed'
|
|
23
|
+
* - If statuses are equal → 'unchanged'
|
|
24
|
+
* - Otherwise → 'updated'
|
|
25
|
+
*/
|
|
26
|
+
export declare function classifyDiffStatus(oldEffectiveStatus: string, newEffectiveStatus: string): RequirementState;
|
|
27
|
+
//# sourceMappingURL=status.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../src/status.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AA0BjE;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CACpC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACpC,kBAAkB,CAAC,EAAE,MAAM,GAC1B,MAAM,CAyCR;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,YAAY,CAAC,EAAE,MAAM,EACrB,YAAY,CAAC,EAAE,MAAM,GACpB,YAAY,EAAE,CAuDhB;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,kBAAkB,EAAE,MAAM,EAC1B,kBAAkB,EAAE,MAAM,GACzB,gBAAgB,CAkBlB"}
|
package/dist/status.js
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/** Status severity ranking — higher index = worse */
|
|
2
|
+
const STATUS_SEVERITY = [
|
|
3
|
+
'notApplicable',
|
|
4
|
+
'notReviewed',
|
|
5
|
+
'passed',
|
|
6
|
+
'failed',
|
|
7
|
+
'error',
|
|
8
|
+
];
|
|
9
|
+
/** Statuses that count as "passing" for fixed/regressed classification */
|
|
10
|
+
const PASSING_STATUSES = new Set(['passed']);
|
|
11
|
+
/** Statuses that count as "failing" for fixed/regressed classification */
|
|
12
|
+
const FAILING_STATUSES = new Set(['failed', 'error', 'notReviewed']);
|
|
13
|
+
/**
|
|
14
|
+
* Determine the effective status of a requirement from its results and overrides.
|
|
15
|
+
*
|
|
16
|
+
* Priority:
|
|
17
|
+
* 1. impact === 0 → notApplicable (regardless of results)
|
|
18
|
+
* 2. effectiveStatus field set (and no statusOverrides) → use it
|
|
19
|
+
* 3. Non-expired statusOverrides → use first non-expired
|
|
20
|
+
* 4. Aggregate results using worst-wins
|
|
21
|
+
* 5. Empty results → notReviewed
|
|
22
|
+
*/
|
|
23
|
+
export function computeEffectiveStatus(requirement, referenceTimestamp) {
|
|
24
|
+
const impact = requirement['impact'];
|
|
25
|
+
if (impact === 0) {
|
|
26
|
+
return 'notApplicable';
|
|
27
|
+
}
|
|
28
|
+
const overrides = requirement['statusOverrides'];
|
|
29
|
+
if (overrides && overrides.length > 0) {
|
|
30
|
+
const refTime = referenceTimestamp ? new Date(referenceTimestamp).getTime() : Date.now();
|
|
31
|
+
for (const override of overrides) {
|
|
32
|
+
const expiresAt = new Date(override.expiresAt).getTime();
|
|
33
|
+
if (expiresAt > refTime) {
|
|
34
|
+
return override.status;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
// All overrides expired — fall through to results
|
|
38
|
+
}
|
|
39
|
+
// If effectiveStatus is set and there are no overrides, use it
|
|
40
|
+
const effectiveStatus = requirement['effectiveStatus'];
|
|
41
|
+
if (effectiveStatus && (!overrides || overrides.length === 0)) {
|
|
42
|
+
return effectiveStatus;
|
|
43
|
+
}
|
|
44
|
+
const results = requirement['results'];
|
|
45
|
+
if (!results || results.length === 0) {
|
|
46
|
+
return 'notReviewed';
|
|
47
|
+
}
|
|
48
|
+
// Worst-wins: find the status with the highest severity index
|
|
49
|
+
let worstIndex = -1;
|
|
50
|
+
let worstStatus = 'notReviewed';
|
|
51
|
+
for (const result of results) {
|
|
52
|
+
const idx = STATUS_SEVERITY.indexOf(result.status);
|
|
53
|
+
if (idx > worstIndex) {
|
|
54
|
+
worstIndex = idx;
|
|
55
|
+
worstStatus = result.status;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return worstStatus;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Classify why the status changed between two requirements.
|
|
62
|
+
* Returns an array of change reasons (a status change can have multiple causes).
|
|
63
|
+
*/
|
|
64
|
+
export function classifyChangeReasons(oldReq, newReq, oldTimestamp, newTimestamp) {
|
|
65
|
+
const reasons = [];
|
|
66
|
+
// Check result status changes
|
|
67
|
+
const oldResults = oldReq['results'] ?? [];
|
|
68
|
+
const newResults = newReq['results'] ?? [];
|
|
69
|
+
const oldResultStatuses = oldResults.map((r) => r.status).sort();
|
|
70
|
+
const newResultStatuses = newResults.map((r) => r.status).sort();
|
|
71
|
+
if (JSON.stringify(oldResultStatuses) !== JSON.stringify(newResultStatuses)) {
|
|
72
|
+
reasons.push('resultChanged');
|
|
73
|
+
}
|
|
74
|
+
// Check override changes
|
|
75
|
+
const oldOverrides = oldReq['statusOverrides'] ?? [];
|
|
76
|
+
const newOverrides = newReq['statusOverrides'] ?? [];
|
|
77
|
+
if (newOverrides.length > oldOverrides.length) {
|
|
78
|
+
reasons.push('overrideAdded');
|
|
79
|
+
}
|
|
80
|
+
else if (newOverrides.length < oldOverrides.length) {
|
|
81
|
+
reasons.push('overrideRemoved');
|
|
82
|
+
}
|
|
83
|
+
// Check for override expiration between scans
|
|
84
|
+
if (oldTimestamp && newTimestamp && oldOverrides.length > 0) {
|
|
85
|
+
const oldTime = new Date(oldTimestamp).getTime();
|
|
86
|
+
const newTime = new Date(newTimestamp).getTime();
|
|
87
|
+
for (const override of oldOverrides) {
|
|
88
|
+
const expiresAt = new Date(override.expiresAt).getTime();
|
|
89
|
+
if (expiresAt > oldTime && expiresAt <= newTime) {
|
|
90
|
+
reasons.push('overrideExpired');
|
|
91
|
+
break; // Only report once
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// Check impact changes
|
|
96
|
+
const oldImpact = oldReq['impact'];
|
|
97
|
+
const newImpact = newReq['impact'];
|
|
98
|
+
if (oldImpact !== newImpact) {
|
|
99
|
+
reasons.push('impactChanged');
|
|
100
|
+
}
|
|
101
|
+
// Check baseline metadata changes (tags, descriptions, title)
|
|
102
|
+
const oldTags = JSON.stringify(oldReq['tags'] ?? {});
|
|
103
|
+
const newTags = JSON.stringify(newReq['tags'] ?? {});
|
|
104
|
+
const oldDescs = JSON.stringify(oldReq['descriptions'] ?? []);
|
|
105
|
+
const newDescs = JSON.stringify(newReq['descriptions'] ?? []);
|
|
106
|
+
const oldTitle = oldReq['title'];
|
|
107
|
+
const newTitle = newReq['title'];
|
|
108
|
+
if (oldTags !== newTags || oldDescs !== newDescs || oldTitle !== newTitle) {
|
|
109
|
+
reasons.push('metadataChanged');
|
|
110
|
+
}
|
|
111
|
+
return reasons;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Classify the overall diff status based on old and new effective statuses.
|
|
115
|
+
*
|
|
116
|
+
* - If old is failing and new is passing → 'fixed'
|
|
117
|
+
* - If old is passing and new is failing → 'regressed'
|
|
118
|
+
* - If statuses are equal → 'unchanged'
|
|
119
|
+
* - Otherwise → 'updated'
|
|
120
|
+
*/
|
|
121
|
+
export function classifyDiffStatus(oldEffectiveStatus, newEffectiveStatus) {
|
|
122
|
+
if (oldEffectiveStatus === newEffectiveStatus) {
|
|
123
|
+
return 'unchanged';
|
|
124
|
+
}
|
|
125
|
+
const oldIsFailing = FAILING_STATUSES.has(oldEffectiveStatus);
|
|
126
|
+
const newIsPassing = PASSING_STATUSES.has(newEffectiveStatus);
|
|
127
|
+
const oldIsPassing = PASSING_STATUSES.has(oldEffectiveStatus);
|
|
128
|
+
const newIsFailing = FAILING_STATUSES.has(newEffectiveStatus);
|
|
129
|
+
if (oldIsFailing && newIsPassing) {
|
|
130
|
+
return 'fixed';
|
|
131
|
+
}
|
|
132
|
+
if (oldIsPassing && newIsFailing) {
|
|
133
|
+
return 'regressed';
|
|
134
|
+
}
|
|
135
|
+
return 'updated';
|
|
136
|
+
}
|
|
137
|
+
//# sourceMappingURL=status.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.js","sourceRoot":"","sources":["../src/status.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ComparisonSummary, RequirementDiff } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Compute summary counts from an array of RequirementDiff entries.
|
|
4
|
+
*/
|
|
5
|
+
export declare function computeSummary(requirements: RequirementDiff[]): ComparisonSummary;
|
|
6
|
+
//# sourceMappingURL=summary.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"summary.d.ts","sourceRoot":"","sources":["../src/summary.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAErE;;GAEG;AACH,wBAAgB,cAAc,CAAC,YAAY,EAAE,eAAe,EAAE,GAAG,iBAAiB,CA4CjF"}
|
package/dist/summary.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compute summary counts from an array of RequirementDiff entries.
|
|
3
|
+
*/
|
|
4
|
+
export function computeSummary(requirements) {
|
|
5
|
+
const summary = {
|
|
6
|
+
fixed: 0,
|
|
7
|
+
regressed: 0,
|
|
8
|
+
new: 0,
|
|
9
|
+
absent: 0,
|
|
10
|
+
unchanged: 0,
|
|
11
|
+
updated: 0,
|
|
12
|
+
total: requirements.length,
|
|
13
|
+
matchedCount: 0,
|
|
14
|
+
unmatchedOldCount: 0,
|
|
15
|
+
unmatchedNewCount: 0,
|
|
16
|
+
};
|
|
17
|
+
for (const req of requirements) {
|
|
18
|
+
switch (req.state) {
|
|
19
|
+
case 'fixed':
|
|
20
|
+
summary.fixed++;
|
|
21
|
+
summary.matchedCount++;
|
|
22
|
+
break;
|
|
23
|
+
case 'regressed':
|
|
24
|
+
summary.regressed++;
|
|
25
|
+
summary.matchedCount++;
|
|
26
|
+
break;
|
|
27
|
+
case 'new':
|
|
28
|
+
summary.new++;
|
|
29
|
+
summary.unmatchedNewCount++;
|
|
30
|
+
break;
|
|
31
|
+
case 'absent':
|
|
32
|
+
summary.absent++;
|
|
33
|
+
summary.unmatchedOldCount++;
|
|
34
|
+
break;
|
|
35
|
+
case 'unchanged':
|
|
36
|
+
summary.unchanged++;
|
|
37
|
+
summary.matchedCount++;
|
|
38
|
+
break;
|
|
39
|
+
case 'updated':
|
|
40
|
+
summary.updated++;
|
|
41
|
+
summary.matchedCount++;
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return summary;
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=summary.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"summary.js","sourceRoot":"","sources":["../src/summary.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,YAA+B;IAC5D,MAAM,OAAO,GAAsB;QACjC,KAAK,EAAE,CAAC;QACR,SAAS,EAAE,CAAC;QACZ,GAAG,EAAE,CAAC;QACN,MAAM,EAAE,CAAC;QACT,SAAS,EAAE,CAAC;QACZ,OAAO,EAAE,CAAC;QACV,KAAK,EAAE,YAAY,CAAC,MAAM;QAC1B,YAAY,EAAE,CAAC;QACf,iBAAiB,EAAE,CAAC;QACpB,iBAAiB,EAAE,CAAC;KACrB,CAAC;IAEF,KAAK,MAAM,GAAG,IAAI,YAAY,EAAE,CAAC;QAC/B,QAAQ,GAAG,CAAC,KAAK,EAAE,CAAC;YAClB,KAAK,OAAO;gBACV,OAAO,CAAC,KAAK,EAAE,CAAC;gBAChB,OAAO,CAAC,YAAY,EAAE,CAAC;gBACvB,MAAM;YACR,KAAK,WAAW;gBACd,OAAO,CAAC,SAAS,EAAE,CAAC;gBACpB,OAAO,CAAC,YAAY,EAAE,CAAC;gBACvB,MAAM;YACR,KAAK,KAAK;gBACR,OAAO,CAAC,GAAG,EAAE,CAAC;gBACd,OAAO,CAAC,iBAAiB,EAAE,CAAC;gBAC5B,MAAM;YACR,KAAK,QAAQ;gBACX,OAAO,CAAC,MAAM,EAAE,CAAC;gBACjB,OAAO,CAAC,iBAAiB,EAAE,CAAC;gBAC5B,MAAM;YACR,KAAK,WAAW;gBACd,OAAO,CAAC,SAAS,EAAE,CAAC;gBACpB,OAAO,CAAC,YAAY,EAAE,CAAC;gBACvB,MAAM;YACR,KAAK,SAAS;gBACZ,OAAO,CAAC,OAAO,EAAE,CAAC;gBAClB,OAAO,CAAC,YAAY,EAAE,CAAC;gBACvB,MAAM;QACV,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC"}
|