@mergesignal/shared 0.4.2 → 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/engineWireRegression.test.js +2 -2
- 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 +4 -3
- package/dist/presentation/dto/dashboardCardPresentation.d.ts +29 -0
- package/dist/presentation/dto/dashboardCardPresentation.d.ts.map +1 -0
- 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/index.d.ts +6 -5
- package/dist/presentation/index.d.ts.map +1 -1
- package/dist/presentation/index.js +2 -3
- package/dist/presentation/intent/formatPresentationCopy.d.ts +2 -1
- package/dist/presentation/intent/formatPresentationCopy.d.ts.map +1 -1
- package/dist/presentation/intent/formatPresentationCopy.js +4 -17
- 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 +16 -18
- package/dist/presentation/presentationPersonas.test.js +14 -13
- 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 +6 -3
- package/dist/scanCardSummary.d.ts +3 -4
- package/dist/scanCardSummary.d.ts.map +1 -1
- package/dist/scanCardSummary.test.js +16 -16
- package/package.json +1 -1
- package/dist/presentation/dto/scanCardPresentation.d.ts +0 -27
- 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 -44
- /package/dist/presentation/dto/{scanCardPresentation.js → dashboardCardPresentation.js} +0 -0
|
@@ -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,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":"
|
|
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,7 +1,9 @@
|
|
|
1
1
|
import { formatCardAreaLabels } from "../../formatCardAreaLabels.js";
|
|
2
2
|
import { formatChangedPackagesShort, labelBlastRadiusLevel, labelReachabilityKind, labelRuntimeSurface, } from "../../narrativePresentation.js";
|
|
3
3
|
import { scanSurfaceCopy } from "../../scanSurfaceCopy.js";
|
|
4
|
-
import { formatPresentationHeadline,
|
|
4
|
+
import { formatPresentationHeadline, formatPresentationVerification, } from "../intent/formatPresentationCopy.js";
|
|
5
|
+
import { buildNarrativeChannels, projectCompactKeyPoints, } from "./narrativeChannels.js";
|
|
6
|
+
export { buildNarrativeChannels, projectCompactKeyPoints, } from "./narrativeChannels.js";
|
|
5
7
|
function topAreaLabel(bundle) {
|
|
6
8
|
const area = bundle.facts.affectedAreas[0];
|
|
7
9
|
if (!area)
|
|
@@ -20,8 +22,7 @@ export function composeSubheadline(bundle) {
|
|
|
20
22
|
return undefined;
|
|
21
23
|
}
|
|
22
24
|
export function composeKeyPoints(bundle, max) {
|
|
23
|
-
|
|
24
|
-
return formatPresentationKeyPoints(profile.interpretation, facts, profile.status, max);
|
|
25
|
+
return projectCompactKeyPoints(buildNarrativeChannels(bundle), max);
|
|
25
26
|
}
|
|
26
27
|
export function composeVerificationActions(bundle, max) {
|
|
27
28
|
const { profile } = bundle;
|
|
@@ -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"}
|
|
@@ -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
|
+
});
|
|
@@ -9,12 +9,13 @@ export type { PresentationProfile } from "./profile/presentationProfile.js";
|
|
|
9
9
|
export type { PresentationIntent, PresentationInterpretation, } from "./intent/presentationIntent.js";
|
|
10
10
|
export { derivePresentationInterpretation } from "./intent/derivePresentationInterpretation.js";
|
|
11
11
|
export type { PipelineStatus, PresentationStatus, PresentationDensity, PresentationConfidence, PresentationPriority, PresentationEvidenceContext, PresentationEvidenceRow, } from "./dto/types.js";
|
|
12
|
-
export type {
|
|
12
|
+
export type { DashboardCardPresentation } from "./dto/dashboardCardPresentation.js";
|
|
13
|
+
export type { DashboardCardLayout } from "./dto/dashboardCardPresentation.js";
|
|
14
|
+
/** @deprecated Use DashboardCardPresentation */
|
|
15
|
+
export type { DashboardCardPresentation as ScanCardPresentation } from "./dto/dashboardCardPresentation.js";
|
|
13
16
|
export type { ScanDetailsPresentation } from "./dto/scanDetailsPresentation.js";
|
|
14
17
|
export type { GitHubCheckRunPresentation, GitHubPrCommentPresentation, CliScanPresentation, } from "./dto/githubAndCliPresentation.js";
|
|
15
|
-
export {
|
|
16
|
-
export type { PresentScanCardContext } from "./presenters/presentScanCard.js";
|
|
17
|
-
export { presentPipelineScanCard } from "./presenters/presentPipelineScanCard.js";
|
|
18
|
+
export { presentDashboardCard, presentPipelineDashboardCard, } from "./presenters/presentDashboardCard.js";
|
|
18
19
|
export { presentScanDetails } from "./presenters/presentScanDetails.js";
|
|
19
20
|
export type { PresentScanDetailsContext } from "./presenters/presentScanDetails.js";
|
|
20
21
|
export { presentGitHubCheckRun } from "./presenters/presentGitHubCheckRun.js";
|
|
@@ -23,5 +24,5 @@ export { presentCliScanSummary } from "./presenters/presentCliScanSummary.js";
|
|
|
23
24
|
export { renderGitHubCheckRunMarkdown } from "./render/renderGitHubCheckRunMarkdown.js";
|
|
24
25
|
export { renderGitHubPrCommentMarkdown } from "./render/renderGitHubPrCommentMarkdown.js";
|
|
25
26
|
export { renderCliScanSummaryText } from "./render/renderCliScanSummaryText.js";
|
|
26
|
-
export { composeHeadline, composeSubheadline, composeKeyPoints, composeVerificationActions, composeAffectedAreaLabels, composeEvidenceRows, composeSupportingContext, evidenceContextFromProfile, } from "./compose/narrativeCompose.js";
|
|
27
|
+
export { buildNarrativeChannels, projectCompactKeyPoints, composeHeadline, composeSubheadline, composeKeyPoints, composeVerificationActions, composeAffectedAreaLabels, composeEvidenceRows, composeSupportingContext, evidenceContextFromProfile, } from "./compose/narrativeCompose.js";
|
|
27
28
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/presentation/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,sBAAsB,EAAE,MAAM,2CAA2C,CAAC;AACxF,OAAO,EAAE,2BAA2B,EAAE,MAAM,gDAAgD,CAAC;AAC7F,YAAY,EAAE,gCAAgC,EAAE,MAAM,gDAAgD,CAAC;AACvG,OAAO,EAAE,yBAAyB,EAAE,MAAM,8CAA8C,CAAC;AACzF,YAAY,EAAE,8BAA8B,EAAE,MAAM,8CAA8C,CAAC;AACnG,OAAO,EAAE,4BAA4B,EAAE,MAAM,iDAAiD,CAAC;AAC/F,YAAY,EAAE,iCAAiC,EAAE,MAAM,iDAAiD,CAAC;AAEzG,YAAY,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAC5E,YAAY,EACV,kBAAkB,EAClB,0BAA0B,GAC3B,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAE,gCAAgC,EAAE,MAAM,8CAA8C,CAAC;AAChG,YAAY,EACV,cAAc,EACd,kBAAkB,EAClB,mBAAmB,EACnB,sBAAsB,EACtB,oBAAoB,EACpB,2BAA2B,EAC3B,uBAAuB,GACxB,MAAM,gBAAgB,CAAC;AAExB,YAAY,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/presentation/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,sBAAsB,EAAE,MAAM,2CAA2C,CAAC;AACxF,OAAO,EAAE,2BAA2B,EAAE,MAAM,gDAAgD,CAAC;AAC7F,YAAY,EAAE,gCAAgC,EAAE,MAAM,gDAAgD,CAAC;AACvG,OAAO,EAAE,yBAAyB,EAAE,MAAM,8CAA8C,CAAC;AACzF,YAAY,EAAE,8BAA8B,EAAE,MAAM,8CAA8C,CAAC;AACnG,OAAO,EAAE,4BAA4B,EAAE,MAAM,iDAAiD,CAAC;AAC/F,YAAY,EAAE,iCAAiC,EAAE,MAAM,iDAAiD,CAAC;AAEzG,YAAY,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAC5E,YAAY,EACV,kBAAkB,EAClB,0BAA0B,GAC3B,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAE,gCAAgC,EAAE,MAAM,8CAA8C,CAAC;AAChG,YAAY,EACV,cAAc,EACd,kBAAkB,EAClB,mBAAmB,EACnB,sBAAsB,EACtB,oBAAoB,EACpB,2BAA2B,EAC3B,uBAAuB,GACxB,MAAM,gBAAgB,CAAC;AAExB,YAAY,EAAE,yBAAyB,EAAE,MAAM,oCAAoC,CAAC;AACpF,YAAY,EAAE,mBAAmB,EAAE,MAAM,oCAAoC,CAAC;AAC9E,gDAAgD;AAChD,YAAY,EAAE,yBAAyB,IAAI,oBAAoB,EAAE,MAAM,oCAAoC,CAAC;AAC5G,YAAY,EAAE,uBAAuB,EAAE,MAAM,kCAAkC,CAAC;AAChF,YAAY,EACV,0BAA0B,EAC1B,2BAA2B,EAC3B,mBAAmB,GACpB,MAAM,mCAAmC,CAAC;AAE3C,OAAO,EACL,oBAAoB,EACpB,4BAA4B,GAC7B,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AACxE,YAAY,EAAE,yBAAyB,EAAE,MAAM,oCAAoC,CAAC;AACpF,OAAO,EAAE,qBAAqB,EAAE,MAAM,uCAAuC,CAAC;AAC9E,OAAO,EAAE,sBAAsB,EAAE,MAAM,wCAAwC,CAAC;AAChF,OAAO,EAAE,qBAAqB,EAAE,MAAM,uCAAuC,CAAC;AAE9E,OAAO,EAAE,4BAA4B,EAAE,MAAM,0CAA0C,CAAC;AACxF,OAAO,EAAE,6BAA6B,EAAE,MAAM,2CAA2C,CAAC;AAC1F,OAAO,EAAE,wBAAwB,EAAE,MAAM,sCAAsC,CAAC;AAEhF,OAAO,EACL,sBAAsB,EACtB,uBAAuB,EACvB,eAAe,EACf,kBAAkB,EAClB,gBAAgB,EAChB,0BAA0B,EAC1B,yBAAyB,EACzB,mBAAmB,EACnB,wBAAwB,EACxB,0BAA0B,GAC3B,MAAM,+BAA+B,CAAC"}
|
|
@@ -2,8 +2,7 @@ export { buildScanPresentationBundle } from "./orchestration/buildScanPresentati
|
|
|
2
2
|
export { buildScanCardPresentation } from "./orchestration/buildScanCardPresentation.js";
|
|
3
3
|
export { buildScanDetailsPresentation } from "./orchestration/buildScanDetailsPresentation.js";
|
|
4
4
|
export { derivePresentationInterpretation } from "./intent/derivePresentationInterpretation.js";
|
|
5
|
-
export {
|
|
6
|
-
export { presentPipelineScanCard } from "./presenters/presentPipelineScanCard.js";
|
|
5
|
+
export { presentDashboardCard, presentPipelineDashboardCard, } from "./presenters/presentDashboardCard.js";
|
|
7
6
|
export { presentScanDetails } from "./presenters/presentScanDetails.js";
|
|
8
7
|
export { presentGitHubCheckRun } from "./presenters/presentGitHubCheckRun.js";
|
|
9
8
|
export { presentGitHubPrComment } from "./presenters/presentGitHubPrComment.js";
|
|
@@ -11,4 +10,4 @@ export { presentCliScanSummary } from "./presenters/presentCliScanSummary.js";
|
|
|
11
10
|
export { renderGitHubCheckRunMarkdown } from "./render/renderGitHubCheckRunMarkdown.js";
|
|
12
11
|
export { renderGitHubPrCommentMarkdown } from "./render/renderGitHubPrCommentMarkdown.js";
|
|
13
12
|
export { renderCliScanSummaryText } from "./render/renderCliScanSummaryText.js";
|
|
14
|
-
export { composeHeadline, composeSubheadline, composeKeyPoints, composeVerificationActions, composeAffectedAreaLabels, composeEvidenceRows, composeSupportingContext, evidenceContextFromProfile, } from "./compose/narrativeCompose.js";
|
|
13
|
+
export { buildNarrativeChannels, projectCompactKeyPoints, composeHeadline, composeSubheadline, composeKeyPoints, composeVerificationActions, composeAffectedAreaLabels, composeEvidenceRows, composeSupportingContext, evidenceContextFromProfile, } from "./compose/narrativeCompose.js";
|
|
@@ -2,6 +2,7 @@ import type { ScanNarrativeFacts } from "../../scanNarrativeFacts.js";
|
|
|
2
2
|
import type { PresentationStatus } from "../dto/types.js";
|
|
3
3
|
import type { PresentationInterpretation } from "./presentationIntent.js";
|
|
4
4
|
export declare function formatPresentationHeadline(interpretation: PresentationInterpretation, facts: ScanNarrativeFacts, status: PresentationStatus | undefined): string;
|
|
5
|
-
|
|
5
|
+
/** Atomic insight lines — no embedded areas or blast radius (those live in separate channels). */
|
|
6
|
+
export declare function formatPresentationInsightLines(interpretation: PresentationInterpretation, facts: ScanNarrativeFacts, status: PresentationStatus | undefined): string[];
|
|
6
7
|
export declare function formatPresentationVerification(interpretation: PresentationInterpretation, status: PresentationStatus | undefined, max: number): string[];
|
|
7
8
|
//# sourceMappingURL=formatPresentationCopy.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"formatPresentationCopy.d.ts","sourceRoot":"","sources":["../../../src/presentation/intent/formatPresentationCopy.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AACtE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAM1D,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,yBAAyB,CAAC;AAa1E,wBAAgB,0BAA0B,CACxC,cAAc,EAAE,0BAA0B,EAC1C,KAAK,EAAE,kBAAkB,EACzB,MAAM,EAAE,kBAAkB,GAAG,SAAS,GACrC,MAAM,CAmDR;AA0BD,wBAAgB,
|
|
1
|
+
{"version":3,"file":"formatPresentationCopy.d.ts","sourceRoot":"","sources":["../../../src/presentation/intent/formatPresentationCopy.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AACtE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAM1D,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,yBAAyB,CAAC;AAa1E,wBAAgB,0BAA0B,CACxC,cAAc,EAAE,0BAA0B,EAC1C,KAAK,EAAE,kBAAkB,EACzB,MAAM,EAAE,kBAAkB,GAAG,SAAS,GACrC,MAAM,CAmDR;AA0BD,kGAAkG;AAClG,wBAAgB,8BAA8B,CAC5C,cAAc,EAAE,0BAA0B,EAC1C,KAAK,EAAE,kBAAkB,EACzB,MAAM,EAAE,kBAAkB,GAAG,SAAS,GACrC,MAAM,EAAE,CA0CV;AAuBD,wBAAgB,8BAA8B,CAC5C,cAAc,EAAE,0BAA0B,EAC1C,MAAM,EAAE,kBAAkB,GAAG,SAAS,EACtC,GAAG,EAAE,MAAM,GACV,MAAM,EAAE,CAmBV"}
|
|
@@ -69,14 +69,15 @@ function formatExpectedImpactKeyPoint(interpretation) {
|
|
|
69
69
|
function formatLimitedEvidenceKeyPoint() {
|
|
70
70
|
return scanSurfaceCopy.presentation.limitedEvidenceKeyPoint;
|
|
71
71
|
}
|
|
72
|
-
|
|
72
|
+
/** Atomic insight lines — no embedded areas or blast radius (those live in separate channels). */
|
|
73
|
+
export function formatPresentationInsightLines(interpretation, facts, status) {
|
|
73
74
|
const points = [];
|
|
74
75
|
if (interpretation.intent === "limited_context") {
|
|
75
76
|
points.push(scanSurfaceCopy.presentation.limitedContextKeyPoint);
|
|
76
77
|
if (facts.changedPackages.primary) {
|
|
77
78
|
points.push(scanSurfaceCopy.presentation.changedPackageKeyPoint.replace("{package}", facts.changedPackages.primary));
|
|
78
79
|
}
|
|
79
|
-
return applyPostureVocabularyGuardLines(points, status)
|
|
80
|
+
return applyPostureVocabularyGuardLines(points, status);
|
|
80
81
|
}
|
|
81
82
|
if (interpretation.showLimitedEvidence) {
|
|
82
83
|
points.push(formatLimitedEvidenceKeyPoint());
|
|
@@ -96,22 +97,8 @@ export function formatPresentationKeyPoints(interpretation, facts, status, max)
|
|
|
96
97
|
else {
|
|
97
98
|
points.push(scanSurfaceCopy.presentation.runtimeUsageKeyPoint);
|
|
98
99
|
}
|
|
99
|
-
if (facts.affectedAreas.length > 0) {
|
|
100
|
-
const labels = facts.affectedAreas
|
|
101
|
-
.slice(0, 2)
|
|
102
|
-
.map((a) => a.label)
|
|
103
|
-
.join(", ");
|
|
104
|
-
points.push(scanSurfaceCopy.presentation.affectedAreasKeyPoint.replace("{areas}", labels));
|
|
105
|
-
}
|
|
106
|
-
const blast = facts.blastRadius;
|
|
107
|
-
if (blast?.level === "wide") {
|
|
108
|
-
points.push(scanSurfaceCopy.presentation.blastRadiusWideKeyPoint);
|
|
109
|
-
}
|
|
110
|
-
else if (blast?.level === "moderate") {
|
|
111
|
-
points.push(scanSurfaceCopy.presentation.blastRadiusLevelKeyPoint.replace("{level}", "Moderate"));
|
|
112
|
-
}
|
|
113
100
|
}
|
|
114
|
-
return applyPostureVocabularyGuardLines(points, status)
|
|
101
|
+
return applyPostureVocabularyGuardLines(points, status);
|
|
115
102
|
}
|
|
116
103
|
const AUTH_VERIFICATION = [
|
|
117
104
|
"routes",
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import type { PipelineStatus } from "../dto/types.js";
|
|
2
2
|
import type { ScanResult } from "../../types.js";
|
|
3
|
-
import type {
|
|
3
|
+
import type { DashboardCardPresentation } from "../dto/dashboardCardPresentation.js";
|
|
4
4
|
export type BuildScanCardPresentationInput = {
|
|
5
5
|
pipelineStatus: PipelineStatus;
|
|
6
6
|
result: ScanResult | null;
|
|
7
7
|
decision?: string | null;
|
|
8
8
|
totalScore?: number | null;
|
|
9
9
|
};
|
|
10
|
-
export declare function buildScanCardPresentation(input: BuildScanCardPresentationInput):
|
|
10
|
+
export declare function buildScanCardPresentation(input: BuildScanCardPresentationInput): DashboardCardPresentation;
|
|
11
11
|
//# sourceMappingURL=buildScanCardPresentation.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"buildScanCardPresentation.d.ts","sourceRoot":"","sources":["../../../src/presentation/orchestration/buildScanCardPresentation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"buildScanCardPresentation.d.ts","sourceRoot":"","sources":["../../../src/presentation/orchestration/buildScanCardPresentation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAOjD,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,qCAAqC,CAAC;AAErF,MAAM,MAAM,8BAA8B,GAAG;IAC3C,cAAc,EAAE,cAAc,CAAC;IAC/B,MAAM,EAAE,UAAU,GAAG,IAAI,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B,CAAC;AA2BF,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,8BAA8B,GACpC,yBAAyB,CA2B3B"}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { mergePostureFromDecision } from "../../riskVocabulary.js";
|
|
2
2
|
import { buildScanPresentationBundle } from "./buildScanPresentationBundle.js";
|
|
3
|
-
import {
|
|
4
|
-
import { presentPipelineScanCard } from "../presenters/presentPipelineScanCard.js";
|
|
3
|
+
import { presentDashboardCard, presentPipelineDashboardCard, } from "../presenters/presentDashboardCard.js";
|
|
5
4
|
function minimalResultFromDenormalized(decision, totalScore) {
|
|
6
5
|
const posture = mergePostureFromDecision(decision ?? undefined);
|
|
7
6
|
if (!posture && (totalScore == null || !Number.isFinite(totalScore))) {
|
|
@@ -24,12 +23,12 @@ function minimalResultFromDenormalized(decision, totalScore) {
|
|
|
24
23
|
}
|
|
25
24
|
export function buildScanCardPresentation(input) {
|
|
26
25
|
if (input.pipelineStatus !== "done") {
|
|
27
|
-
return
|
|
26
|
+
return presentPipelineDashboardCard(input.pipelineStatus === "failed" ? "failed" : input.pipelineStatus);
|
|
28
27
|
}
|
|
29
28
|
const result = input.result ??
|
|
30
29
|
minimalResultFromDenormalized(input.decision, input.totalScore);
|
|
31
30
|
if (!result) {
|
|
32
|
-
return
|
|
31
|
+
return presentPipelineDashboardCard("failed");
|
|
33
32
|
}
|
|
34
33
|
const bundle = buildScanPresentationBundle({
|
|
35
34
|
result,
|
|
@@ -38,7 +37,7 @@ export function buildScanCardPresentation(input) {
|
|
|
38
37
|
totalScore: input.totalScore,
|
|
39
38
|
});
|
|
40
39
|
if (!bundle) {
|
|
41
|
-
return
|
|
40
|
+
return presentPipelineDashboardCard("failed");
|
|
42
41
|
}
|
|
43
|
-
return
|
|
42
|
+
return presentDashboardCard(bundle);
|
|
44
43
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest";
|
|
2
2
|
import { buildScanPresentationBundle } from "./orchestration/buildScanPresentationBundle.js";
|
|
3
|
-
import {
|
|
3
|
+
import { presentDashboardCard } from "./presenters/presentDashboardCard.js";
|
|
4
4
|
import { presentScanDetails } from "./presenters/presentScanDetails.js";
|
|
5
5
|
import { presentGitHubCheckRun } from "./presenters/presentGitHubCheckRun.js";
|
|
6
6
|
import { presentGitHubPrComment } from "./presenters/presentGitHubPrComment.js";
|
|
@@ -43,32 +43,30 @@ describe("buildScanPresentationBundle", () => {
|
|
|
43
43
|
expect(bundle.profile.degradedMessage).toBeTruthy();
|
|
44
44
|
});
|
|
45
45
|
});
|
|
46
|
-
describe("
|
|
47
|
-
it("fastify card
|
|
46
|
+
describe("presentDashboardCard personas", () => {
|
|
47
|
+
it("fastify card has scope areas and verification", () => {
|
|
48
48
|
const bundle = buildScanPresentationBundle({
|
|
49
49
|
result: scanResultFastifyRuntime,
|
|
50
50
|
pipelineStatus: "done",
|
|
51
51
|
});
|
|
52
|
-
const card =
|
|
53
|
-
expect(card.
|
|
54
|
-
expect(card.
|
|
55
|
-
expect(card.
|
|
52
|
+
const card = presentDashboardCard(bundle);
|
|
53
|
+
expect(card.layout).toBe("expanded");
|
|
54
|
+
expect(card.scopeAreas.length).toBeGreaterThan(0);
|
|
55
|
+
expect(card.verification.length).toBeGreaterThan(0);
|
|
56
56
|
expect(card.headline.toLowerCase()).toContain("fastify");
|
|
57
|
-
expect(card.
|
|
57
|
+
expect(card.verdict?.posture).toBe("needs_review");
|
|
58
58
|
});
|
|
59
|
-
it("typescript card is
|
|
59
|
+
it("typescript card is quiet safe", () => {
|
|
60
60
|
const bundle = buildScanPresentationBundle({
|
|
61
61
|
result: scanResultTypescriptPatch,
|
|
62
62
|
pipelineStatus: "done",
|
|
63
63
|
});
|
|
64
|
-
const card =
|
|
65
|
-
expect(
|
|
66
|
-
expect(card.
|
|
67
|
-
expect(card.presentationIntent).toBe("tooling_patch");
|
|
64
|
+
const card = presentDashboardCard(bundle);
|
|
65
|
+
expect(["quiet", "standard"]).toContain(card.layout);
|
|
66
|
+
expect(card.verdict?.posture).toBe("safe");
|
|
68
67
|
expect(card.headline).toMatch(/patch upgrade/i);
|
|
69
68
|
expect(card.headline).not.toMatch(/needs review/i);
|
|
70
|
-
expect(card.
|
|
71
|
-
expect(card.verificationActions.length).toBeGreaterThan(0);
|
|
69
|
+
expect(card.verification.length).toBeGreaterThan(0);
|
|
72
70
|
});
|
|
73
71
|
});
|
|
74
72
|
describe("presentation parity across surfaces", () => {
|
|
@@ -77,7 +75,7 @@ describe("presentation parity across surfaces", () => {
|
|
|
77
75
|
result: scanResultFastifyRuntime,
|
|
78
76
|
pipelineStatus: "done",
|
|
79
77
|
});
|
|
80
|
-
const card =
|
|
78
|
+
const card = presentDashboardCard(bundle);
|
|
81
79
|
const details = presentScanDetails(bundle, { scanId: SCAN_ID });
|
|
82
80
|
const check = presentGitHubCheckRun(bundle, {
|
|
83
81
|
scanId: SCAN_ID,
|
|
@@ -85,13 +83,13 @@ describe("presentation parity across surfaces", () => {
|
|
|
85
83
|
});
|
|
86
84
|
const comment = presentGitHubPrComment(bundle);
|
|
87
85
|
const cli = presentCliScanSummary(bundle, { repoLabel: "acme/api" });
|
|
88
|
-
for (const dto of [
|
|
86
|
+
for (const dto of [details, check, comment, cli]) {
|
|
89
87
|
expect(dto.status).toBe(bundle.profile.status);
|
|
90
88
|
expect(dto.density).toBe(bundle.profile.density);
|
|
91
89
|
expect(dto.confidence).toBe(bundle.profile.confidence);
|
|
92
90
|
expect(dto.evidenceContext?.priority).toBe(bundle.profile.priority);
|
|
93
91
|
}
|
|
94
|
-
expect(card.
|
|
92
|
+
expect(card.verdict?.posture).toBe(bundle.profile.status);
|
|
95
93
|
expect(details.narrative.primaryPackage).toBe("fastify");
|
|
96
94
|
expect(check.title.toLowerCase()).toContain("fastify");
|
|
97
95
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest";
|
|
2
2
|
import { buildScanPresentationBundle } from "./orchestration/buildScanPresentationBundle.js";
|
|
3
|
-
import {
|
|
3
|
+
import { presentDashboardCard } from "./presenters/presentDashboardCard.js";
|
|
4
4
|
import { presentScanDetails } from "./presenters/presentScanDetails.js";
|
|
5
5
|
import { presentCliScanSummary } from "./presenters/presentCliScanSummary.js";
|
|
6
6
|
import { scanResultBullmq, scanResultEslint, scanResultFastifyRuntime, scanResultLimitedContext, scanResultMixedTypescriptFastify, scanResultNextAuth, scanResultPrettier, scanResultTypescriptPatch, scanResultUnknownSafe, scanResultVitest, } from "./fixtures/presentationPersonaFixtures.js";
|
|
@@ -99,15 +99,16 @@ function cardFor(result) {
|
|
|
99
99
|
result,
|
|
100
100
|
pipelineStatus: "done",
|
|
101
101
|
});
|
|
102
|
-
return
|
|
102
|
+
return presentDashboardCard(bundle);
|
|
103
103
|
}
|
|
104
104
|
describe("presentation personas", () => {
|
|
105
105
|
for (const persona of personas) {
|
|
106
106
|
it(`${persona.name}: intent, posture alignment, headline`, () => {
|
|
107
107
|
const card = cardFor(persona.result);
|
|
108
|
-
expect(card.
|
|
109
|
-
|
|
110
|
-
|
|
108
|
+
expect(card.verdict?.posture).toBe(persona.status);
|
|
109
|
+
if (persona.density === "rich") {
|
|
110
|
+
expect(card.layout).toBe("expanded");
|
|
111
|
+
}
|
|
111
112
|
expect(card.headline).toMatch(persona.headlineMatch);
|
|
112
113
|
if (persona.headlineMustNot) {
|
|
113
114
|
expect(card.headline).not.toMatch(persona.headlineMustNot);
|
|
@@ -115,28 +116,28 @@ describe("presentation personas", () => {
|
|
|
115
116
|
if (persona.status === "safe") {
|
|
116
117
|
const combined = [
|
|
117
118
|
card.headline,
|
|
118
|
-
...card.
|
|
119
|
-
...card.
|
|
119
|
+
...card.insights,
|
|
120
|
+
...card.verification,
|
|
120
121
|
].join(" ");
|
|
121
122
|
expect(combined).not.toMatch(SAFE_FORBIDDEN);
|
|
122
123
|
}
|
|
123
124
|
if (persona.status === "needs_review") {
|
|
124
|
-
const combined = card.
|
|
125
|
+
const combined = card.verification.join(" ");
|
|
125
126
|
expect(combined).not.toMatch(REVIEW_FORBIDDEN);
|
|
126
127
|
}
|
|
127
128
|
if (persona.verificationIncludes) {
|
|
128
129
|
for (const v of persona.verificationIncludes) {
|
|
129
|
-
expect(card.
|
|
130
|
+
expect(card.verification.some((a) => a.toLowerCase().includes(v.toLowerCase()))).toBe(true);
|
|
130
131
|
}
|
|
131
132
|
}
|
|
132
133
|
if (persona.verificationMustNot) {
|
|
133
|
-
expect(card.
|
|
134
|
+
expect(card.verification.join(" ")).not.toMatch(persona.verificationMustNot);
|
|
134
135
|
}
|
|
135
136
|
});
|
|
136
137
|
}
|
|
137
138
|
it("mixed typescript+fastify: runtime intent, fastify in headline", () => {
|
|
138
139
|
const card = cardFor(scanResultMixedTypescriptFastify);
|
|
139
|
-
expect(card.
|
|
140
|
+
expect(card.verdict?.posture).toBe("needs_review");
|
|
140
141
|
expect(card.headline.toLowerCase()).toContain("fastify");
|
|
141
142
|
expect(card.headline).not.toMatch(/patch upgrade/i);
|
|
142
143
|
});
|
|
@@ -145,7 +146,7 @@ describe("presentation personas", () => {
|
|
|
145
146
|
result: scanResultFastifyRuntime,
|
|
146
147
|
pipelineStatus: "done",
|
|
147
148
|
});
|
|
148
|
-
const card =
|
|
149
|
+
const card = presentDashboardCard(bundle);
|
|
149
150
|
const details = presentScanDetails(bundle, { scanId: SCAN_ID });
|
|
150
151
|
const cli = presentCliScanSummary(bundle, { repoLabel: "acme/api" });
|
|
151
152
|
expect(details.hero.headline).toBe(card.headline);
|
|
@@ -156,7 +157,7 @@ describe("presentation personas", () => {
|
|
|
156
157
|
result: scanResultTypescriptPatch,
|
|
157
158
|
pipelineStatus: "done",
|
|
158
159
|
});
|
|
159
|
-
const card =
|
|
160
|
+
const card = presentDashboardCard(bundle);
|
|
160
161
|
const details = presentScanDetails(bundle, { scanId: SCAN_ID });
|
|
161
162
|
const cli = presentCliScanSummary(bundle, { repoLabel: "acme/api" });
|
|
162
163
|
expect(details.hero.headline).toBe(card.headline);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"presentCliScanSummary.d.ts","sourceRoot":"","sources":["../../../src/presentation/presenters/presentCliScanSummary.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"presentCliScanSummary.d.ts","sourceRoot":"","sources":["../../../src/presentation/presenters/presentCliScanSummary.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oCAAoC,CAAC;AAC9E,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,4CAA4C,CAAC;AAEzF,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,sBAAsB,EAC9B,GAAG,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,GAC/B,mBAAmB,CA6BrB"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { buildNarrativeChannels, composeHeadline, composeSubheadline, composeSupportingContext, composeVerificationActions, evidenceContextFromProfile, projectCompactKeyPoints, } from "../compose/narrativeCompose.js";
|
|
2
2
|
export function presentCliScanSummary(bundle, ctx = {}) {
|
|
3
3
|
const { facts, profile, result } = bundle;
|
|
4
4
|
const layers = result.layerScores;
|
|
@@ -17,7 +17,7 @@ export function presentCliScanSummary(bundle, ctx = {}) {
|
|
|
17
17
|
evidenceContext: evidenceContextFromProfile(bundle),
|
|
18
18
|
headline: composeHeadline(bundle),
|
|
19
19
|
subheadline: composeSubheadline(bundle),
|
|
20
|
-
keyPoints:
|
|
20
|
+
keyPoints: projectCompactKeyPoints(buildNarrativeChannels(bundle), 6),
|
|
21
21
|
verificationActions: composeVerificationActions(bundle, 5),
|
|
22
22
|
metrics: {
|
|
23
23
|
riskIndex: facts.riskIndex ?? result.totalScore ?? 0,
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { DashboardCardPresentation } from "../dto/dashboardCardPresentation.js";
|
|
2
|
+
import type { ScanPresentationBundle } from "../orchestration/scanPresentationBundle.js";
|
|
3
|
+
export declare function presentDashboardCard(bundle: ScanPresentationBundle): DashboardCardPresentation;
|
|
4
|
+
export declare function presentPipelineDashboardCard(pipelineStatus: "queued" | "running" | "failed"): DashboardCardPresentation;
|
|
5
|
+
//# sourceMappingURL=presentDashboardCard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"presentDashboardCard.d.ts","sourceRoot":"","sources":["../../../src/presentation/presenters/presentDashboardCard.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,qCAAqC,CAAC;AAErF,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,4CAA4C,CAAC;AAmEzF,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,sBAAsB,GAC7B,yBAAyB,CA6C3B;AAED,wBAAgB,4BAA4B,CAC1C,cAAc,EAAE,QAAQ,GAAG,SAAS,GAAG,QAAQ,GAC9C,yBAAyB,CAqB3B"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { MERGE_POSTURE_LABEL, MERGE_POSTURE_SORT_ORDER, } from "../../riskVocabulary.js";
|
|
2
|
+
import { normalizeGeneratedText, normalizeGeneratedTextNullable, } from "../../normalizeGeneratedText.js";
|
|
3
|
+
import { scanSurfaceCopy } from "../../scanSurfaceCopy.js";
|
|
4
|
+
import { buildNarrativeChannels } from "../compose/narrativeChannels.js";
|
|
5
|
+
const INSIGHTS_CAP = 3;
|
|
6
|
+
const SCOPE_AREAS_CAP = 2;
|
|
7
|
+
const EVIDENCE_CAP = 2;
|
|
8
|
+
const VERIFICATION_CAP = 2;
|
|
9
|
+
function formatVerificationLine(action) {
|
|
10
|
+
const trimmed = action.trim();
|
|
11
|
+
if (/^verify\b/i.test(trimmed))
|
|
12
|
+
return trimmed;
|
|
13
|
+
return `Verify ${trimmed}`;
|
|
14
|
+
}
|
|
15
|
+
function deriveLayout(channels, profile) {
|
|
16
|
+
const bodyCount = channels.insights.length +
|
|
17
|
+
channels.scopeAreas.length +
|
|
18
|
+
channels.evidence.length +
|
|
19
|
+
channels.verification.length;
|
|
20
|
+
if (profile.status === "safe" &&
|
|
21
|
+
profile.density === "minimal" &&
|
|
22
|
+
bodyCount === 0 &&
|
|
23
|
+
!channels.limitedContextMessage) {
|
|
24
|
+
return "quiet";
|
|
25
|
+
}
|
|
26
|
+
if (profile.density === "rich") {
|
|
27
|
+
return "expanded";
|
|
28
|
+
}
|
|
29
|
+
return "standard";
|
|
30
|
+
}
|
|
31
|
+
function normalizeDashboardCard(card) {
|
|
32
|
+
return {
|
|
33
|
+
...card,
|
|
34
|
+
headline: normalizeGeneratedText(card.headline),
|
|
35
|
+
limitedContext: card.limitedContext
|
|
36
|
+
? {
|
|
37
|
+
message: normalizeGeneratedText(card.limitedContext.message),
|
|
38
|
+
}
|
|
39
|
+
: undefined,
|
|
40
|
+
insights: card.insights.map((i) => normalizeGeneratedText(i)),
|
|
41
|
+
scopeAreas: card.scopeAreas?.map((a) => normalizeGeneratedText(a)),
|
|
42
|
+
verification: card.verification.map((v) => normalizeGeneratedText(v)),
|
|
43
|
+
evidenceChips: card.evidenceChips?.map((e) => ({
|
|
44
|
+
label: normalizeGeneratedText(e.label),
|
|
45
|
+
value: normalizeGeneratedText(e.value),
|
|
46
|
+
})),
|
|
47
|
+
pipeline: card.pipeline
|
|
48
|
+
? {
|
|
49
|
+
...card.pipeline,
|
|
50
|
+
headline: normalizeGeneratedText(card.pipeline.headline),
|
|
51
|
+
subheadline: normalizeGeneratedTextNullable(card.pipeline.subheadline) ??
|
|
52
|
+
undefined,
|
|
53
|
+
}
|
|
54
|
+
: undefined,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
export function presentDashboardCard(bundle) {
|
|
58
|
+
const { facts, profile } = bundle;
|
|
59
|
+
const channels = buildNarrativeChannels(bundle);
|
|
60
|
+
const posture = profile.status;
|
|
61
|
+
const riskIndex = facts.riskIndex != null && Number.isFinite(facts.riskIndex)
|
|
62
|
+
? facts.riskIndex
|
|
63
|
+
: -1;
|
|
64
|
+
const insights = channels.insights.slice(0, INSIGHTS_CAP);
|
|
65
|
+
const scopeAreas = channels.scopeAreas.length > 0
|
|
66
|
+
? channels.scopeAreas.slice(0, SCOPE_AREAS_CAP)
|
|
67
|
+
: undefined;
|
|
68
|
+
const evidenceChips = channels.evidence.length > 0
|
|
69
|
+
? channels.evidence.slice(0, EVIDENCE_CAP)
|
|
70
|
+
: undefined;
|
|
71
|
+
const verification = channels.verification
|
|
72
|
+
.slice(0, VERIFICATION_CAP)
|
|
73
|
+
.map(formatVerificationLine);
|
|
74
|
+
const card = {
|
|
75
|
+
verdict: {
|
|
76
|
+
posture,
|
|
77
|
+
postureLabel: MERGE_POSTURE_LABEL[posture],
|
|
78
|
+
scopeLabel: channels.reachLabel,
|
|
79
|
+
},
|
|
80
|
+
headline: channels.headline,
|
|
81
|
+
limitedContext: channels.limitedContextMessage
|
|
82
|
+
? { message: channels.limitedContextMessage }
|
|
83
|
+
: undefined,
|
|
84
|
+
insights,
|
|
85
|
+
scopeAreas,
|
|
86
|
+
verification,
|
|
87
|
+
evidenceChips,
|
|
88
|
+
layout: deriveLayout(channels, profile),
|
|
89
|
+
detailActionLabel: scanSurfaceCopy.presentation.actionLabelReview,
|
|
90
|
+
sortKey: {
|
|
91
|
+
postureRank: MERGE_POSTURE_SORT_ORDER[posture],
|
|
92
|
+
riskIndex,
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
return normalizeDashboardCard(card);
|
|
96
|
+
}
|
|
97
|
+
export function presentPipelineDashboardCard(pipelineStatus) {
|
|
98
|
+
const isRunning = pipelineStatus === "queued" || pipelineStatus === "running";
|
|
99
|
+
const headline = isRunning
|
|
100
|
+
? scanSurfaceCopy.pipeline.scanRunning
|
|
101
|
+
: scanSurfaceCopy.pipeline.analysisIncomplete;
|
|
102
|
+
return normalizeDashboardCard({
|
|
103
|
+
pipeline: {
|
|
104
|
+
status: pipelineStatus,
|
|
105
|
+
headline,
|
|
106
|
+
subheadline: isRunning
|
|
107
|
+
? scanSurfaceCopy.pipeline.scanIncomplete
|
|
108
|
+
: undefined,
|
|
109
|
+
},
|
|
110
|
+
headline,
|
|
111
|
+
insights: [],
|
|
112
|
+
verification: [],
|
|
113
|
+
layout: "standard",
|
|
114
|
+
detailActionLabel: scanSurfaceCopy.presentation.actionLabelReview,
|
|
115
|
+
sortKey: { postureRank: -1, riskIndex: -1 },
|
|
116
|
+
});
|
|
117
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"presentGitHubCheckRun.d.ts","sourceRoot":"","sources":["../../../src/presentation/presenters/presentGitHubCheckRun.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"presentGitHubCheckRun.d.ts","sourceRoot":"","sources":["../../../src/presentation/presenters/presentGitHubCheckRun.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,oCAAoC,CAAC;AACrF,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,4CAA4C,CAAC;AAEzF,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,sBAAsB,EAC9B,GAAG,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,GAChE,0BAA0B,CAkC5B"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { buildNarrativeChannels, composeHeadline, composeSubheadline, composeVerificationActions, evidenceContextFromProfile, projectCompactKeyPoints, } from "../compose/narrativeCompose.js";
|
|
2
2
|
export function presentGitHubCheckRun(bundle, ctx) {
|
|
3
3
|
const { profile } = bundle;
|
|
4
4
|
const headline = composeHeadline(bundle);
|
|
5
|
-
const keyPoints =
|
|
5
|
+
const keyPoints = projectCompactKeyPoints(buildNarrativeChannels(bundle), 3);
|
|
6
6
|
const actions = composeVerificationActions(bundle, 3);
|
|
7
7
|
const detailsUrl = `${ctx.webAppOrigin.replace(/\/$/, "")}/scan/${ctx.scanId}`;
|
|
8
8
|
const conclusion = profile.status === "safe"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"presentScanDetails.d.ts","sourceRoot":"","sources":["../../../src/presentation/presenters/presentScanDetails.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"presentScanDetails.d.ts","sourceRoot":"","sources":["../../../src/presentation/presenters/presentScanDetails.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,mCAAmC,CAAC;AACjF,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,4CAA4C,CAAC;AAEzF,MAAM,MAAM,yBAAyB,GAAG;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,kBAAkB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B,CAAC;AAoHF,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,sBAAsB,EAC9B,GAAG,EAAE,yBAAyB,GAC7B,uBAAuB,CA6FzB"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { deriveDetailReachChip } from "../../formatCardExposureDisplay.js";
|
|
2
2
|
import { MERGE_POSTURE_LABEL } from "../../riskVocabulary.js";
|
|
3
3
|
import { deriveVerdictLineFromFacts, presentScanDetailViewModel, } from "../../scanDetailViewModel.js";
|
|
4
|
-
import {
|
|
4
|
+
import { buildNarrativeChannels, composeHeadline, composeSubheadline, composeSupportingContext, composeVerificationActions, evidenceContextFromProfile, projectCompactKeyPoints, } from "../compose/narrativeCompose.js";
|
|
5
5
|
import { normalizeGeneratedText, normalizeGeneratedTextNullable, } from "../../normalizeGeneratedText.js";
|
|
6
6
|
import { scanSurfaceCopy } from "../../scanSurfaceCopy.js";
|
|
7
7
|
function mapOperationalImpact(vm) {
|
|
@@ -112,7 +112,8 @@ export function presentScanDetails(bundle, ctx) {
|
|
|
112
112
|
});
|
|
113
113
|
const headline = composeHeadline(bundle);
|
|
114
114
|
const subheadline = composeSubheadline(bundle);
|
|
115
|
-
const
|
|
115
|
+
const channels = buildNarrativeChannels(bundle);
|
|
116
|
+
const keyPoints = projectCompactKeyPoints(channels, 6);
|
|
116
117
|
const verificationTitles = composeVerificationActions(bundle, 6);
|
|
117
118
|
const supportingLines = composeSupportingContext(bundle);
|
|
118
119
|
const supportingContext = supportingLines
|
|
@@ -138,7 +139,9 @@ export function presentScanDetails(bundle, ctx) {
|
|
|
138
139
|
headline: normalizeGeneratedText(headline),
|
|
139
140
|
subheadline: normalizeGeneratedTextNullable(subheadline) ?? undefined,
|
|
140
141
|
verdictLine: normalizeGeneratedText(verdictLine),
|
|
141
|
-
scopeChip:
|
|
142
|
+
scopeChip: channels.reachLabel ??
|
|
143
|
+
deriveDetailReachChip(result.totalScore) ??
|
|
144
|
+
undefined,
|
|
142
145
|
postureLabel,
|
|
143
146
|
riskIndex: facts.riskIndex,
|
|
144
147
|
},
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { DashboardCardPresentation } from "./presentation/dto/dashboardCardPresentation.js";
|
|
2
2
|
import type { Finding, ScanResult } from "./types.js";
|
|
3
3
|
export type ScanPipelineStatus = "queued" | "running" | "done" | "failed";
|
|
4
4
|
export type ScanCardPresentationState = "not_scanned" | "scanning" | "analysis_failed" | "ready" | "stale";
|
|
@@ -9,9 +9,8 @@ export type FindingCountSummary = {
|
|
|
9
9
|
medium: number;
|
|
10
10
|
low: number;
|
|
11
11
|
};
|
|
12
|
-
/** @deprecated Use
|
|
13
|
-
export type ScanCardSummary =
|
|
14
|
-
export { ScanCardPresentation };
|
|
12
|
+
/** @deprecated Use DashboardCardPresentation */
|
|
13
|
+
export type ScanCardSummary = DashboardCardPresentation;
|
|
15
14
|
export declare const SCAN_CARD_SCANNING_SUMMARY = "Waiting for results...";
|
|
16
15
|
export type PipelineCompletionEvidence = {
|
|
17
16
|
scannedAt?: string | null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scanCardSummary.d.ts","sourceRoot":"","sources":["../src/scanCardSummary.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"scanCardSummary.d.ts","sourceRoot":"","sources":["../src/scanCardSummary.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,iDAAiD,CAAC;AAMjG,OAAO,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAEtD,MAAM,MAAM,kBAAkB,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,GAAG,QAAQ,CAAC;AAE1E,MAAM,MAAM,yBAAyB,GACjC,aAAa,GACb,UAAU,GACV,iBAAiB,GACjB,OAAO,GACP,OAAO,CAAC;AAEZ,MAAM,MAAM,aAAa,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;AAEtD,MAAM,MAAM,mBAAmB,GAAG;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;CACb,CAAC;AAEF,gDAAgD;AAChD,MAAM,MAAM,eAAe,GAAG,yBAAyB,CAAC;AAGxD,eAAO,MAAM,0BAA0B,2BAA2B,CAAC;AAEnE,MAAM,MAAM,0BAA0B,GAAG;IACvC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,CAAC;AAsBF,wFAAwF;AACxF,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,0BAA0B,GACnC,OAAO,CAQT;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,QAAQ,EAAE,0BAA0B,GACnC,kBAAkB,CAKpB;AAED,8EAA8E;AAC9E,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAC9B,OAAO,CAST;AAED,wEAAwE;AACxE,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAEvE;AAED,mFAAmF;AACnF,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAC/B,aAAa,GAAG,IAAI,CAKtB;AAED,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,OAAO,EAAE,GAAG,IAAI,GAAG,SAAS,GACrC,mBAAmB,CAgBrB;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,UAAU,GAAG,IAAI,GAAG,SAAS,EACrC,cAAc,EAAE,kBAAkB,GACjC,eAAe,CAOjB;AAED,wEAAwE;AACxE,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAED,2FAA2F;AAC3F,wBAAgB,qCAAqC,CACnD,QAAQ,EAAE,MAAM,GAAG,IAAI,EACvB,UAAU,EAAE,MAAM,GAAG,IAAI,EACzB,WAAW,EAAE,MAAM,GAAG,IAAI,EAC1B,cAAc,EAAE,kBAAkB,GACjC,eAAe,CAuBjB;AAED,MAAM,MAAM,sBAAsB,GAAG;IACnC,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,EAAE,UAAU,GAAG,IAAI,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B,CAAC;AAEF;;GAEG;AACH,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,sBAAsB,GAC5B,eAAe,CAcjB"}
|
|
@@ -20,11 +20,11 @@ function pipelineCard(overrides = {}) {
|
|
|
20
20
|
subheadline: scanSurfaceCopy.pipeline.scanIncomplete,
|
|
21
21
|
},
|
|
22
22
|
headline: scanSurfaceCopy.pipeline.scanRunning,
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
insights: [],
|
|
24
|
+
verification: [],
|
|
25
|
+
layout: "standard",
|
|
26
|
+
detailActionLabel: scanSurfaceCopy.presentation.actionLabelReview,
|
|
27
|
+
sortKey: { postureRank: -1, riskIndex: -1 },
|
|
28
28
|
...overrides,
|
|
29
29
|
};
|
|
30
30
|
}
|
|
@@ -70,7 +70,7 @@ describe("deriveScanCardSummary", () => {
|
|
|
70
70
|
const queued = deriveScanCardSummary(null, "queued");
|
|
71
71
|
expect(queued.headline).toBe(scanSurfaceCopy.pipeline.scanRunning);
|
|
72
72
|
expect(queued.pipeline?.status).toBe("queued");
|
|
73
|
-
expect(queued.
|
|
73
|
+
expect(queued.verdict).toBeUndefined();
|
|
74
74
|
const running = deriveScanCardSummary(null, "running");
|
|
75
75
|
expect(running.headline).toBe(scanSurfaceCopy.pipeline.scanRunning);
|
|
76
76
|
expect(running.pipeline?.status).toBe("running");
|
|
@@ -101,9 +101,9 @@ describe("deriveScanCardSummary", () => {
|
|
|
101
101
|
},
|
|
102
102
|
};
|
|
103
103
|
const summary = deriveScanCardSummary(r, "done");
|
|
104
|
-
expect(summary.
|
|
104
|
+
expect(summary.verdict?.posture).toBe("risky");
|
|
105
105
|
expect(summary.headline).toBe(scanSurfaceCopy.presentation.limitedContextHeadline);
|
|
106
|
-
expect(summary.riskIndex).toBe(72);
|
|
106
|
+
expect(summary.sortKey.riskIndex).toBe(72);
|
|
107
107
|
});
|
|
108
108
|
it("keeps quiet safe cards minimal without extra key points beyond defaults", () => {
|
|
109
109
|
const r = {
|
|
@@ -116,8 +116,8 @@ describe("deriveScanCardSummary", () => {
|
|
|
116
116
|
},
|
|
117
117
|
};
|
|
118
118
|
const summary = deriveScanCardSummary(r, "done");
|
|
119
|
-
expect(summary.
|
|
120
|
-
expect(summary.
|
|
119
|
+
expect(summary.verdict?.posture).toBe("safe");
|
|
120
|
+
expect(summary.insights.length).toBeGreaterThan(0);
|
|
121
121
|
});
|
|
122
122
|
it("surfaces catalog key points from explain signals", () => {
|
|
123
123
|
const r = {
|
|
@@ -146,7 +146,7 @@ describe("deriveScanCardSummary", () => {
|
|
|
146
146
|
},
|
|
147
147
|
};
|
|
148
148
|
const summary = deriveScanCardSummary(r, "done");
|
|
149
|
-
expect(summary.
|
|
149
|
+
expect(summary.insights.length).toBeGreaterThan(0);
|
|
150
150
|
});
|
|
151
151
|
it("exports stale subline constant", () => {
|
|
152
152
|
expect(staleScanSubline()).toBe("Based on earlier commit");
|
|
@@ -204,18 +204,18 @@ describe("isPipelinePlaceholderCopy", () => {
|
|
|
204
204
|
describe("deriveScanCardSummaryFromDenormalized", () => {
|
|
205
205
|
it("builds risky summary from denormalized columns", () => {
|
|
206
206
|
const summary = deriveScanCardSummaryFromDenormalized("risky", 72, "Auth boundary change", "done");
|
|
207
|
-
expect(summary.
|
|
208
|
-
expect(summary.riskIndex).toBe(72);
|
|
207
|
+
expect(summary.verdict?.posture).toBe("risky");
|
|
208
|
+
expect(summary.sortKey.riskIndex).toBe(72);
|
|
209
209
|
expect(summary.pipeline).toBeUndefined();
|
|
210
210
|
});
|
|
211
211
|
it("does not inject pipeline placeholder summaryText", () => {
|
|
212
212
|
const summary = deriveScanCardSummaryFromDenormalized("risky", 72, "Waiting for results...", "done");
|
|
213
|
-
expect(summary.
|
|
213
|
+
expect(summary.verdict?.posture).toBe("risky");
|
|
214
214
|
expect(summary.pipeline).toBeUndefined();
|
|
215
215
|
});
|
|
216
216
|
it("does not inject raw generic summaryText on quiet safe cards", () => {
|
|
217
217
|
const summary = deriveScanCardSummaryFromDenormalized("safe", 12, "No high-confidence merge risks from this PR dependency change", "done");
|
|
218
|
-
expect(summary.
|
|
218
|
+
expect(summary.verdict?.posture).toBe("safe");
|
|
219
219
|
expect(summary.pipeline).toBeUndefined();
|
|
220
220
|
});
|
|
221
221
|
});
|
|
@@ -228,7 +228,7 @@ describe("resolvePrScanCardSummary", () => {
|
|
|
228
228
|
summaryText: "Waiting for results...",
|
|
229
229
|
result: null,
|
|
230
230
|
});
|
|
231
|
-
expect(summary.
|
|
231
|
+
expect(summary.verdict?.posture).toBe("risky");
|
|
232
232
|
expect(summary.pipeline).toBeUndefined();
|
|
233
233
|
});
|
|
234
234
|
});
|
package/package.json
CHANGED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import type { FindingCountSummary, PresentationConfidence, PresentationDensity, PresentationEvidenceContext, PresentationEvidenceRow, PresentationStatus } from "./types.js";
|
|
2
|
-
import type { PresentationIntent } from "../intent/presentationIntent.js";
|
|
3
|
-
export type ScanCardPresentation = {
|
|
4
|
-
pipeline?: {
|
|
5
|
-
status: "queued" | "running" | "failed";
|
|
6
|
-
headline: string;
|
|
7
|
-
subheadline?: string;
|
|
8
|
-
};
|
|
9
|
-
status?: PresentationStatus;
|
|
10
|
-
density?: PresentationDensity;
|
|
11
|
-
confidence?: PresentationConfidence;
|
|
12
|
-
evidenceContext?: PresentationEvidenceContext;
|
|
13
|
-
headline: string;
|
|
14
|
-
subheadline?: string;
|
|
15
|
-
changedPackages: string[];
|
|
16
|
-
primaryPackage?: string;
|
|
17
|
-
keyPoints: string[];
|
|
18
|
-
affectedAreas: string[];
|
|
19
|
-
verificationActions: string[];
|
|
20
|
-
evidence: PresentationEvidenceRow[];
|
|
21
|
-
supportingContext?: string[];
|
|
22
|
-
riskIndex?: number | null;
|
|
23
|
-
findingCounts?: FindingCountSummary | null;
|
|
24
|
-
actionLabel?: string;
|
|
25
|
-
presentationIntent?: PresentationIntent;
|
|
26
|
-
};
|
|
27
|
-
//# sourceMappingURL=scanCardPresentation.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"scanCardPresentation.d.ts","sourceRoot":"","sources":["../../../src/presentation/dto/scanCardPresentation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,mBAAmB,EACnB,sBAAsB,EACtB,mBAAmB,EACnB,2BAA2B,EAC3B,uBAAuB,EACvB,kBAAkB,EACnB,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AAE1E,MAAM,MAAM,oBAAoB,GAAG;IACjC,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,MAAM,CAAC,EAAE,kBAAkB,CAAC;IAC5B,OAAO,CAAC,EAAE,mBAAmB,CAAC;IAC9B,UAAU,CAAC,EAAE,sBAAsB,CAAC;IACpC,eAAe,CAAC,EAAE,2BAA2B,CAAC;IAC9C,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,QAAQ,EAAE,uBAAuB,EAAE,CAAC;IAEpC,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAE7B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,aAAa,CAAC,EAAE,mBAAmB,GAAG,IAAI,CAAC;IAC3C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kBAAkB,CAAC,EAAE,kBAAkB,CAAC;CACzC,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"presentPipelineScanCard.d.ts","sourceRoot":"","sources":["../../../src/presentation/presenters/presentPipelineScanCard.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAC;AAE3E,wBAAgB,uBAAuB,CACrC,cAAc,EAAE,QAAQ,GAAG,SAAS,GAAG,QAAQ,GAC9C,oBAAoB,CAqBtB"}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { scanSurfaceCopy } from "../../scanSurfaceCopy.js";
|
|
2
|
-
import { normalizeGeneratedText, } from "../../normalizeGeneratedText.js";
|
|
3
|
-
export function presentPipelineScanCard(pipelineStatus) {
|
|
4
|
-
const isRunning = pipelineStatus === "queued" || pipelineStatus === "running";
|
|
5
|
-
const headline = isRunning
|
|
6
|
-
? scanSurfaceCopy.pipeline.scanRunning
|
|
7
|
-
: scanSurfaceCopy.pipeline.analysisIncomplete;
|
|
8
|
-
return {
|
|
9
|
-
pipeline: {
|
|
10
|
-
status: pipelineStatus,
|
|
11
|
-
headline: normalizeGeneratedText(headline),
|
|
12
|
-
subheadline: isRunning
|
|
13
|
-
? scanSurfaceCopy.pipeline.scanIncomplete
|
|
14
|
-
: undefined,
|
|
15
|
-
},
|
|
16
|
-
headline: normalizeGeneratedText(headline),
|
|
17
|
-
changedPackages: [],
|
|
18
|
-
keyPoints: [],
|
|
19
|
-
affectedAreas: [],
|
|
20
|
-
verificationActions: [],
|
|
21
|
-
evidence: [],
|
|
22
|
-
};
|
|
23
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import type { ScanCardPresentation } from "../dto/scanCardPresentation.js";
|
|
2
|
-
import type { ScanPresentationBundle } from "../orchestration/scanPresentationBundle.js";
|
|
3
|
-
export type PresentScanCardContext = {
|
|
4
|
-
includeFindingCounts?: boolean;
|
|
5
|
-
};
|
|
6
|
-
export declare function presentScanCard(bundle: ScanPresentationBundle, ctx?: PresentScanCardContext): ScanCardPresentation;
|
|
7
|
-
//# sourceMappingURL=presentScanCard.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"presentScanCard.d.ts","sourceRoot":"","sources":["../../../src/presentation/presenters/presentScanCard.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAC;AAgB3E,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,4CAA4C,CAAC;AAEzF,MAAM,MAAM,sBAAsB,GAAG;IACnC,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC,CAAC;AAsBF,wBAAgB,eAAe,CAC7B,MAAM,EAAE,sBAAsB,EAC9B,GAAG,GAAE,sBAA2B,GAC/B,oBAAoB,CA0BtB"}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { aggregateFindingCounts } from "../dto/types.js";
|
|
2
|
-
import { composeAffectedAreaLabels, composeEvidenceRows, composeHeadline, composeKeyPoints, composeSubheadline, composeSupportingContext, composeVerificationActions, evidenceContextFromProfile, } from "../compose/narrativeCompose.js";
|
|
3
|
-
import { normalizeGeneratedText, normalizeGeneratedTextNullable, } from "../../normalizeGeneratedText.js";
|
|
4
|
-
import { scanSurfaceCopy } from "../../scanSurfaceCopy.js";
|
|
5
|
-
function normalizeCard(p) {
|
|
6
|
-
return {
|
|
7
|
-
...p,
|
|
8
|
-
headline: normalizeGeneratedText(p.headline),
|
|
9
|
-
subheadline: normalizeGeneratedTextNullable(p.subheadline) ?? undefined,
|
|
10
|
-
keyPoints: p.keyPoints.map((k) => normalizeGeneratedText(k)),
|
|
11
|
-
affectedAreas: p.affectedAreas.map((a) => normalizeGeneratedText(a)),
|
|
12
|
-
verificationActions: p.verificationActions.map((v) => normalizeGeneratedText(v)),
|
|
13
|
-
evidence: p.evidence.map((e) => ({
|
|
14
|
-
label: normalizeGeneratedText(e.label),
|
|
15
|
-
value: normalizeGeneratedText(e.value),
|
|
16
|
-
})),
|
|
17
|
-
supportingContext: p.supportingContext?.map((s) => normalizeGeneratedText(s)),
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
export function presentScanCard(bundle, ctx = {}) {
|
|
21
|
-
const { facts, profile, result } = bundle;
|
|
22
|
-
const presentation = {
|
|
23
|
-
status: profile.status,
|
|
24
|
-
density: profile.density,
|
|
25
|
-
confidence: profile.confidence,
|
|
26
|
-
evidenceContext: evidenceContextFromProfile(bundle),
|
|
27
|
-
headline: composeHeadline(bundle),
|
|
28
|
-
subheadline: composeSubheadline(bundle) ?? undefined,
|
|
29
|
-
changedPackages: facts.changedPackages.all,
|
|
30
|
-
primaryPackage: facts.changedPackages.primary ?? undefined,
|
|
31
|
-
keyPoints: composeKeyPoints(bundle, 3),
|
|
32
|
-
affectedAreas: composeAffectedAreaLabels(bundle, 2),
|
|
33
|
-
verificationActions: composeVerificationActions(bundle, 2),
|
|
34
|
-
evidence: composeEvidenceRows(bundle, 3),
|
|
35
|
-
supportingContext: composeSupportingContext(bundle),
|
|
36
|
-
riskIndex: facts.riskIndex,
|
|
37
|
-
findingCounts: ctx.includeFindingCounts
|
|
38
|
-
? aggregateFindingCounts(result.findings)
|
|
39
|
-
: null,
|
|
40
|
-
actionLabel: scanSurfaceCopy.presentation.actionLabelReview,
|
|
41
|
-
presentationIntent: profile.interpretation.intent,
|
|
42
|
-
};
|
|
43
|
-
return normalizeCard(presentation);
|
|
44
|
-
}
|
|
File without changes
|