@mergesignal/shared 0.12.3 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (178) hide show
  1. package/dist/assessment/authoredCommunication.d.ts +30 -0
  2. package/dist/assessment/authoredCommunication.d.ts.map +1 -0
  3. package/dist/assessment/authoredCommunication.js +18 -0
  4. package/dist/assessment/index.d.ts +6 -0
  5. package/dist/assessment/index.d.ts.map +1 -0
  6. package/dist/assessment/index.js +5 -0
  7. package/dist/assessment/literals.d.ts +44 -0
  8. package/dist/assessment/literals.d.ts.map +1 -0
  9. package/dist/assessment/literals.js +140 -0
  10. package/dist/assessment/schema.d.ts +311 -0
  11. package/dist/assessment/schema.d.ts.map +1 -0
  12. package/dist/assessment/schema.js +162 -0
  13. package/dist/assessment/types.d.ts +197 -0
  14. package/dist/assessment/types.d.ts.map +1 -0
  15. package/dist/assessment/types.js +1 -0
  16. package/dist/assessment/version.d.ts +11 -0
  17. package/dist/assessment/version.d.ts.map +1 -0
  18. package/dist/assessment/version.js +9 -0
  19. package/dist/assessmentLabels.d.ts +1 -1
  20. package/dist/assessmentLabels.d.ts.map +1 -1
  21. package/dist/assessmentLabels.js +2 -2
  22. package/dist/assessmentPresentationUtils.d.ts +1 -1
  23. package/dist/assessmentPresentationUtils.d.ts.map +1 -1
  24. package/dist/assessmentProjection.d.ts +1 -1
  25. package/dist/assessmentProjection.d.ts.map +1 -1
  26. package/dist/assessmentProjection.js +2 -2
  27. package/dist/deriveScanNarrative.d.ts.map +1 -1
  28. package/dist/deriveScanNarrative.js +2 -1
  29. package/dist/fixtures/assessmentFixtures.d.ts +1 -1
  30. package/dist/fixtures/assessmentFixtures.js +12 -12
  31. package/dist/fixtures/assessmentScopeFixtures.d.ts +1 -1
  32. package/dist/index.d.ts +2 -0
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js +2 -0
  35. package/dist/lockfileEvidence.d.ts +22 -0
  36. package/dist/lockfileEvidence.d.ts.map +1 -0
  37. package/dist/lockfileEvidence.js +32 -0
  38. package/dist/presentation/dto/assessmentPresentationFields.d.ts +1 -1
  39. package/dist/presentation/dto/assessmentPresentationFields.d.ts.map +1 -1
  40. package/dist/presentation/orchestration/buildScanPresentationBundle.d.ts.map +1 -1
  41. package/dist/presentation/orchestration/buildScanPresentationBundle.js +2 -1
  42. package/dist/presentation/orchestration/scanPresentationBundle.d.ts +2 -1
  43. package/dist/presentation/orchestration/scanPresentationBundle.d.ts.map +1 -1
  44. package/dist/presentation/profile/buildProfileFromAssessment.d.ts +1 -1
  45. package/dist/presentation/profile/buildProfileFromAssessment.d.ts.map +1 -1
  46. package/dist/presentation/profile/buildProfileFromAssessment.js +14 -6
  47. package/dist/repoIntelligenceSchema.d.ts +5 -5
  48. package/dist/scanResultSchema.d.ts +8 -8
  49. package/dist/scanResultSchema.js +1 -1
  50. package/dist/scanSurfaceCopy.d.ts +1 -0
  51. package/dist/scanSurfaceCopy.d.ts.map +1 -1
  52. package/dist/scanSurfaceCopy.js +1 -0
  53. package/dist/types.d.ts +6 -2
  54. package/dist/types.d.ts.map +1 -1
  55. package/package.json +2 -3
  56. package/dist/actionsStepSummary.test.d.ts +0 -2
  57. package/dist/actionsStepSummary.test.d.ts.map +0 -1
  58. package/dist/actionsStepSummary.test.js +0 -53
  59. package/dist/assessmentProjection.verificationChannel.test.d.ts +0 -2
  60. package/dist/assessmentProjection.verificationChannel.test.d.ts.map +0 -1
  61. package/dist/assessmentProjection.verificationChannel.test.js +0 -110
  62. package/dist/deriveScanNarrative.test.d.ts +0 -2
  63. package/dist/deriveScanNarrative.test.d.ts.map +0 -1
  64. package/dist/deriveScanNarrative.test.js +0 -274
  65. package/dist/engineWireRegression.test.d.ts +0 -2
  66. package/dist/engineWireRegression.test.d.ts.map +0 -1
  67. package/dist/engineWireRegression.test.js +0 -77
  68. package/dist/fixtures/engineAbi4Fixtures.d.ts +0 -16
  69. package/dist/fixtures/engineAbi4Fixtures.d.ts.map +0 -1
  70. package/dist/fixtures/engineAbi4Fixtures.js +0 -26
  71. package/dist/fixtures/legacyEngineRepoIntelligence.d.ts +0 -28
  72. package/dist/fixtures/legacyEngineRepoIntelligence.d.ts.map +0 -1
  73. package/dist/fixtures/legacyEngineRepoIntelligence.js +0 -28
  74. package/dist/fixtures/repoIntelligenceFixtures.d.ts +0 -8
  75. package/dist/fixtures/repoIntelligenceFixtures.d.ts.map +0 -1
  76. package/dist/fixtures/repoIntelligenceFixtures.js +0 -123
  77. package/dist/fixtures/repoIntelligenceTestHelpers.d.ts +0 -5
  78. package/dist/fixtures/repoIntelligenceTestHelpers.d.ts.map +0 -1
  79. package/dist/fixtures/repoIntelligenceTestHelpers.js +0 -28
  80. package/dist/formatInsight.test.d.ts +0 -2
  81. package/dist/formatInsight.test.d.ts.map +0 -1
  82. package/dist/formatInsight.test.js +0 -102
  83. package/dist/narrativeParity.test.d.ts +0 -2
  84. package/dist/narrativeParity.test.d.ts.map +0 -1
  85. package/dist/narrativeParity.test.js +0 -35
  86. package/dist/narrativePresentation.test.d.ts +0 -2
  87. package/dist/narrativePresentation.test.d.ts.map +0 -1
  88. package/dist/narrativePresentation.test.js +0 -54
  89. package/dist/normalizeGeneratedText.test.d.ts +0 -2
  90. package/dist/normalizeGeneratedText.test.d.ts.map +0 -1
  91. package/dist/normalizeGeneratedText.test.js +0 -18
  92. package/dist/prRiskBand.test.d.ts +0 -2
  93. package/dist/prRiskBand.test.d.ts.map +0 -1
  94. package/dist/prRiskBand.test.js +0 -68
  95. package/dist/prRiskWire.test.d.ts +0 -2
  96. package/dist/prRiskWire.test.d.ts.map +0 -1
  97. package/dist/prRiskWire.test.js +0 -106
  98. package/dist/prRiskWiringRegression.test.d.ts +0 -2
  99. package/dist/prRiskWiringRegression.test.d.ts.map +0 -1
  100. package/dist/prRiskWiringRegression.test.js +0 -102
  101. package/dist/presentation/abi3Expression.test.d.ts +0 -13
  102. package/dist/presentation/abi3Expression.test.d.ts.map +0 -1
  103. package/dist/presentation/abi3Expression.test.js +0 -392
  104. package/dist/presentation/buildGitHubCheckRunOutput.test.d.ts +0 -2
  105. package/dist/presentation/buildGitHubCheckRunOutput.test.d.ts.map +0 -1
  106. package/dist/presentation/buildGitHubCheckRunOutput.test.js +0 -163
  107. package/dist/presentation/bundleAccessorParity.test.d.ts +0 -9
  108. package/dist/presentation/bundleAccessorParity.test.d.ts.map +0 -1
  109. package/dist/presentation/bundleAccessorParity.test.js +0 -126
  110. package/dist/presentation/cliParity.test.d.ts +0 -2
  111. package/dist/presentation/cliParity.test.d.ts.map +0 -1
  112. package/dist/presentation/cliParity.test.js +0 -49
  113. package/dist/presentation/compose/narrativeChannels.test.d.ts +0 -2
  114. package/dist/presentation/compose/narrativeChannels.test.d.ts.map +0 -1
  115. package/dist/presentation/compose/narrativeChannels.test.js +0 -42
  116. package/dist/presentation/decisionSurface.test.d.ts +0 -2
  117. package/dist/presentation/decisionSurface.test.d.ts.map +0 -1
  118. package/dist/presentation/decisionSurface.test.js +0 -290
  119. package/dist/presentation/fixtures/dashboardCardGolden.test.d.ts +0 -2
  120. package/dist/presentation/fixtures/dashboardCardGolden.test.d.ts.map +0 -1
  121. package/dist/presentation/fixtures/dashboardCardGolden.test.js +0 -38
  122. package/dist/presentation/fixtures/presentationPersonaFixtures.d.ts +0 -11
  123. package/dist/presentation/fixtures/presentationPersonaFixtures.d.ts.map +0 -1
  124. package/dist/presentation/fixtures/presentationPersonaFixtures.js +0 -208
  125. package/dist/presentation/fixtures/scanResultFixtures.d.ts +0 -5
  126. package/dist/presentation/fixtures/scanResultFixtures.d.ts.map +0 -1
  127. package/dist/presentation/fixtures/scanResultFixtures.js +0 -123
  128. package/dist/presentation/intent/postureVocabularyGuard.test.d.ts +0 -2
  129. package/dist/presentation/intent/postureVocabularyGuard.test.d.ts.map +0 -1
  130. package/dist/presentation/intent/postureVocabularyGuard.test.js +0 -23
  131. package/dist/presentation/orchestration/buildScanPresentationBundle.test.d.ts +0 -2
  132. package/dist/presentation/orchestration/buildScanPresentationBundle.test.d.ts.map +0 -1
  133. package/dist/presentation/orchestration/buildScanPresentationBundle.test.js +0 -99
  134. package/dist/presentation/presentation.test.d.ts +0 -2
  135. package/dist/presentation/presentation.test.d.ts.map +0 -1
  136. package/dist/presentation/presentation.test.js +0 -126
  137. package/dist/presentation/presentationPersonas.test.d.ts +0 -2
  138. package/dist/presentation/presentationPersonas.test.d.ts.map +0 -1
  139. package/dist/presentation/presentationPersonas.test.js +0 -164
  140. package/dist/presentation/surfaceParity.test.d.ts +0 -2
  141. package/dist/presentation/surfaceParity.test.d.ts.map +0 -1
  142. package/dist/presentation/surfaceParity.test.js +0 -156
  143. package/dist/presentation/verificationChannelParity.test.d.ts +0 -2
  144. package/dist/presentation/verificationChannelParity.test.d.ts.map +0 -1
  145. package/dist/presentation/verificationChannelParity.test.js +0 -57
  146. package/dist/productMessaging.test.d.ts +0 -2
  147. package/dist/productMessaging.test.d.ts.map +0 -1
  148. package/dist/productMessaging.test.js +0 -97
  149. package/dist/repoIntelligenceSchema.contract.test.d.ts +0 -2
  150. package/dist/repoIntelligenceSchema.contract.test.d.ts.map +0 -1
  151. package/dist/repoIntelligenceSchema.contract.test.js +0 -51
  152. package/dist/repoIntelligenceSemantics.test.d.ts +0 -2
  153. package/dist/repoIntelligenceSemantics.test.d.ts.map +0 -1
  154. package/dist/repoIntelligenceSemantics.test.js +0 -63
  155. package/dist/repoIntelligenceValidation.persist.test.d.ts +0 -2
  156. package/dist/repoIntelligenceValidation.persist.test.d.ts.map +0 -1
  157. package/dist/repoIntelligenceValidation.persist.test.js +0 -44
  158. package/dist/riskSignals.test.d.ts +0 -2
  159. package/dist/riskSignals.test.d.ts.map +0 -1
  160. package/dist/riskSignals.test.js +0 -32
  161. package/dist/riskVocabulary.test.d.ts +0 -2
  162. package/dist/riskVocabulary.test.d.ts.map +0 -1
  163. package/dist/riskVocabulary.test.js +0 -14
  164. package/dist/scanPresentationBundle.golden.test.d.ts +0 -2
  165. package/dist/scanPresentationBundle.golden.test.d.ts.map +0 -1
  166. package/dist/scanPresentationBundle.golden.test.js +0 -78
  167. package/dist/scanResultSchema.test.d.ts +0 -2
  168. package/dist/scanResultSchema.test.d.ts.map +0 -1
  169. package/dist/scanResultSchema.test.js +0 -162
  170. package/dist/scanSurfaceCopy.test.d.ts +0 -2
  171. package/dist/scanSurfaceCopy.test.d.ts.map +0 -1
  172. package/dist/scanSurfaceCopy.test.js +0 -14
  173. package/dist/selectTopAffectedAreas.test.d.ts +0 -2
  174. package/dist/selectTopAffectedAreas.test.d.ts.map +0 -1
  175. package/dist/selectTopAffectedAreas.test.js +0 -179
  176. package/dist/trustedScanGuards.test.d.ts +0 -2
  177. package/dist/trustedScanGuards.test.d.ts.map +0 -1
  178. package/dist/trustedScanGuards.test.js +0 -151
