@mergesignal/shared 0.4.1 → 0.4.3
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/deriveScanNarrative.d.ts.map +1 -1
- package/dist/deriveScanNarrative.js +41 -8
- package/dist/deriveScanNarrative.test.js +4 -0
- package/dist/engineWireRegression.test.js +2 -2
- package/dist/fixtures/repoIntelligenceFixtures.d.ts.map +1 -1
- package/dist/fixtures/repoIntelligenceFixtures.js +8 -1
- package/dist/narrativeParity.test.js +3 -4
- package/dist/presentation/compose/narrativeChannels.d.ts +17 -0
- package/dist/presentation/compose/narrativeChannels.d.ts.map +1 -0
- package/dist/presentation/compose/narrativeChannels.js +75 -0
- package/dist/presentation/compose/narrativeChannels.test.d.ts +2 -0
- package/dist/presentation/compose/narrativeChannels.test.d.ts.map +1 -0
- package/dist/presentation/compose/narrativeChannels.test.js +42 -0
- package/dist/presentation/compose/narrativeCompose.d.ts +1 -0
- package/dist/presentation/compose/narrativeCompose.d.ts.map +1 -1
- package/dist/presentation/compose/narrativeCompose.js +17 -102
- package/dist/presentation/dto/dashboardCardPresentation.d.ts +29 -0
- package/dist/presentation/dto/dashboardCardPresentation.d.ts.map +1 -0
- package/dist/presentation/dto/scanDetailsPresentation.d.ts +2 -0
- package/dist/presentation/dto/scanDetailsPresentation.d.ts.map +1 -1
- package/dist/presentation/dto/types.d.ts +0 -9
- package/dist/presentation/dto/types.d.ts.map +1 -1
- package/dist/presentation/dto/types.js +1 -22
- package/dist/presentation/fixtures/dashboardCardGolden.test.d.ts +2 -0
- package/dist/presentation/fixtures/dashboardCardGolden.test.d.ts.map +1 -0
- package/dist/presentation/fixtures/dashboardCardGolden.test.js +31 -0
- package/dist/presentation/fixtures/presentationPersonaFixtures.d.ts +11 -0
- package/dist/presentation/fixtures/presentationPersonaFixtures.d.ts.map +1 -0
- package/dist/presentation/fixtures/presentationPersonaFixtures.js +197 -0
- package/dist/presentation/index.d.ts +8 -5
- package/dist/presentation/index.d.ts.map +1 -1
- package/dist/presentation/index.js +3 -3
- package/dist/presentation/intent/applyPostureVocabularyGuard.d.ts +5 -0
- package/dist/presentation/intent/applyPostureVocabularyGuard.d.ts.map +1 -0
- package/dist/presentation/intent/applyPostureVocabularyGuard.js +44 -0
- package/dist/presentation/intent/derivePresentationInterpretation.d.ts +4 -0
- package/dist/presentation/intent/derivePresentationInterpretation.d.ts.map +1 -0
- package/dist/presentation/intent/derivePresentationInterpretation.js +202 -0
- package/dist/presentation/intent/formatPresentationCopy.d.ts +8 -0
- package/dist/presentation/intent/formatPresentationCopy.d.ts.map +1 -0
- package/dist/presentation/intent/formatPresentationCopy.js +140 -0
- package/dist/presentation/intent/packageDisplayName.d.ts +4 -0
- package/dist/presentation/intent/packageDisplayName.d.ts.map +1 -0
- package/dist/presentation/intent/packageDisplayName.js +17 -0
- package/dist/presentation/intent/postureVocabularyGuard.test.d.ts +2 -0
- package/dist/presentation/intent/postureVocabularyGuard.test.d.ts.map +1 -0
- package/dist/presentation/intent/postureVocabularyGuard.test.js +23 -0
- package/dist/presentation/intent/presentationIntent.d.ts +14 -0
- package/dist/presentation/intent/presentationIntent.d.ts.map +1 -0
- package/dist/presentation/intent/presentationIntent.js +1 -0
- package/dist/presentation/intent/presentationIntent.test.d.ts +2 -0
- package/dist/presentation/intent/presentationIntent.test.d.ts.map +1 -0
- package/dist/presentation/intent/presentationIntent.test.js +29 -0
- package/dist/presentation/intent/verificationFocusLabels.d.ts +2 -0
- package/dist/presentation/intent/verificationFocusLabels.d.ts.map +1 -0
- package/dist/presentation/intent/verificationFocusLabels.js +45 -0
- package/dist/presentation/orchestration/buildScanCardPresentation.d.ts +2 -2
- package/dist/presentation/orchestration/buildScanCardPresentation.d.ts.map +1 -1
- package/dist/presentation/orchestration/buildScanCardPresentation.js +5 -6
- package/dist/presentation/presentation.test.js +18 -17
- package/dist/presentation/presentationPersonas.test.d.ts +2 -0
- package/dist/presentation/presentationPersonas.test.d.ts.map +1 -0
- package/dist/presentation/presentationPersonas.test.js +167 -0
- package/dist/presentation/presenters/presentCliScanSummary.d.ts.map +1 -1
- package/dist/presentation/presenters/presentCliScanSummary.js +2 -2
- package/dist/presentation/presenters/presentDashboardCard.d.ts +5 -0
- package/dist/presentation/presenters/presentDashboardCard.d.ts.map +1 -0
- package/dist/presentation/presenters/presentDashboardCard.js +117 -0
- package/dist/presentation/presenters/presentGitHubCheckRun.d.ts.map +1 -1
- package/dist/presentation/presenters/presentGitHubCheckRun.js +2 -2
- package/dist/presentation/presenters/presentScanDetails.d.ts.map +1 -1
- package/dist/presentation/presenters/presentScanDetails.js +13 -4
- package/dist/presentation/profile/derivePresentationProfile.d.ts.map +1 -1
- package/dist/presentation/profile/derivePresentationProfile.js +18 -27
- package/dist/presentation/profile/presentationProfile.d.ts +2 -0
- package/dist/presentation/profile/presentationProfile.d.ts.map +1 -1
- package/dist/repoIntelligenceSchema.d.ts +1 -1
- package/dist/scanCardSummary.d.ts +3 -4
- package/dist/scanCardSummary.d.ts.map +1 -1
- package/dist/scanCardSummary.test.js +16 -16
- package/dist/scanNarrativeFacts.d.ts +31 -7
- package/dist/scanNarrativeFacts.d.ts.map +1 -1
- package/dist/scanNarrativeFacts.js +1 -0
- package/dist/scanSurfaceCopy.d.ts +8 -0
- package/dist/scanSurfaceCopy.d.ts.map +1 -1
- package/dist/scanSurfaceCopy.js +8 -0
- package/package.json +1 -1
- package/dist/presentation/dto/scanCardPresentation.d.ts +0 -25
- package/dist/presentation/dto/scanCardPresentation.d.ts.map +0 -1
- package/dist/presentation/presenters/presentPipelineScanCard.d.ts +0 -3
- package/dist/presentation/presenters/presentPipelineScanCard.d.ts.map +0 -1
- package/dist/presentation/presenters/presentPipelineScanCard.js +0 -23
- package/dist/presentation/presenters/presentScanCard.d.ts +0 -7
- package/dist/presentation/presenters/presentScanCard.d.ts.map +0 -1
- package/dist/presentation/presenters/presentScanCard.js +0 -43
- /package/dist/presentation/dto/{scanCardPresentation.js → dashboardCardPresentation.js} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deriveScanNarrative.d.ts","sourceRoot":"","sources":["../src/deriveScanNarrative.ts"],"names":[],"mappings":"AAOA,OAAO,
|
|
1
|
+
{"version":3,"file":"deriveScanNarrative.d.ts","sourceRoot":"","sources":["../src/deriveScanNarrative.ts"],"names":[],"mappings":"AAOA,OAAO,EAeL,KAAK,kBAAkB,EACxB,MAAM,yBAAyB,CAAC;AAGjC,OAAO,KAAK,EAKV,UAAU,EACX,MAAM,YAAY,CAAC;AA+dpB;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,UAAU,GAAG,IAAI,GAAG,SAAS,GACpC,kBAAkB,CAwGpB"}
|
|
@@ -125,6 +125,41 @@ function mergeGraphHotspots(codeHotspots, graphHotspots) {
|
|
|
125
125
|
}
|
|
126
126
|
return [...byName.values()].slice(0, NARRATIVE_FACT_MAX_HOTSPOTS);
|
|
127
127
|
}
|
|
128
|
+
function projectPackageSemanticsFromWire(pkgIntel) {
|
|
129
|
+
if (!pkgIntel)
|
|
130
|
+
return null;
|
|
131
|
+
return {
|
|
132
|
+
dependencyClass: pkgIntel.dependencyClass ?? null,
|
|
133
|
+
packageRole: pkgIntel.packageRole ?? null,
|
|
134
|
+
runtimeImpact: pkgIntel.runtimeImpact ?? null,
|
|
135
|
+
expectedImpact: pkgIntel.expectedImpact ?? null,
|
|
136
|
+
suppressRuntimeNarrative: pkgIntel.suppressRuntimeNarrative === true,
|
|
137
|
+
evidenceStrength: pkgIntel.evidenceStrength ?? null,
|
|
138
|
+
verificationFocus: pkgIntel.verificationFocus ?? [],
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
function projectChangedPackageSemantics(ri, changed) {
|
|
142
|
+
return changed.all.map((packageName) => {
|
|
143
|
+
const pkgIntel = ri.packages?.[packageName];
|
|
144
|
+
const usage = pkgIntel?.usage;
|
|
145
|
+
const paths = usage?.paths ?? [];
|
|
146
|
+
const criticalPaths = usage?.criticalPaths ?? [];
|
|
147
|
+
const areas = usage?.areas ?? [];
|
|
148
|
+
const summary = projectPackageSemanticsFromWire(pkgIntel);
|
|
149
|
+
return {
|
|
150
|
+
packageName,
|
|
151
|
+
dependencyClass: summary?.dependencyClass ?? null,
|
|
152
|
+
packageRole: summary?.packageRole ?? null,
|
|
153
|
+
runtimeImpact: summary?.runtimeImpact ?? null,
|
|
154
|
+
expectedImpact: summary?.expectedImpact ?? null,
|
|
155
|
+
suppressRuntimeNarrative: summary?.suppressRuntimeNarrative ?? false,
|
|
156
|
+
evidenceStrength: summary?.evidenceStrength ?? null,
|
|
157
|
+
verificationFocus: summary?.verificationFocus ?? [],
|
|
158
|
+
usagePathCount: paths.length + criticalPaths.length,
|
|
159
|
+
usageAreaCount: areas.length,
|
|
160
|
+
};
|
|
161
|
+
});
|
|
162
|
+
}
|
|
128
163
|
function extractTier1FromRepoIntelligence(ri, changed) {
|
|
129
164
|
const primary = changed.primary;
|
|
130
165
|
const pkgIntel = primary && ri.packages?.[primary] ? ri.packages[primary] : undefined;
|
|
@@ -155,14 +190,8 @@ function extractTier1FromRepoIntelligence(ri, changed) {
|
|
|
155
190
|
},
|
|
156
191
|
}
|
|
157
192
|
: null;
|
|
158
|
-
const packageSemantics = pkgIntel
|
|
159
|
-
|
|
160
|
-
runtimeImpact: pkgIntel.runtimeImpact ?? null,
|
|
161
|
-
expectedImpact: pkgIntel.expectedImpact ?? null,
|
|
162
|
-
suppressRuntimeNarrative: suppressRuntime,
|
|
163
|
-
verificationFocus: pkgIntel.verificationFocus ?? [],
|
|
164
|
-
}
|
|
165
|
-
: null;
|
|
193
|
+
const packageSemantics = projectPackageSemanticsFromWire(pkgIntel);
|
|
194
|
+
const changedPackageSemantics = projectChangedPackageSemantics(ri, changed);
|
|
166
195
|
const blastFromRi = ri.blastRadius;
|
|
167
196
|
const blastLevel = readBlastLevel(blastFromRi?.level) ??
|
|
168
197
|
(changed.all.length > 0
|
|
@@ -195,6 +224,7 @@ function extractTier1FromRepoIntelligence(ri, changed) {
|
|
|
195
224
|
runtimeSurface,
|
|
196
225
|
reachability,
|
|
197
226
|
packageSemantics,
|
|
227
|
+
changedPackageSemantics,
|
|
198
228
|
blastRadius,
|
|
199
229
|
affectedAreas: affectedAreas.slice(0, NARRATIVE_FACT_MAX_AREAS),
|
|
200
230
|
hotspots: codeHotspots.slice(0, NARRATIVE_FACT_MAX_HOTSPOTS),
|
|
@@ -206,6 +236,7 @@ function hasMeaningfulTier1(tier1) {
|
|
|
206
236
|
tier1.runtimeSurface != null ||
|
|
207
237
|
tier1.reachability != null ||
|
|
208
238
|
tier1.packageSemantics?.expectedImpact != null ||
|
|
239
|
+
tier1.changedPackageSemantics.length > 0 ||
|
|
209
240
|
tier1.blastRadius != null ||
|
|
210
241
|
tier1.affectedAreas.length > 0 ||
|
|
211
242
|
tier1.hotspots.length > 0);
|
|
@@ -287,6 +318,7 @@ function emptyTier1Blocks() {
|
|
|
287
318
|
runtimeSurface: null,
|
|
288
319
|
reachability: null,
|
|
289
320
|
packageSemantics: null,
|
|
321
|
+
changedPackageSemantics: [],
|
|
290
322
|
blastRadius: null,
|
|
291
323
|
affectedAreas: [],
|
|
292
324
|
hotspots: [],
|
|
@@ -386,6 +418,7 @@ export function deriveScanNarrative(result) {
|
|
|
386
418
|
runtimeSurface: tier1Blocks.runtimeSurface,
|
|
387
419
|
reachability: tier1Blocks.reachability,
|
|
388
420
|
packageSemantics: tier1Blocks.packageSemantics,
|
|
421
|
+
changedPackageSemantics: tier1Blocks.changedPackageSemantics,
|
|
389
422
|
blastRadius: tier1Blocks.blastRadius,
|
|
390
423
|
affectedAreas,
|
|
391
424
|
hotspots: tier1Blocks.hotspots,
|
|
@@ -126,6 +126,10 @@ describe("deriveScanNarrative", () => {
|
|
|
126
126
|
expect(facts.packageSemantics?.runtimeImpact).toBe("none");
|
|
127
127
|
expect(facts.packageSemantics?.expectedImpact).toBe("typecheck");
|
|
128
128
|
expect(facts.packageSemantics?.suppressRuntimeNarrative).toBe(true);
|
|
129
|
+
expect(facts.packageSemantics?.dependencyClass).toBe("tooling");
|
|
130
|
+
expect(facts.packageSemantics?.packageRole).toBe("typechecker");
|
|
131
|
+
expect(facts.changedPackageSemantics).toHaveLength(1);
|
|
132
|
+
expect(facts.changedPackageSemantics[0]?.packageName).toBe("typescript");
|
|
129
133
|
expect(facts.blastRadius?.level).toBe("narrow");
|
|
130
134
|
});
|
|
131
135
|
it("extracts packageUsage for all changed packages with blast factors and frameworks", () => {
|
|
@@ -37,8 +37,8 @@ describe("engine wire regression (canonical fixtures)", () => {
|
|
|
37
37
|
expect(facts.availability.repoIntelligenceParse).toBe("ok");
|
|
38
38
|
expect(facts.packageUsage[0]?.packageName).toBe("fastify");
|
|
39
39
|
const card = deriveScanCardSummary(result, "done");
|
|
40
|
-
expect(card.
|
|
41
|
-
expect(card.
|
|
40
|
+
expect(card.insights.length).toBeGreaterThan(0);
|
|
41
|
+
expect(card.verification.length).toBeGreaterThan(0);
|
|
42
42
|
});
|
|
43
43
|
it("multi-package fixture: both packages in packageUsage", () => {
|
|
44
44
|
validateRepoIntelligenceWire(fixtureRepoIntelligenceMultiPackage);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"repoIntelligenceFixtures.d.ts","sourceRoot":"","sources":["../../src/fixtures/repoIntelligenceFixtures.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAErE,iEAAiE;AACjE,eAAO,MAAM,8BAA8B,EAAE,
|
|
1
|
+
{"version":3,"file":"repoIntelligenceFixtures.d.ts","sourceRoot":"","sources":["../../src/fixtures/repoIntelligenceFixtures.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAErE,iEAAiE;AACjE,eAAO,MAAM,8BAA8B,EAAE,gBAgD5C,CAAC;AAEF,sDAAsD;AACtD,eAAO,MAAM,iCAAiC,EAAE,gBAqB/C,CAAC;AAEF,wDAAwD;AACxD,eAAO,MAAM,mCAAmC,EAAE,gBAgDjD,CAAC"}
|
|
@@ -10,6 +10,13 @@ export const fixtureRepoIntelligenceFastify = {
|
|
|
10
10
|
suppressRuntimeNarrative: false,
|
|
11
11
|
dependencyClass: "runtime",
|
|
12
12
|
packageRole: "http_framework",
|
|
13
|
+
verificationFocus: [
|
|
14
|
+
"routes",
|
|
15
|
+
"middleware",
|
|
16
|
+
"auth_flow",
|
|
17
|
+
"serialization",
|
|
18
|
+
"error_handling",
|
|
19
|
+
],
|
|
13
20
|
usage: {
|
|
14
21
|
packageName: "fastify",
|
|
15
22
|
files: [
|
|
@@ -29,7 +36,7 @@ export const fixtureRepoIntelligenceFastify = {
|
|
|
29
36
|
changedPackageCount: 1,
|
|
30
37
|
},
|
|
31
38
|
applicationAreas: [
|
|
32
|
-
{ id: "api", label: "API
|
|
39
|
+
{ id: "api", label: "API request handling" },
|
|
33
40
|
{ id: "auth", label: "Auth middleware" },
|
|
34
41
|
],
|
|
35
42
|
hotspots: [
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest";
|
|
2
2
|
import { buildScanPresentationBundle } from "./presentation/orchestration/buildScanPresentationBundle.js";
|
|
3
|
-
import {
|
|
3
|
+
import { presentDashboardCard } from "./presentation/presenters/presentDashboardCard.js";
|
|
4
4
|
import { presentScanDetails } from "./presentation/presenters/presentScanDetails.js";
|
|
5
5
|
import { presentGitHubPrComment } from "./presentation/presenters/presentGitHubPrComment.js";
|
|
6
6
|
import { renderGitHubCheckRunMarkdown } from "./presentation/render/renderGitHubCheckRunMarkdown.js";
|
|
@@ -45,7 +45,7 @@ describe("narrative parity across consumers", () => {
|
|
|
45
45
|
result,
|
|
46
46
|
pipelineStatus: "done",
|
|
47
47
|
});
|
|
48
|
-
const card =
|
|
48
|
+
const card = presentDashboardCard(bundle);
|
|
49
49
|
const detail = presentScanDetails(bundle, {
|
|
50
50
|
scanId: SCAN_ID,
|
|
51
51
|
});
|
|
@@ -55,9 +55,8 @@ describe("narrative parity across consumers", () => {
|
|
|
55
55
|
baseline: false,
|
|
56
56
|
}));
|
|
57
57
|
const prComment = presentGitHubPrComment(bundle);
|
|
58
|
-
expect(card.primaryPackage).toBe("fastify");
|
|
59
58
|
expect(detail.narrative.primaryPackage).toBe("fastify");
|
|
60
|
-
expect(card.
|
|
59
|
+
expect(card.evidenceChips?.some((e) => e.label === "Reachability")).toBe(true);
|
|
61
60
|
const asciiOnly = /^[\x00-\x7F]*$/;
|
|
62
61
|
expect(asciiOnly.test(checkRun)).toBe(true);
|
|
63
62
|
expect(checkRun.toLowerCase()).toContain("fastify");
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { PresentationEvidenceRow } from "../dto/types.js";
|
|
2
|
+
import type { ScanPresentationBundle } from "../orchestration/scanPresentationBundle.js";
|
|
3
|
+
type NarrativeChannels = {
|
|
4
|
+
headline: string;
|
|
5
|
+
limitedContextMessage?: string;
|
|
6
|
+
reachLabel?: string;
|
|
7
|
+
insights: string[];
|
|
8
|
+
scopeAreas: string[];
|
|
9
|
+
evidence: PresentationEvidenceRow[];
|
|
10
|
+
verification: string[];
|
|
11
|
+
};
|
|
12
|
+
/** Canonical non-redundant narrative channels — single dedupe point for all surfaces. */
|
|
13
|
+
export declare function buildNarrativeChannels(bundle: ScanPresentationBundle): NarrativeChannels;
|
|
14
|
+
/** Merge distinct channels into one bullet list for single-column surfaces (GitHub check, CLI). */
|
|
15
|
+
export declare function projectCompactKeyPoints(channels: NarrativeChannels, max: number): string[];
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=narrativeChannels.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"narrativeChannels.d.ts","sourceRoot":"","sources":["../../../src/presentation/compose/narrativeChannels.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAM/D,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,4CAA4C,CAAC;AAOzF,KAAK,iBAAiB,GAAG;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,QAAQ,EAAE,uBAAuB,EAAE,CAAC;IACpC,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB,CAAC;AAgDF,yFAAyF;AACzF,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,sBAAsB,GAC7B,iBAAiB,CA8BnB;AAED,mGAAmG;AACnG,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,iBAAiB,EAC3B,GAAG,EAAE,MAAM,GACV,MAAM,EAAE,CAyBV"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { deriveDetailReachChip } from "../../formatCardExposureDisplay.js";
|
|
2
|
+
import { scanSurfaceCopy } from "../../scanSurfaceCopy.js";
|
|
3
|
+
import { formatPresentationHeadline, formatPresentationInsightLines, formatPresentationVerification, } from "../intent/formatPresentationCopy.js";
|
|
4
|
+
import { formatCardAreaLabels } from "../../formatCardAreaLabels.js";
|
|
5
|
+
import { labelBlastRadiusLevel, labelReachabilityKind, } from "../../narrativePresentation.js";
|
|
6
|
+
function composeScopeAreaLabels(bundle, max) {
|
|
7
|
+
const labels = [];
|
|
8
|
+
for (const area of bundle.facts.affectedAreas) {
|
|
9
|
+
const formatted = formatCardAreaLabels([area.label], 1);
|
|
10
|
+
if (formatted[0])
|
|
11
|
+
labels.push(formatted[0]);
|
|
12
|
+
if (labels.length >= max)
|
|
13
|
+
break;
|
|
14
|
+
}
|
|
15
|
+
return labels;
|
|
16
|
+
}
|
|
17
|
+
function isTautologicalEvidenceRow(row) {
|
|
18
|
+
const label = row.label.trim().toLowerCase();
|
|
19
|
+
const value = row.value.trim().toLowerCase();
|
|
20
|
+
if (label === "runtime" && value === "runtime")
|
|
21
|
+
return true;
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
function composeChannelEvidence(bundle, max) {
|
|
25
|
+
const { facts, profile } = bundle;
|
|
26
|
+
const rows = [];
|
|
27
|
+
if (!profile.interpretation.suppressRuntimeNarrative) {
|
|
28
|
+
const reach = labelReachabilityKind(facts);
|
|
29
|
+
if (reach)
|
|
30
|
+
rows.push({ label: "Reachability", value: reach });
|
|
31
|
+
}
|
|
32
|
+
const blast = labelBlastRadiusLevel(facts);
|
|
33
|
+
if (blast)
|
|
34
|
+
rows.push({ label: "Blast radius", value: blast });
|
|
35
|
+
return rows.filter((row) => !isTautologicalEvidenceRow(row)).slice(0, max);
|
|
36
|
+
}
|
|
37
|
+
function shouldShowReachLabel(bundle) {
|
|
38
|
+
const { profile } = bundle;
|
|
39
|
+
if (profile.status === "safe" && profile.density === "minimal") {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
return profile.status !== "safe" || profile.density !== "minimal";
|
|
43
|
+
}
|
|
44
|
+
/** Canonical non-redundant narrative channels — single dedupe point for all surfaces. */
|
|
45
|
+
export function buildNarrativeChannels(bundle) {
|
|
46
|
+
const { facts, profile, result } = bundle;
|
|
47
|
+
const riskIndex = facts.riskIndex ?? result.totalScore ?? null;
|
|
48
|
+
const reachLabel = shouldShowReachLabel(bundle) && riskIndex != null
|
|
49
|
+
? (deriveDetailReachChip(riskIndex) ?? undefined)
|
|
50
|
+
: undefined;
|
|
51
|
+
return {
|
|
52
|
+
headline: formatPresentationHeadline(profile.interpretation, facts, profile.status),
|
|
53
|
+
limitedContextMessage: profile.degradedMessage ?? undefined,
|
|
54
|
+
reachLabel,
|
|
55
|
+
insights: formatPresentationInsightLines(profile.interpretation, facts, profile.status),
|
|
56
|
+
scopeAreas: composeScopeAreaLabels(bundle, 4),
|
|
57
|
+
evidence: composeChannelEvidence(bundle, 4),
|
|
58
|
+
verification: formatPresentationVerification(profile.interpretation, profile.status, 6),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
/** Merge distinct channels into one bullet list for single-column surfaces (GitHub check, CLI). */
|
|
62
|
+
export function projectCompactKeyPoints(channels, max) {
|
|
63
|
+
const bullets = [...channels.insights];
|
|
64
|
+
if (channels.scopeAreas.length > 0) {
|
|
65
|
+
bullets.push(scanSurfaceCopy.presentation.affectedAreasKeyPoint.replace("{areas}", channels.scopeAreas.slice(0, 2).join(", ")));
|
|
66
|
+
}
|
|
67
|
+
const blastRow = channels.evidence.find((r) => r.label === "Blast radius");
|
|
68
|
+
if (blastRow?.value === "Wide") {
|
|
69
|
+
bullets.push(scanSurfaceCopy.presentation.blastRadiusWideKeyPoint);
|
|
70
|
+
}
|
|
71
|
+
else if (blastRow?.value === "Moderate") {
|
|
72
|
+
bullets.push(scanSurfaceCopy.presentation.blastRadiusLevelKeyPoint.replace("{level}", "Moderate"));
|
|
73
|
+
}
|
|
74
|
+
return bullets.slice(0, max);
|
|
75
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"narrativeChannels.test.d.ts","sourceRoot":"","sources":["../../../src/presentation/compose/narrativeChannels.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { buildScanPresentationBundle } from "../orchestration/buildScanPresentationBundle.js";
|
|
3
|
+
import { scanResultFastifyRuntime } from "../fixtures/scanResultFixtures.js";
|
|
4
|
+
import { buildNarrativeChannels, projectCompactKeyPoints, } from "./narrativeChannels.js";
|
|
5
|
+
import { presentDashboardCard } from "../presenters/presentDashboardCard.js";
|
|
6
|
+
describe("buildNarrativeChannels", () => {
|
|
7
|
+
it("fastify: areas in scopeAreas only, blast in evidence only, not in insights", () => {
|
|
8
|
+
const bundle = buildScanPresentationBundle({
|
|
9
|
+
result: scanResultFastifyRuntime,
|
|
10
|
+
pipelineStatus: "done",
|
|
11
|
+
});
|
|
12
|
+
const channels = buildNarrativeChannels(bundle);
|
|
13
|
+
const insightText = channels.insights.join(" ");
|
|
14
|
+
expect(insightText).not.toMatch(/Affected areas:/i);
|
|
15
|
+
expect(insightText).not.toMatch(/Blast radius is/i);
|
|
16
|
+
expect(channels.scopeAreas.length).toBeGreaterThan(0);
|
|
17
|
+
expect(channels.evidence.some((r) => r.label === "Blast radius")).toBe(true);
|
|
18
|
+
expect(channels.evidence.some((r) => r.label === "Runtime")).toBe(false);
|
|
19
|
+
});
|
|
20
|
+
it("projectCompactKeyPoints merges channels for single-column surfaces", () => {
|
|
21
|
+
const bundle = buildScanPresentationBundle({
|
|
22
|
+
result: scanResultFastifyRuntime,
|
|
23
|
+
pipelineStatus: "done",
|
|
24
|
+
});
|
|
25
|
+
const channels = buildNarrativeChannels(bundle);
|
|
26
|
+
const compact = projectCompactKeyPoints(channels, 5);
|
|
27
|
+
expect(compact.some((p) => /Affected areas:/i.test(p))).toBe(true);
|
|
28
|
+
expect(compact.some((p) => /Blast radius is/i.test(p))).toBe(true);
|
|
29
|
+
});
|
|
30
|
+
it("presentDashboardCard does not duplicate areas across insights and scopeAreas", () => {
|
|
31
|
+
const bundle = buildScanPresentationBundle({
|
|
32
|
+
result: scanResultFastifyRuntime,
|
|
33
|
+
pipelineStatus: "done",
|
|
34
|
+
});
|
|
35
|
+
const card = presentDashboardCard(bundle);
|
|
36
|
+
const insightText = card.insights.join(" ");
|
|
37
|
+
expect(insightText).not.toMatch(/Affected areas:/i);
|
|
38
|
+
expect(card.scopeAreas?.length).toBeGreaterThan(0);
|
|
39
|
+
expect(card.verdict?.scopeLabel).toBeTruthy();
|
|
40
|
+
expect(card.verification.every((v) => v.startsWith("Verify"))).toBe(true);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ScanPresentationBundle } from "../orchestration/scanPresentationBundle.js";
|
|
2
|
+
export { buildNarrativeChannels, projectCompactKeyPoints, } from "./narrativeChannels.js";
|
|
2
3
|
export declare function composeHeadline(bundle: ScanPresentationBundle): string;
|
|
3
4
|
export declare function composeSubheadline(bundle: ScanPresentationBundle): string | undefined;
|
|
4
5
|
export declare function composeKeyPoints(bundle: ScanPresentationBundle, max: number): string[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"narrativeCompose.d.ts","sourceRoot":"","sources":["../../../src/presentation/compose/narrativeCompose.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,4CAA4C,CAAC;
|
|
1
|
+
{"version":3,"file":"narrativeCompose.d.ts","sourceRoot":"","sources":["../../../src/presentation/compose/narrativeCompose.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,4CAA4C,CAAC;AAMzF,OAAO,EACL,sBAAsB,EACtB,uBAAuB,GACxB,MAAM,wBAAwB,CAAC;AAShC,wBAAgB,eAAe,CAAC,MAAM,EAAE,sBAAsB,GAAG,MAAM,CAOtE;AAED,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,sBAAsB,GAC7B,MAAM,GAAG,SAAS,CAIpB;AAED,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,sBAAsB,EAC9B,GAAG,EAAE,MAAM,GACV,MAAM,EAAE,CAEV;AAED,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,sBAAsB,EAC9B,GAAG,EAAE,MAAM,GACV,MAAM,EAAE,CAOV;AAED,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,sBAAsB,EAC9B,GAAG,EAAE,MAAM,GACV,MAAM,EAAE,CAQV;AAED,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,sBAAsB,EAC9B,GAAG,EAAE,MAAM,GACV,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAmBzC;AAED,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,sBAAsB,GAC7B,MAAM,EAAE,GAAG,SAAS,CAwBtB;AAED,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,sBAAsB,GAAG;IAC1E,QAAQ,EAAE,iBAAiB,GAAG,SAAS,CAAC;IACxC,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,CAKA"}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { formatCardAreaLabels } from "../../formatCardAreaLabels.js";
|
|
2
|
-
import {
|
|
2
|
+
import { formatChangedPackagesShort, labelBlastRadiusLevel, labelReachabilityKind, labelRuntimeSurface, } from "../../narrativePresentation.js";
|
|
3
3
|
import { scanSurfaceCopy } from "../../scanSurfaceCopy.js";
|
|
4
|
+
import { formatPresentationHeadline, formatPresentationVerification, } from "../intent/formatPresentationCopy.js";
|
|
5
|
+
import { buildNarrativeChannels, projectCompactKeyPoints, } from "./narrativeChannels.js";
|
|
6
|
+
export { buildNarrativeChannels, projectCompactKeyPoints, } from "./narrativeChannels.js";
|
|
4
7
|
function topAreaLabel(bundle) {
|
|
5
8
|
const area = bundle.facts.affectedAreas[0];
|
|
6
9
|
if (!area)
|
|
@@ -10,29 +13,7 @@ function topAreaLabel(bundle) {
|
|
|
10
13
|
}
|
|
11
14
|
export function composeHeadline(bundle) {
|
|
12
15
|
const { facts, profile } = bundle;
|
|
13
|
-
|
|
14
|
-
if (profile.priority === "limited") {
|
|
15
|
-
return scanSurfaceCopy.presentation.limitedContextHeadline;
|
|
16
|
-
}
|
|
17
|
-
if (!primary) {
|
|
18
|
-
return scanSurfaceCopy.presentation.noChangedPackagesHeadline;
|
|
19
|
-
}
|
|
20
|
-
const runtime = facts.runtimeSurface?.kind === "runtime";
|
|
21
|
-
const area = topAreaLabel(bundle);
|
|
22
|
-
if (facts.changedPackages.all.length > 1 && runtime) {
|
|
23
|
-
return scanSurfaceCopy.presentation.multiRuntimeHeadline;
|
|
24
|
-
}
|
|
25
|
-
if (facts.runtimeSurface?.kind === "build" ||
|
|
26
|
-
facts.reachability?.kind === "build_only") {
|
|
27
|
-
return scanSurfaceCopy.presentation.buildOnlyHeadline.replace("{package}", primary);
|
|
28
|
-
}
|
|
29
|
-
if (runtime) {
|
|
30
|
-
const surface = area ?? labelRuntimeSurface(facts) ?? "application code";
|
|
31
|
-
return scanSurfaceCopy.presentation.runtimeHeadline
|
|
32
|
-
.replace("{package}", primary)
|
|
33
|
-
.replace("{surface}", surface);
|
|
34
|
-
}
|
|
35
|
-
return scanSurfaceCopy.presentation.defaultUpgradeHeadline.replace("{package}", primary);
|
|
16
|
+
return formatPresentationHeadline(profile.interpretation, facts, profile.status);
|
|
36
17
|
}
|
|
37
18
|
export function composeSubheadline(bundle) {
|
|
38
19
|
const { profile } = bundle;
|
|
@@ -41,79 +22,11 @@ export function composeSubheadline(bundle) {
|
|
|
41
22
|
return undefined;
|
|
42
23
|
}
|
|
43
24
|
export function composeKeyPoints(bundle, max) {
|
|
44
|
-
|
|
45
|
-
const points = [];
|
|
46
|
-
if (profile.priority === "limited") {
|
|
47
|
-
points.push(scanSurfaceCopy.presentation.limitedContextKeyPoint);
|
|
48
|
-
if (facts.changedPackages.primary) {
|
|
49
|
-
points.push(scanSurfaceCopy.presentation.changedPackageKeyPoint.replace("{package}", facts.changedPackages.primary));
|
|
50
|
-
}
|
|
51
|
-
return points.slice(0, max);
|
|
52
|
-
}
|
|
53
|
-
const usage = summarizePackageUsage(facts, { maxPaths: 2 });
|
|
54
|
-
if (usage && usage.pathCount > 0) {
|
|
55
|
-
if (usage.pathCount === 1 && usage.pathSamples[0]) {
|
|
56
|
-
points.push(scanSurfaceCopy.presentation.usedInPathsSingle.replace("{path}", usage.pathSamples[0]));
|
|
57
|
-
}
|
|
58
|
-
else {
|
|
59
|
-
points.push(scanSurfaceCopy.presentation.usedInPathsMultiple.replace("{count}", String(usage.pathCount)));
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
else if (profile.density === "minimal") {
|
|
63
|
-
points.push(scanSurfaceCopy.presentation.noRuntimePathsKeyPoint);
|
|
64
|
-
}
|
|
65
|
-
const blast = summarizeBlastRadius(facts, 2);
|
|
66
|
-
if (blast.levelLabel) {
|
|
67
|
-
if (blast.levelLabel.toLowerCase().includes("wide")) {
|
|
68
|
-
points.push(scanSurfaceCopy.presentation.blastRadiusWideKeyPoint);
|
|
69
|
-
}
|
|
70
|
-
else if (profile.density === "rich") {
|
|
71
|
-
points.push(scanSurfaceCopy.presentation.blastRadiusLevelKeyPoint.replace("{level}", blast.levelLabel));
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
if (facts.affectedAreas.length === 0 && profile.density === "minimal") {
|
|
75
|
-
points.push(scanSurfaceCopy.presentation.noAffectedAreasKeyPoint);
|
|
76
|
-
}
|
|
77
|
-
else if (facts.affectedAreas.length > 0) {
|
|
78
|
-
const labels = facts.affectedAreas
|
|
79
|
-
.slice(0, 2)
|
|
80
|
-
.map((a) => a.label)
|
|
81
|
-
.join(", ");
|
|
82
|
-
points.push(scanSurfaceCopy.presentation.affectedAreasKeyPoint.replace("{areas}", labels));
|
|
83
|
-
}
|
|
84
|
-
const guidance = selectReviewerGuidance(facts, { scope: "changed", max: 2 });
|
|
85
|
-
for (const g of guidance) {
|
|
86
|
-
const msg = g.message.trim();
|
|
87
|
-
if (msg && !points.includes(msg))
|
|
88
|
-
points.push(msg);
|
|
89
|
-
if (points.length >= max)
|
|
90
|
-
break;
|
|
91
|
-
}
|
|
92
|
-
return points.slice(0, max);
|
|
25
|
+
return projectCompactKeyPoints(buildNarrativeChannels(bundle), max);
|
|
93
26
|
}
|
|
94
27
|
export function composeVerificationActions(bundle, max) {
|
|
95
|
-
const {
|
|
96
|
-
|
|
97
|
-
const guidance = selectReviewerGuidance(facts, { scope: "changed", max: 4 });
|
|
98
|
-
for (const g of guidance) {
|
|
99
|
-
const rem = g.remediation?.trim();
|
|
100
|
-
if (rem)
|
|
101
|
-
actions.push(rem);
|
|
102
|
-
}
|
|
103
|
-
const prompt = composeVerificationPrompt(facts);
|
|
104
|
-
if (prompt && !actions.includes(prompt))
|
|
105
|
-
actions.push(prompt);
|
|
106
|
-
if (actions.length === 0) {
|
|
107
|
-
if (profile.density === "minimal" ||
|
|
108
|
-
facts.runtimeSurface?.kind === "build") {
|
|
109
|
-
actions.push(scanSurfaceCopy.presentation.verifyCiTypecheck);
|
|
110
|
-
}
|
|
111
|
-
else if (profile.density === "rich") {
|
|
112
|
-
actions.push(scanSurfaceCopy.presentation.verifyAuthFlow);
|
|
113
|
-
actions.push(scanSurfaceCopy.presentation.verifyErrorResponses);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
return actions.slice(0, max);
|
|
28
|
+
const { profile } = bundle;
|
|
29
|
+
return formatPresentationVerification(profile.interpretation, profile.status, max);
|
|
117
30
|
}
|
|
118
31
|
export function composeAffectedAreaLabels(bundle, max) {
|
|
119
32
|
const labels = [];
|
|
@@ -127,14 +40,16 @@ export function composeAffectedAreaLabels(bundle, max) {
|
|
|
127
40
|
return labels;
|
|
128
41
|
}
|
|
129
42
|
export function composeEvidenceRows(bundle, max) {
|
|
130
|
-
const { facts } = bundle;
|
|
43
|
+
const { facts, profile } = bundle;
|
|
131
44
|
const rows = [];
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
45
|
+
if (!profile.interpretation.suppressRuntimeNarrative) {
|
|
46
|
+
const runtime = labelRuntimeSurface(facts);
|
|
47
|
+
if (runtime)
|
|
48
|
+
rows.push({ label: "Runtime", value: runtime });
|
|
49
|
+
const reach = labelReachabilityKind(facts);
|
|
50
|
+
if (reach)
|
|
51
|
+
rows.push({ label: "Reachability", value: reach });
|
|
52
|
+
}
|
|
138
53
|
const blast = labelBlastRadiusLevel(facts);
|
|
139
54
|
if (blast)
|
|
140
55
|
rows.push({ label: "Blast radius", value: blast });
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { PresentationEvidenceRow, PresentationStatus } from "./types.js";
|
|
2
|
+
export type DashboardCardLayout = "quiet" | "standard" | "expanded";
|
|
3
|
+
export type DashboardCardPresentation = {
|
|
4
|
+
verdict?: {
|
|
5
|
+
posture: PresentationStatus;
|
|
6
|
+
postureLabel: string;
|
|
7
|
+
scopeLabel?: string;
|
|
8
|
+
};
|
|
9
|
+
headline: string;
|
|
10
|
+
pipeline?: {
|
|
11
|
+
status: "queued" | "running" | "failed";
|
|
12
|
+
headline: string;
|
|
13
|
+
subheadline?: string;
|
|
14
|
+
};
|
|
15
|
+
limitedContext?: {
|
|
16
|
+
message: string;
|
|
17
|
+
};
|
|
18
|
+
insights: string[];
|
|
19
|
+
scopeAreas?: string[];
|
|
20
|
+
verification: string[];
|
|
21
|
+
evidenceChips?: PresentationEvidenceRow[];
|
|
22
|
+
layout: DashboardCardLayout;
|
|
23
|
+
detailActionLabel: string;
|
|
24
|
+
sortKey: {
|
|
25
|
+
postureRank: number;
|
|
26
|
+
riskIndex: number;
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
//# sourceMappingURL=dashboardCardPresentation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dashboardCardPresentation.d.ts","sourceRoot":"","sources":["../../../src/presentation/dto/dashboardCardPresentation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAE9E,MAAM,MAAM,mBAAmB,GAAG,OAAO,GAAG,UAAU,GAAG,UAAU,CAAC;AAEpE,MAAM,MAAM,yBAAyB,GAAG;IACtC,OAAO,CAAC,EAAE;QACR,OAAO,EAAE,kBAAkB,CAAC;QAC5B,YAAY,EAAE,MAAM,CAAC;QACrB,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,CAAC;IAEF,QAAQ,EAAE,MAAM,CAAC;IAEjB,QAAQ,CAAC,EAAE;QACT,MAAM,EAAE,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAC;QACxC,QAAQ,EAAE,MAAM,CAAC;QACjB,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,CAAC;IAEF,cAAc,CAAC,EAAE;QACf,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;IAEF,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,aAAa,CAAC,EAAE,uBAAuB,EAAE,CAAC;IAE1C,MAAM,EAAE,mBAAmB,CAAC;IAC5B,iBAAiB,EAAE,MAAM,CAAC;IAE1B,OAAO,EAAE;QACP,WAAW,EAAE,MAAM,CAAC;QACpB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;CACH,CAAC"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { PresentationConfidence, PresentationDensity, PresentationEvidenceContext, PresentationStatus } from "./types.js";
|
|
2
|
+
import type { PresentationIntent } from "../intent/presentationIntent.js";
|
|
2
3
|
import type { FindingSeverity, ScoreLayer } from "../../types.js";
|
|
3
4
|
export type ScanDetailsPresentation = {
|
|
4
5
|
evidenceContext: PresentationEvidenceContext;
|
|
@@ -107,5 +108,6 @@ export type ScanDetailsPresentation = {
|
|
|
107
108
|
methodologyVersion?: string | null;
|
|
108
109
|
changedPackagesSummary?: string;
|
|
109
110
|
};
|
|
111
|
+
presentationIntent?: PresentationIntent;
|
|
110
112
|
};
|
|
111
113
|
//# sourceMappingURL=scanDetailsPresentation.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scanDetailsPresentation.d.ts","sourceRoot":"","sources":["../../../src/presentation/dto/scanDetailsPresentation.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"scanDetailsPresentation.d.ts","sourceRoot":"","sources":["../../../src/presentation/dto/scanDetailsPresentation.ts"],"names":[],"mappings":"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"}
|
|
@@ -12,13 +12,4 @@ export type PresentationEvidenceContext = {
|
|
|
12
12
|
priority: PresentationPriority;
|
|
13
13
|
degradedMessage?: string;
|
|
14
14
|
};
|
|
15
|
-
export type FindingCountSummary = {
|
|
16
|
-
critical: number;
|
|
17
|
-
high: number;
|
|
18
|
-
medium: number;
|
|
19
|
-
low: number;
|
|
20
|
-
};
|
|
21
|
-
export declare function aggregateFindingCounts(findings: Array<{
|
|
22
|
-
severity: string;
|
|
23
|
-
}> | null | undefined): FindingCountSummary;
|
|
24
15
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/presentation/dto/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,GAAG,QAAQ,CAAC;AAEtE,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG,cAAc,GAAG,OAAO,CAAC;AAEnE,MAAM,MAAM,mBAAmB,GAAG,SAAS,GAAG,UAAU,GAAG,MAAM,CAAC;AAElE,MAAM,MAAM,sBAAsB,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;AAE/D,MAAM,MAAM,oBAAoB,GAAG,iBAAiB,GAAG,SAAS,CAAC;AAEjE,MAAM,MAAM,uBAAuB,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvE,8DAA8D;AAC9D,MAAM,MAAM,2BAA2B,GAAG;IACxC,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/presentation/dto/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,GAAG,QAAQ,CAAC;AAEtE,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG,cAAc,GAAG,OAAO,CAAC;AAEnE,MAAM,MAAM,mBAAmB,GAAG,SAAS,GAAG,UAAU,GAAG,MAAM,CAAC;AAElE,MAAM,MAAM,sBAAsB,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;AAE/D,MAAM,MAAM,oBAAoB,GAAG,iBAAiB,GAAG,SAAS,CAAC;AAEjE,MAAM,MAAM,uBAAuB,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvE,8DAA8D;AAC9D,MAAM,MAAM,2BAA2B,GAAG;IACxC,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC"}
|
|
@@ -1,22 +1 @@
|
|
|
1
|
-
export
|
|
2
|
-
const counts = {
|
|
3
|
-
critical: 0,
|
|
4
|
-
high: 0,
|
|
5
|
-
medium: 0,
|
|
6
|
-
low: 0,
|
|
7
|
-
};
|
|
8
|
-
if (!Array.isArray(findings))
|
|
9
|
-
return counts;
|
|
10
|
-
for (const f of findings) {
|
|
11
|
-
const s = f.severity;
|
|
12
|
-
if (s === "critical")
|
|
13
|
-
counts.critical += 1;
|
|
14
|
-
else if (s === "high")
|
|
15
|
-
counts.high += 1;
|
|
16
|
-
else if (s === "medium")
|
|
17
|
-
counts.medium += 1;
|
|
18
|
-
else if (s === "low")
|
|
19
|
-
counts.low += 1;
|
|
20
|
-
}
|
|
21
|
-
return counts;
|
|
22
|
-
}
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dashboardCardGolden.test.d.ts","sourceRoot":"","sources":["../../../src/presentation/fixtures/dashboardCardGolden.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { describe, expect, it } from "vitest";
|
|
5
|
+
import { buildScanCardPresentation } from "../orchestration/buildScanCardPresentation.js";
|
|
6
|
+
import { scanResultFastifyRuntime, scanResultTypescriptPatch, } from "./presentationPersonaFixtures.js";
|
|
7
|
+
const goldenPath = join(dirname(fileURLToPath(import.meta.url)), "dashboardCardGolden.json");
|
|
8
|
+
const golden = JSON.parse(readFileSync(goldenPath, "utf8"));
|
|
9
|
+
describe("dashboardCardGolden fixtures", () => {
|
|
10
|
+
it("matches live typescript patch card", () => {
|
|
11
|
+
const live = buildScanCardPresentation({
|
|
12
|
+
result: scanResultTypescriptPatch,
|
|
13
|
+
pipelineStatus: "done",
|
|
14
|
+
});
|
|
15
|
+
expect(live).toEqual(golden.typescriptPatch);
|
|
16
|
+
});
|
|
17
|
+
it("matches live fastify runtime card", () => {
|
|
18
|
+
const live = buildScanCardPresentation({
|
|
19
|
+
result: scanResultFastifyRuntime,
|
|
20
|
+
pipelineStatus: "done",
|
|
21
|
+
});
|
|
22
|
+
expect(live).toEqual(golden.fastifyRuntime);
|
|
23
|
+
});
|
|
24
|
+
it("fastify golden has deduplicated channels", () => {
|
|
25
|
+
const card = golden.fastifyRuntime;
|
|
26
|
+
const insightText = card.insights.join(" ");
|
|
27
|
+
expect(insightText).not.toMatch(/Affected areas:/i);
|
|
28
|
+
expect(card.scopeAreas?.length).toBeGreaterThan(0);
|
|
29
|
+
expect(card.evidenceChips?.some((e) => e.label === "Blast radius")).toBe(true);
|
|
30
|
+
});
|
|
31
|
+
});
|