@mergesignal/shared 0.2.8 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/deriveScanNarrative.d.ts +0 -3
- package/dist/deriveScanNarrative.d.ts.map +1 -1
- package/dist/deriveScanNarrative.js +29 -18
- package/dist/deriveScanNarrative.test.js +37 -8
- package/dist/engineWireRegression.test.d.ts +2 -0
- package/dist/engineWireRegression.test.d.ts.map +1 -0
- package/dist/engineWireRegression.test.js +57 -0
- package/dist/fixtures/legacyEngineRepoIntelligence.d.ts +28 -0
- package/dist/fixtures/legacyEngineRepoIntelligence.d.ts.map +1 -0
- package/dist/fixtures/legacyEngineRepoIntelligence.js +28 -0
- package/dist/fixtures/repoIntelligenceFixtures.d.ts +0 -2
- package/dist/fixtures/repoIntelligenceFixtures.d.ts.map +1 -1
- package/dist/fixtures/repoIntelligenceFixtures.js +14 -4
- package/dist/fixtures/repoIntelligenceTestHelpers.d.ts +5 -0
- package/dist/fixtures/repoIntelligenceTestHelpers.d.ts.map +1 -0
- package/dist/fixtures/repoIntelligenceTestHelpers.js +28 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/narrativeParity.test.js +2 -1
- package/dist/narrativePresentation.test.js +2 -1
- package/dist/repoIntelligenceMappers.d.ts +20 -0
- package/dist/repoIntelligenceMappers.d.ts.map +1 -0
- package/dist/repoIntelligenceMappers.js +60 -0
- package/dist/repoIntelligenceSchema.contract.test.d.ts +2 -0
- package/dist/repoIntelligenceSchema.contract.test.d.ts.map +1 -0
- package/dist/repoIntelligenceSchema.contract.test.js +19 -0
- package/dist/repoIntelligenceSchema.d.ts +52 -83
- package/dist/repoIntelligenceSchema.d.ts.map +1 -1
- package/dist/repoIntelligenceSchema.js +99 -141
- package/dist/repoIntelligenceValidation.d.ts +18 -0
- package/dist/repoIntelligenceValidation.d.ts.map +1 -0
- package/dist/repoIntelligenceValidation.js +76 -0
- package/dist/repoIntelligenceValidation.persist.test.d.ts +2 -0
- package/dist/repoIntelligenceValidation.persist.test.d.ts.map +1 -0
- package/dist/repoIntelligenceValidation.persist.test.js +44 -0
- package/dist/scanDetailViewModel.test.js +2 -1
- package/dist/scanNarrativeFacts.d.ts +2 -0
- package/dist/scanNarrativeFacts.d.ts.map +1 -1
- package/dist/scanNarrativeFacts.js +1 -0
- package/dist/types.d.ts +15 -3
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -1,106 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical `ScanResult.repoIntelligence` wire contract (Wave 1 + Wave 2).
|
|
3
|
+
*
|
|
4
|
+
* Bump {@link REPO_INTELLIGENCE_ABI} when this schema changes incompatibly.
|
|
5
|
+
* Engine maps internal signals to this shape at emit time; consumers must use
|
|
6
|
+
* {@link safeParseRepoIntelligence} and honor `analysisPreparation.repoIntelligenceValidation`.
|
|
7
|
+
*/
|
|
1
8
|
import { z } from "zod";
|
|
2
|
-
import type {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
}, z.core.$loose>]>;
|
|
11
|
-
declare const looseReachabilitySchema: z.ZodUnion<readonly [z.ZodEnum<{
|
|
12
|
-
unknown: "unknown";
|
|
13
|
-
on_runtime_paths: "on_runtime_paths";
|
|
14
|
-
build_only: "build_only";
|
|
15
|
-
test_only: "test_only";
|
|
16
|
-
unreachable: "unreachable";
|
|
17
|
-
}>, z.ZodString, z.ZodObject<{
|
|
18
|
-
kind: z.ZodString;
|
|
19
|
-
}, z.core.$loose>]>;
|
|
20
|
-
export declare const repoIntelligenceSchema: z.ZodObject<{
|
|
21
|
-
packages: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
22
|
-
runtimeSurface: z.ZodOptional<z.ZodUnion<readonly [z.ZodEnum<{
|
|
9
|
+
import type { ScanResult } from "./types.js";
|
|
10
|
+
import type { BlastRadiusLevel } from "./scanNarrativeFacts.js";
|
|
11
|
+
/** Bump when wire schema changes incompatibly. */
|
|
12
|
+
export declare const REPO_INTELLIGENCE_ABI: "1";
|
|
13
|
+
/** Strict canonical wire schema — single source of truth. */
|
|
14
|
+
export declare const repoIntelligenceWireSchema: z.ZodObject<{
|
|
15
|
+
packages: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
16
|
+
runtimeSurface: z.ZodEnum<{
|
|
23
17
|
unknown: "unknown";
|
|
24
18
|
runtime: "runtime";
|
|
25
19
|
build: "build";
|
|
26
20
|
test: "test";
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
}, z.core.$loose>]>>;
|
|
30
|
-
reachability: z.ZodOptional<z.ZodUnion<readonly [z.ZodEnum<{
|
|
21
|
+
}>;
|
|
22
|
+
reachability: z.ZodEnum<{
|
|
31
23
|
unknown: "unknown";
|
|
32
24
|
on_runtime_paths: "on_runtime_paths";
|
|
33
25
|
build_only: "build_only";
|
|
34
26
|
test_only: "test_only";
|
|
35
27
|
unreachable: "unreachable";
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
packageName: z.ZodOptional<z.ZodString>;
|
|
41
|
-
package: z.ZodOptional<z.ZodString>;
|
|
42
|
-
files: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
28
|
+
}>;
|
|
29
|
+
usage: z.ZodObject<{
|
|
30
|
+
packageName: z.ZodString;
|
|
31
|
+
files: z.ZodArray<z.ZodString>;
|
|
43
32
|
paths: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
44
33
|
criticalPaths: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
45
34
|
areas: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
46
|
-
}, z.core.$
|
|
47
|
-
usage: z.ZodOptional<z.ZodObject<{
|
|
48
|
-
packageName: z.ZodOptional<z.ZodString>;
|
|
49
|
-
package: z.ZodOptional<z.ZodString>;
|
|
50
|
-
files: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
51
|
-
paths: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
52
|
-
criticalPaths: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
53
|
-
areas: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
54
|
-
}, z.core.$loose>>;
|
|
35
|
+
}, z.core.$strip>;
|
|
55
36
|
areas: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
56
|
-
|
|
57
|
-
packageName: z.ZodOptional<z.ZodString>;
|
|
58
|
-
paths: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
59
|
-
depth: z.ZodOptional<z.ZodNumber>;
|
|
60
|
-
}, z.core.$loose>>>;
|
|
61
|
-
}, z.core.$loose>>>;
|
|
37
|
+
}, z.core.$strip>>;
|
|
62
38
|
packageUsage: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
63
|
-
packageName: z.
|
|
64
|
-
|
|
65
|
-
files: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
39
|
+
packageName: z.ZodString;
|
|
40
|
+
files: z.ZodArray<z.ZodString>;
|
|
66
41
|
paths: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
67
42
|
criticalPaths: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
68
43
|
areas: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
69
|
-
}, z.core.$
|
|
44
|
+
}, z.core.$strip>>>;
|
|
70
45
|
blastRadius: z.ZodOptional<z.ZodObject<{
|
|
71
|
-
level: z.
|
|
46
|
+
level: z.ZodEnum<{
|
|
72
47
|
moderate: "moderate";
|
|
73
48
|
narrow: "narrow";
|
|
74
49
|
wide: "wide";
|
|
75
|
-
}
|
|
50
|
+
}>;
|
|
76
51
|
factors: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
77
52
|
changedPackageCount: z.ZodOptional<z.ZodNumber>;
|
|
78
|
-
}, z.core.$
|
|
79
|
-
runtimeSurface: z.ZodOptional<z.ZodUnion<readonly [z.ZodEnum<{
|
|
80
|
-
unknown: "unknown";
|
|
81
|
-
runtime: "runtime";
|
|
82
|
-
build: "build";
|
|
83
|
-
test: "test";
|
|
84
|
-
}>, z.ZodString, z.ZodObject<{
|
|
85
|
-
kind: z.ZodString;
|
|
86
|
-
}, z.core.$loose>]>>;
|
|
87
|
-
reachability: z.ZodOptional<z.ZodUnion<readonly [z.ZodEnum<{
|
|
88
|
-
unknown: "unknown";
|
|
89
|
-
on_runtime_paths: "on_runtime_paths";
|
|
90
|
-
build_only: "build_only";
|
|
91
|
-
test_only: "test_only";
|
|
92
|
-
unreachable: "unreachable";
|
|
93
|
-
}>, z.ZodString, z.ZodObject<{
|
|
94
|
-
kind: z.ZodString;
|
|
95
|
-
}, z.core.$loose>]>>;
|
|
53
|
+
}, z.core.$strip>>;
|
|
96
54
|
applicationAreas: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
97
55
|
id: z.ZodString;
|
|
98
56
|
label: z.ZodString;
|
|
99
|
-
}, z.core.$
|
|
57
|
+
}, z.core.$strip>>>;
|
|
100
58
|
affectedAreas: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
101
59
|
id: z.ZodString;
|
|
102
60
|
label: z.ZodString;
|
|
103
|
-
}, z.core.$
|
|
61
|
+
}, z.core.$strip>>>;
|
|
104
62
|
hotspots: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
105
63
|
packageName: z.ZodString;
|
|
106
64
|
source: z.ZodOptional<z.ZodEnum<{
|
|
@@ -108,16 +66,27 @@ export declare const repoIntelligenceSchema: z.ZodObject<{
|
|
|
108
66
|
graph: "graph";
|
|
109
67
|
}>>;
|
|
110
68
|
depth: z.ZodOptional<z.ZodNumber>;
|
|
111
|
-
paths: z.
|
|
112
|
-
}, z.core.$
|
|
69
|
+
paths: z.ZodArray<z.ZodString>;
|
|
70
|
+
}, z.core.$strip>>>;
|
|
113
71
|
frameworks: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
114
|
-
}, z.core.$
|
|
115
|
-
export type RepoIntelligence = z.infer<typeof
|
|
116
|
-
export
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
export
|
|
72
|
+
}, z.core.$strip>;
|
|
73
|
+
export type RepoIntelligence = z.infer<typeof repoIntelligenceWireSchema>;
|
|
74
|
+
export type ParseRepoIntelligenceSuccess = {
|
|
75
|
+
ok: true;
|
|
76
|
+
value: RepoIntelligence;
|
|
77
|
+
};
|
|
78
|
+
export type ParseRepoIntelligenceFailure = {
|
|
79
|
+
ok: false;
|
|
80
|
+
issues: string[];
|
|
81
|
+
issueCount: number;
|
|
82
|
+
packageCount: number;
|
|
83
|
+
};
|
|
84
|
+
export type ParseRepoIntelligenceResult = ParseRepoIntelligenceSuccess | ParseRepoIntelligenceFailure;
|
|
85
|
+
export declare function countRepoIntelligencePackages(raw: unknown): number;
|
|
86
|
+
export declare function safeParseRepoIntelligence(raw: unknown): ParseRepoIntelligenceResult;
|
|
87
|
+
/** Throws when wire block is present but invalid (CI / tests only). */
|
|
88
|
+
export declare function validateRepoIntelligenceWire(raw: unknown): RepoIntelligence;
|
|
89
|
+
/** CI helper — fails test when engine output includes invalid repoIntelligence. */
|
|
90
|
+
export declare function assertEngineOutputRepoIntelligenceValid(scan: ScanResult): void;
|
|
121
91
|
export declare function readBlastLevel(level: BlastRadiusLevel | undefined): BlastRadiusLevel | null;
|
|
122
|
-
export {};
|
|
123
92
|
//# sourceMappingURL=repoIntelligenceSchema.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"repoIntelligenceSchema.d.ts","sourceRoot":"","sources":["../src/repoIntelligenceSchema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"repoIntelligenceSchema.d.ts","sourceRoot":"","sources":["../src/repoIntelligenceSchema.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEhE,kDAAkD;AAClD,eAAO,MAAM,qBAAqB,EAAG,GAAY,CAAC;AA+ClD,6DAA6D;AAC7D,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAQrC,CAAC;AAEH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAE1E,MAAM,MAAM,4BAA4B,GAAG;IACzC,EAAE,EAAE,IAAI,CAAC;IACT,KAAK,EAAE,gBAAgB,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,4BAA4B,GAAG;IACzC,EAAE,EAAE,KAAK,CAAC;IACV,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,2BAA2B,GACnC,4BAA4B,GAC5B,4BAA4B,CAAC;AAQjC,wBAAgB,6BAA6B,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAUlE;AAED,wBAAgB,yBAAyB,CACvC,GAAG,EAAE,OAAO,GACX,2BAA2B,CAyC7B;AAED,uEAAuE;AACvE,wBAAgB,4BAA4B,CAAC,GAAG,EAAE,OAAO,GAAG,gBAAgB,CAM3E;AAED,mFAAmF;AACnF,wBAAgB,uCAAuC,CACrD,IAAI,EAAE,UAAU,GACf,IAAI,CAGN;AAED,wBAAgB,cAAc,CAC5B,KAAK,EAAE,gBAAgB,GAAG,SAAS,GAClC,gBAAgB,GAAG,IAAI,CAKzB"}
|
|
@@ -1,4 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical `ScanResult.repoIntelligence` wire contract (Wave 1 + Wave 2).
|
|
3
|
+
*
|
|
4
|
+
* Bump {@link REPO_INTELLIGENCE_ABI} when this schema changes incompatibly.
|
|
5
|
+
* Engine maps internal signals to this shape at emit time; consumers must use
|
|
6
|
+
* {@link safeParseRepoIntelligence} and honor `analysisPreparation.repoIntelligenceValidation`.
|
|
7
|
+
*/
|
|
1
8
|
import { z } from "zod";
|
|
9
|
+
/** Bump when wire schema changes incompatibly. */
|
|
10
|
+
export const REPO_INTELLIGENCE_ABI = "1";
|
|
2
11
|
const surfaceKindSchema = z.enum(["runtime", "build", "test", "unknown"]);
|
|
3
12
|
const reachabilityKindSchema = z.enum([
|
|
4
13
|
"on_runtime_paths",
|
|
@@ -8,163 +17,112 @@ const reachabilityKindSchema = z.enum([
|
|
|
8
17
|
"unknown",
|
|
9
18
|
]);
|
|
10
19
|
const blastLevelSchema = z.enum(["narrow", "moderate", "wide"]);
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
z.string(),
|
|
14
|
-
z.object({ kind: z.string() }).passthrough(),
|
|
15
|
-
]);
|
|
16
|
-
const looseReachabilitySchema = z.union([
|
|
17
|
-
reachabilityKindSchema,
|
|
18
|
-
z.string(),
|
|
19
|
-
z.object({ kind: z.string() }).passthrough(),
|
|
20
|
-
]);
|
|
21
|
-
const packageUsageEntrySchema = z
|
|
22
|
-
.object({
|
|
23
|
-
packageName: z.string().optional(),
|
|
24
|
-
package: z.string().optional(),
|
|
25
|
-
files: z.array(z.string()).optional(),
|
|
20
|
+
const packageUsageWireSchema = z.object({
|
|
21
|
+
packageName: z.string().min(1),
|
|
22
|
+
files: z.array(z.string()),
|
|
26
23
|
paths: z.array(z.string()).optional(),
|
|
27
24
|
criticalPaths: z.array(z.string()).optional(),
|
|
28
25
|
areas: z.array(z.string()).optional(),
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
reachability: looseReachabilitySchema.optional(),
|
|
35
|
-
packageUsage: packageUsageEntrySchema.optional(),
|
|
36
|
-
usage: packageUsageEntrySchema.optional(),
|
|
26
|
+
});
|
|
27
|
+
const packageIntelWireSchema = z.object({
|
|
28
|
+
runtimeSurface: surfaceKindSchema,
|
|
29
|
+
reachability: reachabilityKindSchema,
|
|
30
|
+
usage: packageUsageWireSchema,
|
|
37
31
|
areas: z.array(z.string()).optional(),
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
})
|
|
54
|
-
|
|
55
|
-
export const
|
|
56
|
-
.
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
blastRadius: z
|
|
60
|
-
.object({
|
|
61
|
-
level: blastLevelSchema.optional(),
|
|
62
|
-
factors: z.array(z.string()).optional(),
|
|
63
|
-
changedPackageCount: z.number().optional(),
|
|
64
|
-
})
|
|
65
|
-
.passthrough()
|
|
66
|
-
.optional(),
|
|
67
|
-
runtimeSurface: looseSurfaceSchema.optional(),
|
|
68
|
-
reachability: looseReachabilitySchema.optional(),
|
|
32
|
+
});
|
|
33
|
+
const areaSchema = z.object({
|
|
34
|
+
id: z.string().min(1),
|
|
35
|
+
label: z.string().min(1),
|
|
36
|
+
});
|
|
37
|
+
const hotspotWireSchema = z.object({
|
|
38
|
+
packageName: z.string().min(1),
|
|
39
|
+
source: z.enum(["code", "graph"]).optional(),
|
|
40
|
+
depth: z.number().optional(),
|
|
41
|
+
paths: z.array(z.string()),
|
|
42
|
+
});
|
|
43
|
+
const blastRadiusWireSchema = z.object({
|
|
44
|
+
level: blastLevelSchema,
|
|
45
|
+
factors: z.array(z.string()).optional(),
|
|
46
|
+
changedPackageCount: z.number().int().nonnegative().optional(),
|
|
47
|
+
});
|
|
48
|
+
/** Strict canonical wire schema — single source of truth. */
|
|
49
|
+
export const repoIntelligenceWireSchema = z.object({
|
|
50
|
+
packages: z.record(z.string(), packageIntelWireSchema),
|
|
51
|
+
packageUsage: z.array(packageUsageWireSchema).optional(),
|
|
52
|
+
blastRadius: blastRadiusWireSchema.optional(),
|
|
69
53
|
applicationAreas: z.array(areaSchema).optional(),
|
|
70
54
|
affectedAreas: z.array(areaSchema).optional(),
|
|
71
|
-
hotspots: z
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
})
|
|
79
|
-
.passthrough())
|
|
80
|
-
.optional(),
|
|
81
|
-
frameworks: z.array(z.string()).optional(),
|
|
82
|
-
})
|
|
83
|
-
.passthrough();
|
|
84
|
-
export function parseRepoIntelligence(raw) {
|
|
55
|
+
hotspots: z.array(hotspotWireSchema).optional(),
|
|
56
|
+
frameworks: z.array(z.string().min(1)).optional(),
|
|
57
|
+
});
|
|
58
|
+
function formatZodIssues(error) {
|
|
59
|
+
return error.issues.map((i) => `${i.path.join(".") || "(root)"}: ${i.message}`);
|
|
60
|
+
}
|
|
61
|
+
export function countRepoIntelligencePackages(raw) {
|
|
85
62
|
if (raw == null || typeof raw !== "object")
|
|
86
|
-
return
|
|
87
|
-
const
|
|
88
|
-
if (
|
|
89
|
-
return
|
|
90
|
-
|
|
91
|
-
if (
|
|
92
|
-
return
|
|
93
|
-
if (Object.keys(value).length === 1 &&
|
|
94
|
-
value.packages &&
|
|
95
|
-
Object.keys(value.packages).length === 0) {
|
|
96
|
-
return null;
|
|
63
|
+
return 0;
|
|
64
|
+
const obj = raw;
|
|
65
|
+
if (obj.packages != null && typeof obj.packages === "object") {
|
|
66
|
+
return Object.keys(obj.packages).length;
|
|
67
|
+
}
|
|
68
|
+
if (Array.isArray(obj.packageUsage)) {
|
|
69
|
+
return obj.packageUsage.length;
|
|
97
70
|
}
|
|
98
|
-
return
|
|
71
|
+
return 0;
|
|
99
72
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
[/\bjest\b/i, "test"],
|
|
110
|
-
];
|
|
111
|
-
const REACHABILITY_ALIASES = [
|
|
112
|
-
[/\bon[_\s-]?runtime/i, "on_runtime_paths"],
|
|
113
|
-
[/\bruntime[_\s-]?path/i, "on_runtime_paths"],
|
|
114
|
-
[/\bdirect\b/i, "on_runtime_paths"],
|
|
115
|
-
[/\bbuild[_\s-]?only/i, "build_only"],
|
|
116
|
-
[/\btest[_\s-]?only/i, "test_only"],
|
|
117
|
-
[/\bunreachable\b/i, "unreachable"],
|
|
118
|
-
];
|
|
119
|
-
export function normalizeRuntimeSurfaceKind(raw) {
|
|
120
|
-
if (!raw?.trim())
|
|
121
|
-
return "unknown";
|
|
122
|
-
const t = raw.trim().toLowerCase();
|
|
123
|
-
if (t === "runtime" || t === "build" || t === "test" || t === "unknown") {
|
|
124
|
-
return t;
|
|
73
|
+
export function safeParseRepoIntelligence(raw) {
|
|
74
|
+
const packageCount = countRepoIntelligencePackages(raw);
|
|
75
|
+
if (raw == null || typeof raw !== "object") {
|
|
76
|
+
return {
|
|
77
|
+
ok: false,
|
|
78
|
+
issues: ["(root): expected object"],
|
|
79
|
+
issueCount: 1,
|
|
80
|
+
packageCount: 0,
|
|
81
|
+
};
|
|
125
82
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
83
|
+
const parsed = repoIntelligenceWireSchema.safeParse(raw);
|
|
84
|
+
if (!parsed.success) {
|
|
85
|
+
const issues = formatZodIssues(parsed.error);
|
|
86
|
+
return {
|
|
87
|
+
ok: false,
|
|
88
|
+
issues,
|
|
89
|
+
issueCount: issues.length,
|
|
90
|
+
packageCount,
|
|
91
|
+
};
|
|
129
92
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
t === "test_only" ||
|
|
139
|
-
t === "unreachable" ||
|
|
140
|
-
t === "unknown") {
|
|
141
|
-
return t;
|
|
93
|
+
const value = parsed.data;
|
|
94
|
+
if (Object.keys(value).length === 0) {
|
|
95
|
+
return {
|
|
96
|
+
ok: false,
|
|
97
|
+
issues: ["(root): empty object"],
|
|
98
|
+
issueCount: 1,
|
|
99
|
+
packageCount: 0,
|
|
100
|
+
};
|
|
142
101
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
102
|
+
if (Object.keys(value.packages).length === 0 &&
|
|
103
|
+
(value.packageUsage?.length ?? 0) === 0) {
|
|
104
|
+
return {
|
|
105
|
+
ok: false,
|
|
106
|
+
issues: ["packages: at least one package required"],
|
|
107
|
+
issueCount: 1,
|
|
108
|
+
packageCount: 0,
|
|
109
|
+
};
|
|
146
110
|
}
|
|
147
|
-
return
|
|
111
|
+
return { ok: true, value };
|
|
148
112
|
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
if (
|
|
153
|
-
|
|
154
|
-
if (typeof value === "object" && "kind" in value) {
|
|
155
|
-
return normalizeRuntimeSurfaceKind(String(value.kind));
|
|
113
|
+
/** Throws when wire block is present but invalid (CI / tests only). */
|
|
114
|
+
export function validateRepoIntelligenceWire(raw) {
|
|
115
|
+
const r = safeParseRepoIntelligence(raw);
|
|
116
|
+
if (!r.ok) {
|
|
117
|
+
throw new Error(`repoIntelligence contract: ${r.issues.join("; ")}`);
|
|
156
118
|
}
|
|
157
|
-
return
|
|
119
|
+
return r.value;
|
|
158
120
|
}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
if (typeof value === "object" && "kind" in value) {
|
|
165
|
-
return normalizeReachabilityKind(String(value.kind));
|
|
166
|
-
}
|
|
167
|
-
return null;
|
|
121
|
+
/** CI helper — fails test when engine output includes invalid repoIntelligence. */
|
|
122
|
+
export function assertEngineOutputRepoIntelligenceValid(scan) {
|
|
123
|
+
if (scan.repoIntelligence == null)
|
|
124
|
+
return;
|
|
125
|
+
validateRepoIntelligenceWire(scan.repoIntelligence);
|
|
168
126
|
}
|
|
169
127
|
export function readBlastLevel(level) {
|
|
170
128
|
if (level === "narrow" || level === "moderate" || level === "wide") {
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ScanResult } from "./types.js";
|
|
2
|
+
export type RepoIntelligenceContractLogPayload = {
|
|
3
|
+
issueCount: number;
|
|
4
|
+
representativeIssues: string[];
|
|
5
|
+
packageCount: number;
|
|
6
|
+
methodologyVersion?: string;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Non-blocking production gate: validate repoIntelligence, attach metadata, retain raw payload.
|
|
10
|
+
*/
|
|
11
|
+
export declare function applyRepoIntelligenceValidation(result: ScanResult, options?: {
|
|
12
|
+
validatedAt?: string;
|
|
13
|
+
}): {
|
|
14
|
+
result: ScanResult;
|
|
15
|
+
contractFailed: boolean;
|
|
16
|
+
logPayload?: RepoIntelligenceContractLogPayload;
|
|
17
|
+
};
|
|
18
|
+
//# sourceMappingURL=repoIntelligenceValidation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repoIntelligenceValidation.d.ts","sourceRoot":"","sources":["../src/repoIntelligenceValidation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAGV,UAAU,EACX,MAAM,YAAY,CAAC;AAOpB,MAAM,MAAM,kCAAkC,GAAG;IAC/C,UAAU,EAAE,MAAM,CAAC;IACnB,oBAAoB,EAAE,MAAM,EAAE,CAAC;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B,CAAC;AAIF;;GAEG;AACH,wBAAgB,+BAA+B,CAC7C,MAAM,EAAE,UAAU,EAClB,OAAO,CAAC,EAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,GACjC;IACD,MAAM,EAAE,UAAU,CAAC;IACnB,cAAc,EAAE,OAAO,CAAC;IACxB,UAAU,CAAC,EAAE,kCAAkC,CAAC;CACjD,CAqFA"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { REPO_INTELLIGENCE_ABI, countRepoIntelligencePackages, safeParseRepoIntelligence, } from "./repoIntelligenceSchema.js";
|
|
2
|
+
const MAX_REPRESENTATIVE_ISSUES = 5;
|
|
3
|
+
/**
|
|
4
|
+
* Non-blocking production gate: validate repoIntelligence, attach metadata, retain raw payload.
|
|
5
|
+
*/
|
|
6
|
+
export function applyRepoIntelligenceValidation(result, options) {
|
|
7
|
+
const validatedAt = options?.validatedAt ?? new Date().toISOString();
|
|
8
|
+
const raw = result.repoIntelligence;
|
|
9
|
+
if (raw == null ||
|
|
10
|
+
(typeof raw === "object" && Object.keys(raw).length === 0)) {
|
|
11
|
+
const analysisPreparation = {
|
|
12
|
+
...result.analysisPreparation,
|
|
13
|
+
codeIntelligenceAvailable: result.analysisPreparation?.codeIntelligenceAvailable ?? false,
|
|
14
|
+
warnings: result.analysisPreparation?.warnings ?? [],
|
|
15
|
+
repoIntelligenceValidation: {
|
|
16
|
+
status: "absent",
|
|
17
|
+
abi: REPO_INTELLIGENCE_ABI,
|
|
18
|
+
validatedAt,
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
return {
|
|
22
|
+
result: { ...result, analysisPreparation },
|
|
23
|
+
contractFailed: false,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
const parsed = safeParseRepoIntelligence(raw);
|
|
27
|
+
const packageCount = countRepoIntelligencePackages(raw);
|
|
28
|
+
if (parsed.ok) {
|
|
29
|
+
const analysisPreparation = {
|
|
30
|
+
...result.analysisPreparation,
|
|
31
|
+
codeIntelligenceAvailable: result.analysisPreparation?.codeIntelligenceAvailable ?? true,
|
|
32
|
+
warnings: result.analysisPreparation?.warnings ?? [],
|
|
33
|
+
repoIntelligenceValidation: {
|
|
34
|
+
status: "valid",
|
|
35
|
+
abi: REPO_INTELLIGENCE_ABI,
|
|
36
|
+
validatedAt,
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
return {
|
|
40
|
+
result: { ...result, analysisPreparation },
|
|
41
|
+
contractFailed: false,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
const representativeIssues = parsed.issues.slice(0, MAX_REPRESENTATIVE_ISSUES);
|
|
45
|
+
const warning = {
|
|
46
|
+
code: "repo_intelligence_contract_invalid",
|
|
47
|
+
message: "Repository intelligence block failed contract validation; narrative uses graph fallback.",
|
|
48
|
+
details: {
|
|
49
|
+
issueCount: parsed.issueCount,
|
|
50
|
+
representativeIssues,
|
|
51
|
+
abi: REPO_INTELLIGENCE_ABI,
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
const analysisPreparation = {
|
|
55
|
+
...result.analysisPreparation,
|
|
56
|
+
codeIntelligenceAvailable: result.analysisPreparation?.codeIntelligenceAvailable ?? true,
|
|
57
|
+
warnings: [...(result.analysisPreparation?.warnings ?? []), warning],
|
|
58
|
+
repoIntelligenceValidation: {
|
|
59
|
+
status: "invalid",
|
|
60
|
+
abi: REPO_INTELLIGENCE_ABI,
|
|
61
|
+
issueCount: parsed.issueCount,
|
|
62
|
+
representativeIssues,
|
|
63
|
+
validatedAt,
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
return {
|
|
67
|
+
result: { ...result, repoIntelligence: raw, analysisPreparation },
|
|
68
|
+
contractFailed: true,
|
|
69
|
+
logPayload: {
|
|
70
|
+
issueCount: parsed.issueCount,
|
|
71
|
+
representativeIssues,
|
|
72
|
+
packageCount,
|
|
73
|
+
methodologyVersion: result.methodologyVersion,
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repoIntelligenceValidation.persist.test.d.ts","sourceRoot":"","sources":["../src/repoIntelligenceValidation.persist.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,44 @@
|
|
|
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
|
+
});
|
|
@@ -2,6 +2,7 @@ import { describe, expect, it } from "vitest";
|
|
|
2
2
|
import { primaryPrExample } from "./productMessaging.js";
|
|
3
3
|
import { ACT2_MAX_THEMES, TIER1_MAX_VISIBLE_IMPACTS, deriveAct2Themes, deriveOperationalImpact, deriveFollowUpBridgeNote, deriveScanDetailViewModel, deriveVerdictLine, sanitizeAct2Theme, tier1PassesRecallTest, } from "./scanDetailViewModel.js";
|
|
4
4
|
import { fixtureRepoIntelligenceFastify } from "./fixtures/repoIntelligenceFixtures.js";
|
|
5
|
+
import { analysisPreparationWithValidRepoIntel } from "./fixtures/repoIntelligenceTestHelpers.js";
|
|
5
6
|
const baseResult = {
|
|
6
7
|
totalScore: 10,
|
|
7
8
|
layerScores: {
|
|
@@ -133,7 +134,7 @@ describe("Tier 1 success criterion", () => {
|
|
|
133
134
|
const vm = deriveScanDetailViewModel({
|
|
134
135
|
...baseResult,
|
|
135
136
|
changedPackages: ["fastify"],
|
|
136
|
-
analysisPreparation:
|
|
137
|
+
analysisPreparation: analysisPreparationWithValidRepoIntel(),
|
|
137
138
|
repoIntelligence: fixtureRepoIntelligenceFastify,
|
|
138
139
|
insights: [
|
|
139
140
|
{
|
|
@@ -14,6 +14,7 @@ export type NarrativePackageUsage = {
|
|
|
14
14
|
files: string[];
|
|
15
15
|
areas: string[];
|
|
16
16
|
};
|
|
17
|
+
export type RepoIntelligenceParseStatus = "ok" | "invalid" | "absent" | "untrusted";
|
|
17
18
|
export type ScanNarrativeFacts = {
|
|
18
19
|
availability: {
|
|
19
20
|
mode: NarrativeAvailabilityMode;
|
|
@@ -23,6 +24,7 @@ export type ScanNarrativeFacts = {
|
|
|
23
24
|
tier2: boolean;
|
|
24
25
|
tier3: boolean;
|
|
25
26
|
};
|
|
27
|
+
repoIntelligenceParse: RepoIntelligenceParseStatus;
|
|
26
28
|
};
|
|
27
29
|
changedPackages: {
|
|
28
30
|
primary: string | null;
|