@@ -1,44 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { deriveScanNarrative } from "./deriveScanNarrative.js";
3
- import { legacyEngineRepoIntelligenceJwt } from "./fixtures/legacyEngineRepoIntelligence.js";
4
- import { applyRepoIntelligenceValidation } from "./repoIntelligenceValidation.js";
5
- const baseScan = {
6
- totalScore: 40,
7
- layerScores: {
8
- security: 10,
9
- maintainability: 10,
10
- ecosystem: 10,
11
- upgradeImpact: 10,
12
- },
13
- findings: [],
14
- generatedAt: "2026-01-01T00:00:00.000Z",
15
- changedPackages: ["jsonwebtoken"],
16
- analysisPreparation: {
17
- codeIntelligenceAvailable: true,
18
- warnings: [],
19
- },
20
- };
21
- describe("applyRepoIntelligenceValidation (production persist model)", () => {
22
- it("retains raw invalid repoIntelligence and marks status invalid", () => {
23
- const { result, contractFailed, logPayload } = applyRepoIntelligenceValidation({
24
- ...baseScan,
25
- repoIntelligence: legacyEngineRepoIntelligenceJwt,
26
- });
27
- expect(contractFailed).toBe(true);
28
- expect(logPayload?.issueCount).toBeGreaterThan(0);
29
- expect(result.repoIntelligence).toEqual(legacyEngineRepoIntelligenceJwt);
30
- expect(result.analysisPreparation?.repoIntelligenceValidation?.status).toBe("invalid");
31
- expect(result.analysisPreparation?.warnings.some((w) => w.code === "repo_intelligence_contract_invalid")).toBe(true);
32
- });
33
- it("deriveScanNarrative ignores invalid wire (graph_fallback, untrusted parse status)", () => {
34
- const { result } = applyRepoIntelligenceValidation({
35
- ...baseScan,
36
- repoIntelligence: legacyEngineRepoIntelligenceJwt,
37
- });
38
- const facts = deriveScanNarrative(result);
39
- expect(facts.availability.repoIntelligenceParse).toBe("untrusted");
40
- expect(facts.availability.tiersPresent.tier1).toBe(false);
41
- expect(facts.availability.mode).not.toBe("pr_intelligence");
42
- expect(facts.packageUsage).toHaveLength(0);
43
- });
44
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=riskSignals.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"riskSignals.test.d.ts","sourceRoot":"","sources":["../src/riskSignals.test.ts"],"names":[],"mappings":""}
@@ -1,32 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { deriveRiskSignals } from "./riskSignals.js";
3
- import { resolveRepositoryHealthScore } from "./prRiskWire.js";
4
- function baseResult(overrides = {}) {
5
- return {
6
- totalScore: 63,
7
- layerScores: {
8
- security: 10,
9
- maintainability: 20,
10
- ecosystem: 30,
11
- upgradeImpact: 15,
12
- },
13
- findings: [],
14
- generatedAt: "2026-01-01T00:00:00.000Z",
15
- ...overrides,
16
- };
17
- }
18
- describe("deriveRiskSignals", () => {
19
- it("prefers prRisk.score over totalScore and repositoryHealth", () => {
20
- const result = baseResult({
21
- prRisk: { score: 55 },
22
- repositoryHealth: { totalScore: 63 },
23
- totalScore: 63,
24
- });
25
- expect(deriveRiskSignals(result)?.riskIndex).toBe(55);
26
- expect(resolveRepositoryHealthScore(result)).toBe(63);
27
- });
28
- it("falls back to totalScore when prRisk is missing (historical scans)", () => {
29
- const result = baseResult({ totalScore: 48 });
30
- expect(deriveRiskSignals(result)?.riskIndex).toBe(48);
31
- });
32
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=riskVocabulary.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"riskVocabulary.test.d.ts","sourceRoot":"","sources":["../src/riskVocabulary.test.ts"],"names":[],"mappings":""}
@@ -1,14 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { mergePostureFromDecision } from "./riskVocabulary.js";
3
- describe("mergePostureFromDecision", () => {
4
- it("accepts only canonical lowercase merge posture tokens", () => {
5
- expect(mergePostureFromDecision("safe")).toBe("safe");
6
- expect(mergePostureFromDecision("needs_review")).toBe("needs_review");
7
- expect(mergePostureFromDecision("risky")).toBe("risky");
8
- });
9
- it("rejects alternate engine spellings (no silent normalization)", () => {
10
- expect(mergePostureFromDecision("SAFE")).toBeNull();
11
- expect(mergePostureFromDecision("RISKY")).toBeNull();
12
- expect(mergePostureFromDecision("BLOCK")).toBeNull();
13
- });
14
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=scanPresentationBundle.golden.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"scanPresentationBundle.golden.test.d.ts","sourceRoot":"","sources":["../src/scanPresentationBundle.golden.test.ts"],"names":[],"mappings":""}
@@ -1,78 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { scoreToBand } from "./prRiskBand.js";
3
- import { buildScanPresentationBundle } from "./presentation/orchestration/buildScanPresentationBundle.js";
4
- import { scanResultBullmq, scanResultEslint, scanResultFastifyRuntime, scanResultLimitedContext, scanResultMixedTypescriptFastify, scanResultPrettier, scanResultTypescriptPatch, scanResultVitest, } from "./presentation/fixtures/presentationPersonaFixtures.js";
5
- const validationPersonas = [
6
- { id: "fastify-runtime", result: scanResultFastifyRuntime },
7
- { id: "typescript-patch", result: scanResultTypescriptPatch },
8
- { id: "mixed-ts-fastify", result: scanResultMixedTypescriptFastify },
9
- { id: "prettier", result: scanResultPrettier },
10
- { id: "bullmq", result: scanResultBullmq },
11
- { id: "eslint", result: scanResultEslint },
12
- { id: "vitest", result: scanResultVitest },
13
- { id: "limited-context", result: scanResultLimitedContext },
14
- ];
15
- function bundleFor(result) {
16
- return buildScanPresentationBundle({
17
- result,
18
- pipelineStatus: "done",
19
- });
20
- }
21
- describe("scanPresentationBundle golden — proof model", () => {
22
- it.each(validationPersonas)("builds bundle for $id", ({ result }) => {
23
- const bundle = bundleFor(result);
24
- expect(bundle).not.toBeNull();
25
- expect(bundle.facts.availability.corpusGateReason).toBeDefined();
26
- expect(bundle.facts.confidence).toMatchObject({
27
- limitedContext: expect.any(Boolean),
28
- });
29
- expect(bundle.facts.riskSignals).not.toBeNull();
30
- expect(bundle.facts.riskSignals.layers).toHaveLength(4);
31
- });
32
- it("fastify-runtime — linkage and diagnostics", () => {
33
- const bundle = bundleFor(scanResultFastifyRuntime);
34
- const { facts } = bundle;
35
- expect(facts.availability.corpusGateReason).toBe("ok");
36
- expect(facts.confidence.limitedContext).toBe(false);
37
- expect(facts.confidence.assessment).toBe("medium");
38
- const apiArea = facts.affectedAreas.find((a) => a.id === "api");
39
- expect(apiArea?.packages).toContain("fastify");
40
- expect(apiArea?.findingIds).toContain("finding-fastify-runtime");
41
- expect(apiArea?.paths.length).toBeGreaterThan(0);
42
- expect(apiArea?.hotspotPackages).toContain("fastify");
43
- expect(apiArea?.verificationFocus.length).toBeGreaterThan(0);
44
- expect(facts.riskSignals?.riskIndex).toBe(55);
45
- expect(facts.riskSignals?.band).toBe(scoreToBand(55));
46
- expect(facts.riskIndex).toBe(facts.riskSignals?.riskIndex);
47
- });
48
- it("limited-context — diagnostics without synthetic confidence", () => {
49
- const bundle = bundleFor(scanResultLimitedContext);
50
- const { facts } = bundle;
51
- expect(facts.availability.corpusGateReason).toBe("no_code_intelligence");
52
- expect(facts.availability.preparationWarnings.some((w) => w.code === "base_lockfile_missing")).toBe(true);
53
- expect(facts.confidence.assessment).toBe("low");
54
- expect(facts.confidence.limitedContext).toBe(true);
55
- });
56
- it("typescript-patch — tooling semantics with risk signals", () => {
57
- const bundle = bundleFor(scanResultTypescriptPatch);
58
- const { facts } = bundle;
59
- expect(facts.availability.corpusGateReason).toBe("ok");
60
- expect(facts.packageSemantics?.dependencyClass).toBe("tooling");
61
- expect(facts.riskSignals?.riskIndex).toBe(30);
62
- expect(facts.riskSignals?.band).toBe(scoreToBand(30));
63
- });
64
- it("affected areas always carry linkage arrays (structure)", () => {
65
- for (const { result } of validationPersonas) {
66
- const bundle = bundleFor(result);
67
- if (!bundle)
68
- continue;
69
- for (const area of bundle.facts.affectedAreas) {
70
- expect(Array.isArray(area.packages)).toBe(true);
71
- expect(Array.isArray(area.findingIds)).toBe(true);
72
- expect(Array.isArray(area.paths)).toBe(true);
73
- expect(Array.isArray(area.hotspotPackages)).toBe(true);
74
- expect(Array.isArray(area.verificationFocus)).toBe(true);
75
- }
76
- }
77
- });
78
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=scanResultSchema.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"scanResultSchema.test.d.ts","sourceRoot":"","sources":["../src/scanResultSchema.test.ts"],"names":[],"mappings":""}
@@ -1,162 +0,0 @@
1
- import { describe, it, expect } from "vitest";
2
- import { safeParseScanResult, parseScanResultOrThrow, scanResultSchema, safeParseEngineOutputScanResult, parseEngineOutputScanResultOrThrow, } from "./scanResultSchema.js";
3
- import { emptyReachScope, emptyVerificationScope, minimalReviewFocalPoint, } from "./fixtures/assessmentScopeFixtures.js";
4
- import { withAbi4EngineScores } from "./fixtures/engineAbi4Fixtures.js";
5
- const minimalValid = {
6
- totalScore: 42,
7
- layerScores: {
8
- security: 10,
9
- maintainability: 20,
10
- ecosystem: 30,
11
- upgradeImpact: 40,
12
- },
13
- findings: [],
14
- generatedAt: "2026-01-01T00:00:00.000Z",
15
- };
16
- describe("scanResultSchema", () => {
17
- it("accepts a minimal valid payload", () => {
18
- const r = safeParseScanResult(minimalValid);
19
- expect(r.ok).toBe(true);
20
- if (r.ok)
21
- expect(r.result.totalScore).toBe(42);
22
- });
23
- it("preserves unknown top-level keys (passthrough)", () => {
24
- const r = safeParseScanResult({
25
- ...minimalValid,
26
- futureEngineField: { x: 1 },
27
- });
28
- expect(r.ok).toBe(true);
29
- if (r.ok)
30
- expect(r.result.futureEngineField).toEqual({
31
- x: 1,
32
- });
33
- });
34
- it("rejects missing layerScores", () => {
35
- const r = safeParseScanResult({
36
- totalScore: 1,
37
- findings: [],
38
- generatedAt: "2026-01-01T00:00:00.000Z",
39
- });
40
- expect(r.ok).toBe(false);
41
- });
42
- it("rejects out-of-range totalScore", () => {
43
- const r = safeParseScanResult({
44
- ...minimalValid,
45
- totalScore: 101,
46
- });
47
- expect(r.ok).toBe(false);
48
- });
49
- it("rejects invalid decision.recommendation", () => {
50
- const r = safeParseScanResult({
51
- ...minimalValid,
52
- decision: { recommendation: "maybe", reasoning: [] },
53
- });
54
- expect(r.ok).toBe(false);
55
- });
56
- it("rejects non-canonical merge posture tokens (e.g. proprietary uppercase)", () => {
57
- const r = safeParseScanResult({
58
- ...minimalValid,
59
- decision: { recommendation: "SAFE", reasoning: [] },
60
- });
61
- expect(r.ok).toBe(false);
62
- });
63
- it("rejects empty generatedAt", () => {
64
- const r = safeParseScanResult({
65
- ...minimalValid,
66
- generatedAt: "",
67
- });
68
- expect(r.ok).toBe(false);
69
- });
70
- it("parseScanResultOrThrow returns on success", () => {
71
- expect(parseScanResultOrThrow(minimalValid).totalScore).toBe(42);
72
- });
73
- it("parseScanResultOrThrow throws with validation prefix", () => {
74
- expect(() => parseScanResultOrThrow({})).toThrow(/^validation:/);
75
- });
76
- it("scanResultSchema default empty findings when omitted", () => {
77
- const { findings: _f, ...rest } = minimalValid;
78
- const parsed = scanResultSchema.parse(rest);
79
- expect(parsed.findings).toEqual([]);
80
- });
81
- });
82
- const minimalAssessment = {
83
- reviewFocalPoint: minimalReviewFocalPoint(["typescript"]),
84
- reachScope: emptyReachScope(),
85
- verificationScope: emptyVerificationScope(),
86
- posture: "safe",
87
- confidence: "high",
88
- primaryConcern: null,
89
- concerns: [],
90
- factors: ["tooling_maintenance"],
91
- changeClasses: ["tooling_maintenance"],
92
- reasoning: [],
93
- confidenceRationale: "",
94
- presentation: {
95
- narrativeIntensity: "minimal",
96
- reachVisibility: "hidden",
97
- verificationIntensity: "advisory",
98
- insightEmissionFloor: "none",
99
- reportMode: "high_signal_pr",
100
- },
101
- };
102
- describe("engineOutputScanResultSchema (strict, fresh engine only)", () => {
103
- const withMethodology = withAbi4EngineScores({
104
- ...minimalValid,
105
- methodologyVersion: "engine-test-fixture/v1",
106
- assessment: minimalAssessment,
107
- });
108
- it("rejects payload that relaxed parser accepts when methodology is missing", () => {
109
- const r = safeParseEngineOutputScanResult(minimalValid);
110
- expect(r.ok).toBe(false);
111
- });
112
- it("accepts when methodology and parseable generatedAt are present", () => {
113
- const r = safeParseEngineOutputScanResult(withMethodology);
114
- expect(r.ok).toBe(true);
115
- if (r.ok)
116
- expect(r.result.methodologyVersion).toBe("engine-test-fixture/v1");
117
- });
118
- it("synthesizes prRisk/repositoryHealth from totalScore for legacy engines", () => {
119
- const r = safeParseEngineOutputScanResult({
120
- ...minimalValid,
121
- methodologyVersion: "acme-prod/v1",
122
- assessment: minimalAssessment,
123
- });
124
- expect(r.ok).toBe(true);
125
- if (r.ok) {
126
- expect(r.result.prRisk?.score).toBe(42);
127
- expect(r.result.repositoryHealth?.totalScore).toBe(42);
128
- }
129
- });
130
- it("parseEngineOutputScanResultOrThrow throws with validation prefix", () => {
131
- expect(() => parseEngineOutputScanResultOrThrow(minimalValid)).toThrow(/^validation:/);
132
- });
133
- it("rejects unparseable generatedAt", () => {
134
- const r = safeParseEngineOutputScanResult({
135
- ...withMethodology,
136
- generatedAt: "not-a-date",
137
- });
138
- expect(r.ok).toBe(false);
139
- });
140
- it("rejects ABI-1 assessment missing focal/scope fields on fresh engine path", () => {
141
- const legacyAssessment = {
142
- posture: "safe",
143
- confidence: "high",
144
- primaryConcern: null,
145
- concerns: [],
146
- factors: ["tooling_maintenance"],
147
- changeClasses: ["tooling_maintenance"],
148
- presentation: {
149
- narrativeIntensity: "minimal",
150
- reachVisibility: "hidden",
151
- verificationIntensity: "advisory",
152
- insightEmissionFloor: "none",
153
- reportMode: "high_signal_pr",
154
- },
155
- };
156
- const r = safeParseEngineOutputScanResult({
157
- ...withMethodology,
158
- assessment: legacyAssessment,
159
- });
160
- expect(r.ok).toBe(false);
161
- });
162
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=scanSurfaceCopy.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"scanSurfaceCopy.test.d.ts","sourceRoot":"","sources":["../src/scanSurfaceCopy.test.ts"],"names":[],"mappings":""}
@@ -1,14 +0,0 @@
1
- import { describe, it, expect } from "vitest";
2
- import { readFileSync } from "node:fs";
3
- import { fileURLToPath } from "node:url";
4
- import { dirname, join } from "node:path";
5
- import { scanSurfaceCopyFlat } from "./scanSurfaceCopy.js";
6
- describe("scanSurfaceCopy", () => {
7
- it("matches generated CI JSON (no drift)", () => {
8
- const root = join(dirname(fileURLToPath(import.meta.url)), "..", "..", "..");
9
- const jsonPath = join(root, "scripts/ci/scan-surface-copy.generated.json");
10
- const onDisk = JSON.parse(readFileSync(jsonPath, "utf8"));
11
- const fromTs = scanSurfaceCopyFlat();
12
- expect(fromTs).toEqual(onDisk);
13
- });
14
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=selectTopAffectedAreas.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"selectTopAffectedAreas.test.d.ts","sourceRoot":"","sources":["../src/selectTopAffectedAreas.test.ts"],"names":[],"mappings":""}
@@ -1,179 +0,0 @@
1
- import { describe, it, expect } from "vitest";
2
- import { selectTopAffectedAreas } from "./selectTopAffectedAreas.js";
3
- function makeScanResult(overrides = {}) {
4
- return {
5
- totalScore: 60,
6
- layerScores: {
7
- security: 70,
8
- maintainability: 50,
9
- ecosystem: 40,
10
- upgradeImpact: 30,
11
- },
12
- findings: [],
13
- generatedAt: "2026-01-01T00:00:00Z",
14
- ...overrides,
15
- };
16
- }
17
- describe("selectTopAffectedAreas", () => {
18
- it("returns [] for null input", () => {
19
- expect(selectTopAffectedAreas(null)).toEqual([]);
20
- });
21
- it("returns [] for undefined input", () => {
22
- expect(selectTopAffectedAreas(undefined)).toEqual([]);
23
- });
24
- it("returns at most 3 results by default", () => {
25
- const result = makeScanResult({
26
- explain: {
27
- reasons: [
28
- { id: "r1", layer: "security", title: "Auth flow", scoreImpact: -20 },
29
- {
30
- id: "r2",
31
- layer: "security",
32
- title: "State sync",
33
- scoreImpact: -15,
34
- },
35
- {
36
- id: "r3",
37
- layer: "ecosystem",
38
- title: "Middleware",
39
- scoreImpact: -10,
40
- },
41
- {
42
- id: "r4",
43
- layer: "maintainability",
44
- title: "Extra area",
45
- scoreImpact: -5,
46
- },
47
- ],
48
- },
49
- });
50
- const areas = selectTopAffectedAreas(result);
51
- expect(areas.length).toBe(3);
52
- expect(areas).toEqual(["Auth flow", "State sync", "Middleware"]);
53
- });
54
- it("respects custom max", () => {
55
- const result = makeScanResult({
56
- explain: {
57
- reasons: [
58
- { id: "r1", layer: "security", title: "Area A", scoreImpact: -20 },
59
- { id: "r2", layer: "security", title: "Area B", scoreImpact: -15 },
60
- ],
61
- },
62
- });
63
- expect(selectTopAffectedAreas(result, { max: 1 })).toEqual(["Area A"]);
64
- });
65
- it("sorts explain reasons by absolute scoreImpact descending", () => {
66
- const result = makeScanResult({
67
- explain: {
68
- reasons: [
69
- { id: "r1", layer: "security", title: "Minor", scoreImpact: -2 },
70
- { id: "r2", layer: "security", title: "Critical", scoreImpact: -30 },
71
- { id: "r3", layer: "ecosystem", title: "Medium", scoreImpact: -15 },
72
- ],
73
- },
74
- });
75
- const areas = selectTopAffectedAreas(result);
76
- expect(areas[0]).toBe("Critical");
77
- expect(areas[1]).toBe("Medium");
78
- });
79
- it("falls back to insights when explain is empty", () => {
80
- const result = makeScanResult({
81
- insights: [
82
- {
83
- type: "behavioral_change",
84
- priority: "high",
85
- confidence: "confirmed",
86
- scope: "changed",
87
- message: "Auth middleware changed. Check tokens",
88
- context: "src/auth.ts",
89
- remediation: "Review carefully",
90
- },
91
- ],
92
- });
93
- const areas = selectTopAffectedAreas(result);
94
- expect(areas).toEqual(["Auth middleware changed"]);
95
- });
96
- it("falls back to layer names when explain and insights are empty", () => {
97
- const result = makeScanResult();
98
- const areas = selectTopAffectedAreas(result);
99
- expect(areas.length).toBeGreaterThan(0);
100
- expect(areas.length).toBeLessThanOrEqual(3);
101
- expect(areas).toContain("Security");
102
- });
103
- it("deduplicates normalized labels", () => {
104
- const result = makeScanResult({
105
- explain: {
106
- reasons: [
107
- { id: "r1", layer: "security", title: "Auth flow", scoreImpact: -20 },
108
- { id: "r2", layer: "security", title: "auth flow", scoreImpact: -15 },
109
- {
110
- id: "r3",
111
- layer: "ecosystem",
112
- title: "Auth flow",
113
- scoreImpact: -10,
114
- },
115
- ],
116
- },
117
- });
118
- const areas = selectTopAffectedAreas(result);
119
- const unique = new Set(areas.map((a) => a.toLowerCase()));
120
- expect(unique.size).toBe(areas.length);
121
- expect(areas).toContain("Auth flow");
122
- expect(areas.filter((a) => a.toLowerCase() === "auth flow").length).toBe(1);
123
- });
124
- it("trims the 'Finding: ' prefix from labels", () => {
125
- const result = makeScanResult({
126
- explain: {
127
- reasons: [
128
- {
129
- id: "r1",
130
- layer: "security",
131
- title: "Finding: Outdated lodash",
132
- scoreImpact: -20,
133
- },
134
- ],
135
- },
136
- });
137
- const areas = selectTopAffectedAreas(result);
138
- expect(areas[0]).toBe("Outdated lodash");
139
- });
140
- it("truncates labels longer than maxLabelLen with ellipsis", () => {
141
- const long = "A".repeat(50);
142
- const result = makeScanResult({
143
- explain: {
144
- reasons: [
145
- { id: "r1", layer: "security", title: long, scoreImpact: -10 },
146
- ],
147
- },
148
- });
149
- const areas = selectTopAffectedAreas(result, { maxLabelLen: 10 });
150
- expect(areas[0].length).toBeLessThanOrEqual(10);
151
- expect(areas[0].endsWith("…")).toBe(true);
152
- });
153
- it("returns empty array when no useful signal at all", () => {
154
- // Completely empty scan result with empty layerScores
155
- const result = {
156
- totalScore: 0,
157
- layerScores: {
158
- security: 0,
159
- maintainability: 0,
160
- ecosystem: 0,
161
- upgradeImpact: 0,
162
- },
163
- findings: [],
164
- generatedAt: "2026-01-01T00:00:00Z",
165
- };
166
- // With layer fallback this would return layer names; only truly empty when
167
- // caller passes a result without layerScores entirely — validate max guard
168
- const areas = selectTopAffectedAreas(result, { max: 3 });
169
- expect(areas.length).toBeLessThanOrEqual(3);
170
- });
171
- it("returns [] when layerScores is missing and no explain/insights", () => {
172
- const partial = {
173
- totalScore: 10,
174
- findings: [],
175
- generatedAt: "2026-01-01T00:00:00Z",
176
- };
177
- expect(selectTopAffectedAreas(partial)).toEqual([]);
178
- });
179
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=trustedScanGuards.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"trustedScanGuards.test.d.ts","sourceRoot":"","sources":["../src/trustedScanGuards.test.ts"],"names":[],"mappings":""}