@produtype/core 0.45.0 → 0.46.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/dist/report/buildReport.js +11 -2
- package/dist/report/categoryScores.d.ts +8 -1
- package/dist/report/categoryScores.js +4 -3
- package/dist/report/executiveSummary.d.ts +1 -1
- package/dist/report/executiveSummary.js +1 -1
- package/dist/report/markdownReport.js +7 -5
- package/dist/report/types.d.ts +2 -1
- package/package.json +1 -1
|
@@ -287,6 +287,15 @@ function buildReport(analysis, options) {
|
|
|
287
287
|
const maturityLevel = overallScore === null
|
|
288
288
|
? 'inconclusive'
|
|
289
289
|
: (0, score_1.computeMaturity)(overallScore, coverage, openCriticals);
|
|
290
|
+
/**
|
|
291
|
+
* The observed half goes with it.
|
|
292
|
+
*
|
|
293
|
+
* "Overall score: not scored" printed directly above "Observed score: 100/100" —
|
|
294
|
+
* the report refusing to characterise a repository and then praising it in the next
|
|
295
|
+
* line. The observed number is the same arithmetic over the same three checks: if
|
|
296
|
+
* it cannot support a reading it cannot support half of one either.
|
|
297
|
+
*/
|
|
298
|
+
const reportedObservedScore = inconclusive ? null : observedScore;
|
|
290
299
|
const findingsByCategory = Object.fromEntries(types_1.CATEGORIES.map((c) => [c, []]));
|
|
291
300
|
for (const f of findings)
|
|
292
301
|
findingsByCategory[f.category].push(f);
|
|
@@ -334,7 +343,7 @@ function buildReport(analysis, options) {
|
|
|
334
343
|
categoryScores,
|
|
335
344
|
profile: productProfile,
|
|
336
345
|
maturity: maturityLevel,
|
|
337
|
-
observedScore,
|
|
346
|
+
observedScore: reportedObservedScore,
|
|
338
347
|
overallScore,
|
|
339
348
|
inconclusive,
|
|
340
349
|
inconclusiveReasons,
|
|
@@ -365,7 +374,7 @@ function buildReport(analysis, options) {
|
|
|
365
374
|
return {
|
|
366
375
|
projectPath: analysis.projectPath,
|
|
367
376
|
generatedAt: new Date().toISOString(),
|
|
368
|
-
observedScore,
|
|
377
|
+
observedScore: reportedObservedScore,
|
|
369
378
|
expectedCapabilityScore: expectationScore,
|
|
370
379
|
overallScore,
|
|
371
380
|
maturityLevel,
|
|
@@ -2,7 +2,14 @@ import type { Category, Finding } from './types';
|
|
|
2
2
|
export interface CategoryScore {
|
|
3
3
|
category: Category;
|
|
4
4
|
/** 0-100. 100 means nothing actionable was found in this category. */
|
|
5
|
-
|
|
5
|
+
/**
|
|
6
|
+
* `null` where nothing in the analysis touched this category.
|
|
7
|
+
*
|
|
8
|
+
* `notAssessed` has been beside it for a while and the number went on saying 100.
|
|
9
|
+
* On a report with no overall score at all, twelve categories still read 100 of 100
|
|
10
|
+
* — a repository the analyzer could not read, presented as perfect in twelve areas.
|
|
11
|
+
*/
|
|
12
|
+
score: number | null;
|
|
6
13
|
findingCount: number;
|
|
7
14
|
/** Checks that ran and found what they were looking for. */
|
|
8
15
|
verifiedCount: number;
|
|
@@ -103,9 +103,10 @@ function buildCategoryScores(findings) {
|
|
|
103
103
|
*/
|
|
104
104
|
const worstOpen = actionable.reduce((worst, finding) => (SEVERITY_WEIGHT[finding.severity] > SEVERITY_WEIGHT[worst] ? finding.severity : worst), 'info');
|
|
105
105
|
const ceiling = SEVERITY_CEILING[worstOpen];
|
|
106
|
+
const notAssessed = categoryFindings.every((finding) => finding.status === 'unknown');
|
|
106
107
|
return {
|
|
107
108
|
category,
|
|
108
|
-
score: Math.max(0, Math.min(100, proportional, ceiling)),
|
|
109
|
+
score: notAssessed ? null : Math.max(0, Math.min(100, proportional, ceiling)),
|
|
109
110
|
findingCount: actionable.length,
|
|
110
111
|
/** Checks in this category that ran and found what they were looking for. */
|
|
111
112
|
verifiedCount: assessed.filter((finding) => finding.status === 'passed').length,
|
|
@@ -122,7 +123,7 @@ function buildCategoryScores(findings) {
|
|
|
122
123
|
* A Django school platform with no payments anywhere was reported as having
|
|
123
124
|
* "no issues found in taking payments", as a strength.
|
|
124
125
|
*/
|
|
125
|
-
notAssessed
|
|
126
|
+
notAssessed,
|
|
126
127
|
};
|
|
127
128
|
});
|
|
128
129
|
}
|
|
@@ -130,6 +131,6 @@ function buildCategoryScores(findings) {
|
|
|
130
131
|
function weakestCategories(scores, limit = 3) {
|
|
131
132
|
return [...scores]
|
|
132
133
|
.filter((entry) => !entry.notAssessed && entry.findingCount > 0)
|
|
133
|
-
.sort((left, right) => left.score - right.score || right.criticalCount - left.criticalCount)
|
|
134
|
+
.sort((left, right) => (left.score ?? 100) - (right.score ?? 100) || right.criticalCount - left.criticalCount)
|
|
134
135
|
.slice(0, limit);
|
|
135
136
|
}
|
|
@@ -20,7 +20,7 @@ export declare function buildExecutiveSummary(args: {
|
|
|
20
20
|
categoryScores: CategoryScore[];
|
|
21
21
|
profile: ProductExpectationResult | undefined;
|
|
22
22
|
maturity: MaturityLevel;
|
|
23
|
-
observedScore: number;
|
|
23
|
+
observedScore: number | null;
|
|
24
24
|
overallScore: number | null;
|
|
25
25
|
inconclusive: boolean;
|
|
26
26
|
/** Why, when the report could not form a reading. Named in the verdict. */
|
|
@@ -158,7 +158,7 @@ function buildStrengths(categoryScores, findings) {
|
|
|
158
158
|
const passedCategories = new Set(passed.map((finding) => finding.category));
|
|
159
159
|
const strongCategories = categoryScores
|
|
160
160
|
.filter((entry) => !entry.notAssessed
|
|
161
|
-
&& entry.score >= 80
|
|
161
|
+
&& (entry.score ?? 0) >= 80
|
|
162
162
|
&& entry.findingCount === 0
|
|
163
163
|
&& passedCategories.has(entry.category));
|
|
164
164
|
/**
|
|
@@ -40,8 +40,10 @@ function profileSummary(report) {
|
|
|
40
40
|
`- Selected profile: ${profile.selectedProfile}`,
|
|
41
41
|
`- Profile: ${profile.profileTitle}`,
|
|
42
42
|
`- Description: ${profile.profileDescription}`,
|
|
43
|
-
`- Observed score: ${report.observedScore}/100`,
|
|
44
|
-
|
|
43
|
+
report.observedScore === null ? '- Observed score: not scored' : `- Observed score: ${report.observedScore}/100`,
|
|
44
|
+
report.expectedCapabilityScore === undefined
|
|
45
|
+
? '- Expected capability score: not measured'
|
|
46
|
+
: `- Expected capability score: ${report.expectedCapabilityScore}/100`,
|
|
45
47
|
report.overallScore === null ? '- Final score: not scored' : `- Final score: ${report.overallScore}/100`,
|
|
46
48
|
'- Capability summary:',
|
|
47
49
|
`- Required: ${requiredPresent} present / ${requiredMissing} missing / ${requiredPartial} partial`,
|
|
@@ -134,7 +136,7 @@ function categoryScoreSection(report) {
|
|
|
134
136
|
*/
|
|
135
137
|
'| Area | Score | Verified | Open findings | Critical |',
|
|
136
138
|
'| --- | --- | --- | --- | --- |',
|
|
137
|
-
...assessed.map((entry) => `| ${entry.category} | ${entry.score}/100 | ${entry.verifiedCount} of ${entry.assessedCount} | ${entry.findingCount} | ${entry.criticalCount} |`),
|
|
139
|
+
...assessed.map((entry) => `| ${entry.category} | ${entry.score === null ? 'not scored' : `${entry.score}/100`} | ${entry.verifiedCount} of ${entry.assessedCount} | ${entry.findingCount} | ${entry.criticalCount} |`),
|
|
138
140
|
'',
|
|
139
141
|
];
|
|
140
142
|
}
|
|
@@ -222,7 +224,7 @@ function renderMarkdown(report) {
|
|
|
222
224
|
'## Score',
|
|
223
225
|
'',
|
|
224
226
|
report.overallScore === null ? '- Overall score: not scored' : `- Overall score: ${report.overallScore}/100`,
|
|
225
|
-
`- Observed score: ${report.observedScore}/100`,
|
|
227
|
+
report.observedScore === null ? '- Observed score: not scored' : `- Observed score: ${report.observedScore}/100`,
|
|
226
228
|
...(report.expectedCapabilityScore !== undefined ? [`- Expected capability score: ${report.expectedCapabilityScore}/100`] : []),
|
|
227
229
|
`- Maturity level: ${report.maturityLevel}${report.inconclusive ? ' (inconclusive)' : ''}`,
|
|
228
230
|
/**
|
|
@@ -238,7 +240,7 @@ function renderMarkdown(report) {
|
|
|
238
240
|
: []),
|
|
239
241
|
...(report.inconclusive
|
|
240
242
|
? [
|
|
241
|
-
'- Assessment: INCONCLUSIVE —
|
|
243
|
+
'- Assessment: INCONCLUSIVE — this report has no score, for these reasons:',
|
|
242
244
|
...report.inconclusiveReasons.map((r) => ` - ${r}`),
|
|
243
245
|
]
|
|
244
246
|
: []),
|
package/dist/report/types.d.ts
CHANGED
|
@@ -88,7 +88,8 @@ export interface ReportDiagnostics {
|
|
|
88
88
|
export interface ProductionReadinessReport {
|
|
89
89
|
projectPath: string;
|
|
90
90
|
generatedAt: string;
|
|
91
|
-
|
|
91
|
+
/** `null` where the reading is inconclusive, for the same reason `overallScore` is. */
|
|
92
|
+
observedScore: number | null;
|
|
92
93
|
expectedCapabilityScore?: number;
|
|
93
94
|
/**
|
|
94
95
|
* `null` where the reading is inconclusive, which is the same rule this product
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@produtype/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.46.0",
|
|
4
4
|
"description": "Deterministic CLI and library that analyzes a web application repository and reports how far it is from production-ready for the kind of product it is meant to be.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"bin": {
|