@brandlab-digital/conformance 1.0.0-rc.8 → 1.0.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/cli.d.ts +0 -3
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +35 -93
- package/dist/cli.js.map +1 -1
- package/dist/current-release.d.ts +3 -0
- package/dist/current-release.d.ts.map +1 -0
- package/dist/current-release.js +54 -0
- package/dist/current-release.js.map +1 -0
- package/dist/project.d.ts +19 -24
- package/dist/project.d.ts.map +1 -1
- package/dist/project.js +444 -1070
- package/dist/project.js.map +1 -1
- package/dist/release-manifest.d.ts +29 -82
- package/dist/release-manifest.d.ts.map +1 -1
- package/dist/release-manifest.js +141 -348
- package/dist/release-manifest.js.map +1 -1
- package/dist/rendered-content.d.ts +36 -0
- package/dist/rendered-content.d.ts.map +1 -0
- package/dist/rendered-content.js +146 -0
- package/dist/rendered-content.js.map +1 -0
- package/dist/verification-policy.d.ts +162 -0
- package/dist/verification-policy.d.ts.map +1 -0
- package/dist/verification-policy.js +309 -0
- package/dist/verification-policy.js.map +1 -0
- package/package.json +6 -22
- package/dist/legacy-architecture-contracts.d.ts +0 -31
- package/dist/legacy-architecture-contracts.d.ts.map +0 -1
- package/dist/legacy-architecture-contracts.js +0 -49
- package/dist/legacy-architecture-contracts.js.map +0 -1
- package/dist/locations.d.ts +0 -63
- package/dist/locations.d.ts.map +0 -1
- package/dist/locations.js +0 -105
- package/dist/locations.js.map +0 -1
- package/dist/project-config-v2-contract.d.ts +0 -32
- package/dist/project-config-v2-contract.d.ts.map +0 -1
- package/dist/project-config-v2-contract.js +0 -926
- package/dist/project-config-v2-contract.js.map +0 -1
- package/dist/project-config-v3.d.ts +0 -43
- package/dist/project-config-v3.d.ts.map +0 -1
- package/dist/project-config-v3.js +0 -2876
- package/dist/project-config-v3.js.map +0 -1
- package/dist/project-config.d.ts +0 -3
- package/dist/project-config.d.ts.map +0 -1
- package/dist/project-config.js +0 -3
- package/dist/project-config.js.map +0 -1
- package/dist/release-catalog.d.ts +0 -4
- package/dist/release-catalog.d.ts.map +0 -1
- package/dist/release-catalog.js +0 -714
- package/dist/release-catalog.js.map +0 -1
- package/dist/release-contract-compatibility.d.ts +0 -97
- package/dist/release-contract-compatibility.d.ts.map +0 -1
- package/dist/release-contract-compatibility.js +0 -112
- package/dist/release-contract-compatibility.js.map +0 -1
- package/dist/schema-hygiene.d.ts +0 -26
- package/dist/schema-hygiene.d.ts.map +0 -1
- package/dist/schema-hygiene.js +0 -28
- package/dist/schema-hygiene.js.map +0 -1
- package/dist/source-safety-contracts.d.ts +0 -20
- package/dist/source-safety-contracts.d.ts.map +0 -1
- package/dist/source-safety-contracts.js +0 -60
- package/dist/source-safety-contracts.js.map +0 -1
- package/dist/vitest.d.ts +0 -110
- package/dist/vitest.d.ts.map +0 -1
- package/dist/vitest.js +0 -2678
- package/dist/vitest.js.map +0 -1
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export interface RenderedContentSample {
|
|
2
|
+
id: string;
|
|
3
|
+
mainText: string;
|
|
4
|
+
/**
|
|
5
|
+
* Page-specific values such as a suburb or council name. These are used only
|
|
6
|
+
* to detect pages that are otherwise exact token substitutions.
|
|
7
|
+
*/
|
|
8
|
+
variationTokens?: readonly string[];
|
|
9
|
+
}
|
|
10
|
+
export interface RenderedContentSimilarityOptions {
|
|
11
|
+
warningThreshold?: number;
|
|
12
|
+
shingleSize?: number;
|
|
13
|
+
/** Limits the diagnostic comparison set deterministically. */
|
|
14
|
+
sampleLimit?: number;
|
|
15
|
+
}
|
|
16
|
+
export interface RenderedContentSimilarityFinding {
|
|
17
|
+
leftId: string;
|
|
18
|
+
rightId: string;
|
|
19
|
+
score: number;
|
|
20
|
+
severity: 'warning';
|
|
21
|
+
}
|
|
22
|
+
export interface RenderedContentDuplicateFinding {
|
|
23
|
+
leftId: string;
|
|
24
|
+
rightId: string;
|
|
25
|
+
kind: 'exact-duplicate' | 'token-substitution';
|
|
26
|
+
severity: 'blocker';
|
|
27
|
+
}
|
|
28
|
+
export declare function normalizeRenderedContent(value: string): string;
|
|
29
|
+
/**
|
|
30
|
+
* Operational duplicate protection. This deliberately blocks only exact
|
|
31
|
+
* copied output and explicit location-token substitutions.
|
|
32
|
+
*/
|
|
33
|
+
export declare function evaluateRenderedContentDuplicates(samples: readonly RenderedContentSample[]): RenderedContentDuplicateFinding[];
|
|
34
|
+
export declare function fiveWordShingleSimilarity(left: string, right: string): number;
|
|
35
|
+
export declare function evaluateRenderedContentSimilarity(samples: readonly RenderedContentSample[], options?: RenderedContentSimilarityOptions): RenderedContentSimilarityFinding[];
|
|
36
|
+
//# sourceMappingURL=rendered-content.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rendered-content.d.ts","sourceRoot":"","sources":["../src/rendered-content.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,qBAAqB;IACpC,EAAE,EAAE,MAAM,CAAA;IACV,QAAQ,EAAE,MAAM,CAAA;IAChB;;;OAGG;IACH,eAAe,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CACpC;AAED,MAAM,WAAW,gCAAgC;IAC/C,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,8DAA8D;IAC9D,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,MAAM,WAAW,gCAAgC;IAC/C,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,SAAS,CAAA;CACpB;AAED,MAAM,WAAW,+BAA+B;IAC9C,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,iBAAiB,GAAG,oBAAoB,CAAA;IAC9C,QAAQ,EAAE,SAAS,CAAA;CACpB;AAWD,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,UAErD;AA4BD;;;GAGG;AACH,wBAAgB,iCAAiC,CAC/C,OAAO,EAAE,SAAS,qBAAqB,EAAE,qCAiD1C;AAWD,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,UAYpE;AAED,wBAAgB,iCAAiC,CAC/C,OAAO,EAAE,SAAS,qBAAqB,EAAE,EACzC,OAAO,GAAE,gCAAqC,sCAyD/C"}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
function normalizedWords(value) {
|
|
2
|
+
return value
|
|
3
|
+
.normalize('NFKC')
|
|
4
|
+
.toLowerCase()
|
|
5
|
+
.replace(/[\p{P}\p{S}]+/gu, ' ')
|
|
6
|
+
.split(/\s+/)
|
|
7
|
+
.filter(Boolean);
|
|
8
|
+
}
|
|
9
|
+
export function normalizeRenderedContent(value) {
|
|
10
|
+
return normalizedWords(value).join(' ');
|
|
11
|
+
}
|
|
12
|
+
function assertUniqueSampleIds(samples) {
|
|
13
|
+
const duplicateId = samples.find((sample, index) => samples.slice(0, index).some((candidate) => candidate.id === sample.id));
|
|
14
|
+
if (duplicateId) {
|
|
15
|
+
throw new Error(`Rendered-content sample ID "${duplicateId.id}" is duplicated.`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function normalizeWithoutVariationTokens(sample) {
|
|
19
|
+
let normalized = normalizeRenderedContent(sample.mainText);
|
|
20
|
+
const tokens = [...(sample.variationTokens ?? [])]
|
|
21
|
+
.map(normalizeRenderedContent)
|
|
22
|
+
.filter(Boolean)
|
|
23
|
+
.sort((left, right) => right.length - left.length);
|
|
24
|
+
for (const token of tokens) {
|
|
25
|
+
const escaped = token.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
26
|
+
normalized = normalized.replace(new RegExp(`(?:^|\\s)${escaped}(?=\\s|$)`, 'g'), ' location');
|
|
27
|
+
}
|
|
28
|
+
return normalized.trim().replace(/\s+/g, ' ');
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Operational duplicate protection. This deliberately blocks only exact
|
|
32
|
+
* copied output and explicit location-token substitutions.
|
|
33
|
+
*/
|
|
34
|
+
export function evaluateRenderedContentDuplicates(samples) {
|
|
35
|
+
assertUniqueSampleIds(samples);
|
|
36
|
+
const prepared = samples.map((sample) => ({
|
|
37
|
+
...sample,
|
|
38
|
+
normalized: normalizeRenderedContent(sample.mainText),
|
|
39
|
+
withoutVariationTokens: normalizeWithoutVariationTokens(sample),
|
|
40
|
+
}));
|
|
41
|
+
const findings = [];
|
|
42
|
+
for (let leftIndex = 0; leftIndex < prepared.length; leftIndex += 1) {
|
|
43
|
+
const left = prepared[leftIndex];
|
|
44
|
+
if (!left.normalized)
|
|
45
|
+
continue;
|
|
46
|
+
for (let rightIndex = leftIndex + 1; rightIndex < prepared.length; rightIndex += 1) {
|
|
47
|
+
const right = prepared[rightIndex];
|
|
48
|
+
if (!right.normalized)
|
|
49
|
+
continue;
|
|
50
|
+
if (left.normalized === right.normalized) {
|
|
51
|
+
findings.push({
|
|
52
|
+
leftId: left.id,
|
|
53
|
+
rightId: right.id,
|
|
54
|
+
kind: 'exact-duplicate',
|
|
55
|
+
severity: 'blocker',
|
|
56
|
+
});
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
if (left.variationTokens?.length &&
|
|
60
|
+
right.variationTokens?.length &&
|
|
61
|
+
left.withoutVariationTokens === right.withoutVariationTokens) {
|
|
62
|
+
findings.push({
|
|
63
|
+
leftId: left.id,
|
|
64
|
+
rightId: right.id,
|
|
65
|
+
kind: 'token-substitution',
|
|
66
|
+
severity: 'blocker',
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return findings.sort((left, right) => left.leftId.localeCompare(right.leftId) ||
|
|
72
|
+
left.rightId.localeCompare(right.rightId));
|
|
73
|
+
}
|
|
74
|
+
function shingles(value, size) {
|
|
75
|
+
const words = normalizedWords(value);
|
|
76
|
+
const values = new Set();
|
|
77
|
+
for (let index = 0; index <= words.length - size; index += 1) {
|
|
78
|
+
values.add(words.slice(index, index + size).join(' '));
|
|
79
|
+
}
|
|
80
|
+
return values;
|
|
81
|
+
}
|
|
82
|
+
export function fiveWordShingleSimilarity(left, right) {
|
|
83
|
+
const leftShingles = shingles(left, 5);
|
|
84
|
+
const rightShingles = shingles(right, 5);
|
|
85
|
+
if (leftShingles.size === 0 && rightShingles.size === 0)
|
|
86
|
+
return 1;
|
|
87
|
+
if (leftShingles.size === 0 || rightShingles.size === 0)
|
|
88
|
+
return 0;
|
|
89
|
+
let intersection = 0;
|
|
90
|
+
for (const shingle of leftShingles) {
|
|
91
|
+
if (rightShingles.has(shingle))
|
|
92
|
+
intersection += 1;
|
|
93
|
+
}
|
|
94
|
+
const union = leftShingles.size + rightShingles.size - intersection;
|
|
95
|
+
return union === 0 ? 0 : intersection / union;
|
|
96
|
+
}
|
|
97
|
+
export function evaluateRenderedContentSimilarity(samples, options = {}) {
|
|
98
|
+
const warningThreshold = options.warningThreshold ?? 0.55;
|
|
99
|
+
const shingleSize = options.shingleSize ?? 5;
|
|
100
|
+
const sampleLimit = options.sampleLimit ?? 100;
|
|
101
|
+
if (warningThreshold < 0 || warningThreshold > 1) {
|
|
102
|
+
throw new Error('Rendered-content warningThreshold must be between zero and one.');
|
|
103
|
+
}
|
|
104
|
+
if (!Number.isInteger(shingleSize) || shingleSize < 2 || shingleSize > 10) {
|
|
105
|
+
throw new Error('Rendered-content shingleSize must be an integer from 2 to 10.');
|
|
106
|
+
}
|
|
107
|
+
if (!Number.isInteger(sampleLimit) || sampleLimit < 2) {
|
|
108
|
+
throw new Error('Rendered-content sampleLimit must be an integer of at least two.');
|
|
109
|
+
}
|
|
110
|
+
assertUniqueSampleIds(samples);
|
|
111
|
+
const prepared = [...samples]
|
|
112
|
+
.sort((left, right) => left.id.localeCompare(right.id))
|
|
113
|
+
.slice(0, sampleLimit)
|
|
114
|
+
.map((sample) => ({
|
|
115
|
+
...sample,
|
|
116
|
+
shingleSet: shingles(sample.mainText, shingleSize),
|
|
117
|
+
}));
|
|
118
|
+
const findings = [];
|
|
119
|
+
for (let leftIndex = 0; leftIndex < prepared.length; leftIndex += 1) {
|
|
120
|
+
const left = prepared[leftIndex];
|
|
121
|
+
for (let rightIndex = leftIndex + 1; rightIndex < prepared.length; rightIndex += 1) {
|
|
122
|
+
const right = prepared[rightIndex];
|
|
123
|
+
if (left.shingleSet.size === 0 || right.shingleSet.size === 0)
|
|
124
|
+
continue;
|
|
125
|
+
let intersection = 0;
|
|
126
|
+
for (const shingle of left.shingleSet) {
|
|
127
|
+
if (right.shingleSet.has(shingle))
|
|
128
|
+
intersection += 1;
|
|
129
|
+
}
|
|
130
|
+
const union = left.shingleSet.size + right.shingleSet.size - intersection;
|
|
131
|
+
const score = union === 0 ? 0 : intersection / union;
|
|
132
|
+
if (score < warningThreshold)
|
|
133
|
+
continue;
|
|
134
|
+
findings.push({
|
|
135
|
+
leftId: left.id,
|
|
136
|
+
rightId: right.id,
|
|
137
|
+
score,
|
|
138
|
+
severity: 'warning',
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return findings.sort((left, right) => right.score - left.score ||
|
|
143
|
+
left.leftId.localeCompare(right.leftId) ||
|
|
144
|
+
left.rightId.localeCompare(right.rightId));
|
|
145
|
+
}
|
|
146
|
+
//# sourceMappingURL=rendered-content.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rendered-content.js","sourceRoot":"","sources":["../src/rendered-content.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import type { BrandlabFinding, ConformanceSummary } from "./project.js";
|
|
2
|
+
export declare const BRANDLAB_VERIFICATION_POLICY_VERSION = "simple-v1";
|
|
3
|
+
export declare const BRANDLAB_VERIFICATION_FAMILIES: readonly [{
|
|
4
|
+
readonly id: "runtime-release";
|
|
5
|
+
readonly title: "Runtime and release";
|
|
6
|
+
readonly purpose: "Supported runtime and one reproducible Brandlab release.";
|
|
7
|
+
}, {
|
|
8
|
+
readonly id: "project-verification";
|
|
9
|
+
readonly title: "Project verification";
|
|
10
|
+
readonly purpose: "Typecheck, tests and production build complete successfully.";
|
|
11
|
+
}, {
|
|
12
|
+
readonly id: "contact-delivery";
|
|
13
|
+
readonly title: "Contact delivery";
|
|
14
|
+
readonly purpose: "Shared validation, delivery and provider-error handling.";
|
|
15
|
+
}, {
|
|
16
|
+
readonly id: "analytics";
|
|
17
|
+
readonly title: "Analytics";
|
|
18
|
+
readonly purpose: "Canonical conversions run once without lead PII.";
|
|
19
|
+
}, {
|
|
20
|
+
readonly id: "search-output";
|
|
21
|
+
readonly title: "Search output";
|
|
22
|
+
readonly purpose: "Search routing and structured data are safe and consistent.";
|
|
23
|
+
}, {
|
|
24
|
+
readonly id: "business-data";
|
|
25
|
+
readonly title: "Business data";
|
|
26
|
+
readonly purpose: "Business visibility and route identity match declared facts.";
|
|
27
|
+
}, {
|
|
28
|
+
readonly id: "output-safety";
|
|
29
|
+
readonly title: "Output safety";
|
|
30
|
+
readonly purpose: "Public output and protected shared behaviour remain safe.";
|
|
31
|
+
}, {
|
|
32
|
+
readonly id: "accessibility";
|
|
33
|
+
readonly title: "Accessibility";
|
|
34
|
+
readonly purpose: "Representative browser routes have no serious accessibility failures.";
|
|
35
|
+
}, {
|
|
36
|
+
readonly id: "deployment-provenance";
|
|
37
|
+
readonly title: "Deployment provenance";
|
|
38
|
+
readonly purpose: "The reviewed source, package release and deployment agree.";
|
|
39
|
+
}];
|
|
40
|
+
export type BrandlabVerificationFamilyId = (typeof BRANDLAB_VERIFICATION_FAMILIES)[number]["id"];
|
|
41
|
+
export declare const BRANDLAB_VERIFICATION_CHECKS: readonly [{
|
|
42
|
+
readonly id: "runtime-supported";
|
|
43
|
+
readonly familyId: "runtime-release";
|
|
44
|
+
readonly title: "Supported runtime";
|
|
45
|
+
readonly staticEvidence: true;
|
|
46
|
+
}, {
|
|
47
|
+
readonly id: "release-consistency";
|
|
48
|
+
readonly familyId: "runtime-release";
|
|
49
|
+
readonly title: "Release consistency";
|
|
50
|
+
readonly staticEvidence: true;
|
|
51
|
+
}, {
|
|
52
|
+
readonly id: "project-build";
|
|
53
|
+
readonly familyId: "project-verification";
|
|
54
|
+
readonly title: "Typecheck, tests and build";
|
|
55
|
+
readonly staticEvidence: false;
|
|
56
|
+
}, {
|
|
57
|
+
readonly id: "contact-runtime";
|
|
58
|
+
readonly familyId: "contact-delivery";
|
|
59
|
+
readonly title: "Shared contact runtime";
|
|
60
|
+
readonly staticEvidence: true;
|
|
61
|
+
}, {
|
|
62
|
+
readonly id: "contact-provider-errors";
|
|
63
|
+
readonly familyId: "contact-delivery";
|
|
64
|
+
readonly title: "Provider error handling";
|
|
65
|
+
readonly staticEvidence: true;
|
|
66
|
+
}, {
|
|
67
|
+
readonly id: "analytics-privacy";
|
|
68
|
+
readonly familyId: "analytics";
|
|
69
|
+
readonly title: "Analytics privacy";
|
|
70
|
+
readonly staticEvidence: true;
|
|
71
|
+
}, {
|
|
72
|
+
readonly id: "analytics-conversion";
|
|
73
|
+
readonly familyId: "analytics";
|
|
74
|
+
readonly title: "Canonical conversion events";
|
|
75
|
+
readonly staticEvidence: true;
|
|
76
|
+
}, {
|
|
77
|
+
readonly id: "search-routing";
|
|
78
|
+
readonly familyId: "search-output";
|
|
79
|
+
readonly title: "Canonical, robots and sitemap";
|
|
80
|
+
readonly staticEvidence: true;
|
|
81
|
+
}, {
|
|
82
|
+
readonly id: "structured-data";
|
|
83
|
+
readonly familyId: "search-output";
|
|
84
|
+
readonly title: "Structured data";
|
|
85
|
+
readonly staticEvidence: true;
|
|
86
|
+
}, {
|
|
87
|
+
readonly id: "business-identity";
|
|
88
|
+
readonly familyId: "business-data";
|
|
89
|
+
readonly title: "Business visibility";
|
|
90
|
+
readonly staticEvidence: true;
|
|
91
|
+
}, {
|
|
92
|
+
readonly id: "route-identity";
|
|
93
|
+
readonly familyId: "business-data";
|
|
94
|
+
readonly title: "Route identity";
|
|
95
|
+
readonly staticEvidence: true;
|
|
96
|
+
}, {
|
|
97
|
+
readonly id: "public-output-safety";
|
|
98
|
+
readonly familyId: "output-safety";
|
|
99
|
+
readonly title: "Public output hygiene";
|
|
100
|
+
readonly staticEvidence: true;
|
|
101
|
+
}, {
|
|
102
|
+
readonly id: "source-safety";
|
|
103
|
+
readonly familyId: "output-safety";
|
|
104
|
+
readonly title: "Protected source behaviour";
|
|
105
|
+
readonly staticEvidence: true;
|
|
106
|
+
}, {
|
|
107
|
+
readonly id: "browser-accessibility";
|
|
108
|
+
readonly familyId: "accessibility";
|
|
109
|
+
readonly title: "Browser accessibility";
|
|
110
|
+
readonly staticEvidence: false;
|
|
111
|
+
}, {
|
|
112
|
+
readonly id: "deployment-binding";
|
|
113
|
+
readonly familyId: "deployment-provenance";
|
|
114
|
+
readonly title: "Deployment binding";
|
|
115
|
+
readonly staticEvidence: false;
|
|
116
|
+
}];
|
|
117
|
+
export type BrandlabVerificationCheckId = (typeof BRANDLAB_VERIFICATION_CHECKS)[number]["id"];
|
|
118
|
+
export type BrandlabVerificationStatus = "pass" | "warning" | "blocked" | "not_run";
|
|
119
|
+
export type BrandlabVerificationEvidenceKind = "static" | "test" | "build" | "browser" | "deployment";
|
|
120
|
+
export interface BrandlabVerificationEvidence {
|
|
121
|
+
checkId: BrandlabVerificationCheckId;
|
|
122
|
+
status: BrandlabVerificationStatus;
|
|
123
|
+
evidenceKind: BrandlabVerificationEvidenceKind;
|
|
124
|
+
message: string;
|
|
125
|
+
sourceRuleIds: string[];
|
|
126
|
+
}
|
|
127
|
+
export interface BrandlabOperationalFinding extends BrandlabFinding {
|
|
128
|
+
familyId: BrandlabVerificationFamilyId;
|
|
129
|
+
checkId: BrandlabVerificationCheckId;
|
|
130
|
+
}
|
|
131
|
+
export interface BrandlabVerificationCheckResult extends BrandlabVerificationEvidence {
|
|
132
|
+
title: string;
|
|
133
|
+
blockers: number;
|
|
134
|
+
warnings: number;
|
|
135
|
+
info: number;
|
|
136
|
+
}
|
|
137
|
+
export interface BrandlabVerificationFamilyResult {
|
|
138
|
+
id: BrandlabVerificationFamilyId;
|
|
139
|
+
title: string;
|
|
140
|
+
purpose: string;
|
|
141
|
+
status: BrandlabVerificationStatus;
|
|
142
|
+
blockers: number;
|
|
143
|
+
warnings: number;
|
|
144
|
+
info: number;
|
|
145
|
+
sourceRuleIds: string[];
|
|
146
|
+
checkIds: BrandlabVerificationCheckId[];
|
|
147
|
+
}
|
|
148
|
+
export interface BrandlabVerificationResult {
|
|
149
|
+
policyVersion: typeof BRANDLAB_VERIFICATION_POLICY_VERSION;
|
|
150
|
+
familyCount: typeof BRANDLAB_VERIFICATION_FAMILIES.length;
|
|
151
|
+
checkCount: typeof BRANDLAB_VERIFICATION_CHECKS.length;
|
|
152
|
+
status: BrandlabVerificationStatus;
|
|
153
|
+
summary: ConformanceSummary;
|
|
154
|
+
checks: BrandlabVerificationCheckResult[];
|
|
155
|
+
families: BrandlabVerificationFamilyResult[];
|
|
156
|
+
}
|
|
157
|
+
export declare function applyBrandlabVerificationPolicy(findings: readonly BrandlabFinding[], suppliedEvidence?: readonly BrandlabVerificationEvidence[]): {
|
|
158
|
+
operationalFindings: BrandlabOperationalFinding[];
|
|
159
|
+
verification: BrandlabVerificationResult;
|
|
160
|
+
};
|
|
161
|
+
export declare function summarizeVerificationFindings(findings: readonly BrandlabFinding[]): ConformanceSummary;
|
|
162
|
+
//# sourceMappingURL=verification-policy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verification-policy.d.ts","sourceRoot":"","sources":["../src/verification-policy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAExE,eAAO,MAAM,oCAAoC,cAAc,CAAC;AAEhE,eAAO,MAAM,8BAA8B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA8CjC,CAAC;AAEX,MAAM,MAAM,4BAA4B,GACtC,CAAC,OAAO,8BAA8B,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC;AAExD,eAAO,MAAM,4BAA4B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA2F/B,CAAC;AAEX,MAAM,MAAM,2BAA2B,GACrC,CAAC,OAAO,4BAA4B,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC;AACtD,MAAM,MAAM,0BAA0B,GAClC,MAAM,GACN,SAAS,GACT,SAAS,GACT,SAAS,CAAC;AACd,MAAM,MAAM,gCAAgC,GACxC,QAAQ,GACR,MAAM,GACN,OAAO,GACP,SAAS,GACT,YAAY,CAAC;AAEjB,MAAM,WAAW,4BAA4B;IAC3C,OAAO,EAAE,2BAA2B,CAAC;IACrC,MAAM,EAAE,0BAA0B,CAAC;IACnC,YAAY,EAAE,gCAAgC,CAAC;IAC/C,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,0BAA2B,SAAQ,eAAe;IACjE,QAAQ,EAAE,4BAA4B,CAAC;IACvC,OAAO,EAAE,2BAA2B,CAAC;CACtC;AAED,MAAM,WAAW,+BACf,SAAQ,4BAA4B;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,gCAAgC;IAC/C,EAAE,EAAE,4BAA4B,CAAC;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,0BAA0B,CAAC;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,QAAQ,EAAE,2BAA2B,EAAE,CAAC;CACzC;AAED,MAAM,WAAW,0BAA0B;IACzC,aAAa,EAAE,OAAO,oCAAoC,CAAC;IAC3D,WAAW,EAAE,OAAO,8BAA8B,CAAC,MAAM,CAAC;IAC1D,UAAU,EAAE,OAAO,4BAA4B,CAAC,MAAM,CAAC;IACvD,MAAM,EAAE,0BAA0B,CAAC;IACnC,OAAO,EAAE,kBAAkB,CAAC;IAC5B,MAAM,EAAE,+BAA+B,EAAE,CAAC;IAC1C,QAAQ,EAAE,gCAAgC,EAAE,CAAC;CAC9C;AAED,wBAAgB,+BAA+B,CAC7C,QAAQ,EAAE,SAAS,eAAe,EAAE,EACpC,gBAAgB,GAAE,SAAS,4BAA4B,EAAO,GAC7D;IACD,mBAAmB,EAAE,0BAA0B,EAAE,CAAC;IAClD,YAAY,EAAE,0BAA0B,CAAC;CAC1C,CAiGA;AA0FD,wBAAgB,6BAA6B,CAC3C,QAAQ,EAAE,SAAS,eAAe,EAAE,GACnC,kBAAkB,CAWpB"}
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
export const BRANDLAB_VERIFICATION_POLICY_VERSION = "simple-v1";
|
|
2
|
+
export const BRANDLAB_VERIFICATION_FAMILIES = [
|
|
3
|
+
{
|
|
4
|
+
id: "runtime-release",
|
|
5
|
+
title: "Runtime and release",
|
|
6
|
+
purpose: "Supported runtime and one reproducible Brandlab release.",
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
id: "project-verification",
|
|
10
|
+
title: "Project verification",
|
|
11
|
+
purpose: "Typecheck, tests and production build complete successfully.",
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
id: "contact-delivery",
|
|
15
|
+
title: "Contact delivery",
|
|
16
|
+
purpose: "Shared validation, delivery and provider-error handling.",
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
id: "analytics",
|
|
20
|
+
title: "Analytics",
|
|
21
|
+
purpose: "Canonical conversions run once without lead PII.",
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
id: "search-output",
|
|
25
|
+
title: "Search output",
|
|
26
|
+
purpose: "Search routing and structured data are safe and consistent.",
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
id: "business-data",
|
|
30
|
+
title: "Business data",
|
|
31
|
+
purpose: "Business visibility and route identity match declared facts.",
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
id: "output-safety",
|
|
35
|
+
title: "Output safety",
|
|
36
|
+
purpose: "Public output and protected shared behaviour remain safe.",
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
id: "accessibility",
|
|
40
|
+
title: "Accessibility",
|
|
41
|
+
purpose: "Representative browser routes have no serious accessibility failures.",
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
id: "deployment-provenance",
|
|
45
|
+
title: "Deployment provenance",
|
|
46
|
+
purpose: "The reviewed source, package release and deployment agree.",
|
|
47
|
+
},
|
|
48
|
+
];
|
|
49
|
+
export const BRANDLAB_VERIFICATION_CHECKS = [
|
|
50
|
+
{
|
|
51
|
+
id: "runtime-supported",
|
|
52
|
+
familyId: "runtime-release",
|
|
53
|
+
title: "Supported runtime",
|
|
54
|
+
staticEvidence: true,
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
id: "release-consistency",
|
|
58
|
+
familyId: "runtime-release",
|
|
59
|
+
title: "Release consistency",
|
|
60
|
+
staticEvidence: true,
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
id: "project-build",
|
|
64
|
+
familyId: "project-verification",
|
|
65
|
+
title: "Typecheck, tests and build",
|
|
66
|
+
staticEvidence: false,
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
id: "contact-runtime",
|
|
70
|
+
familyId: "contact-delivery",
|
|
71
|
+
title: "Shared contact runtime",
|
|
72
|
+
staticEvidence: true,
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
id: "contact-provider-errors",
|
|
76
|
+
familyId: "contact-delivery",
|
|
77
|
+
title: "Provider error handling",
|
|
78
|
+
staticEvidence: true,
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
id: "analytics-privacy",
|
|
82
|
+
familyId: "analytics",
|
|
83
|
+
title: "Analytics privacy",
|
|
84
|
+
staticEvidence: true,
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
id: "analytics-conversion",
|
|
88
|
+
familyId: "analytics",
|
|
89
|
+
title: "Canonical conversion events",
|
|
90
|
+
staticEvidence: true,
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
id: "search-routing",
|
|
94
|
+
familyId: "search-output",
|
|
95
|
+
title: "Canonical, robots and sitemap",
|
|
96
|
+
staticEvidence: true,
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
id: "structured-data",
|
|
100
|
+
familyId: "search-output",
|
|
101
|
+
title: "Structured data",
|
|
102
|
+
staticEvidence: true,
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
id: "business-identity",
|
|
106
|
+
familyId: "business-data",
|
|
107
|
+
title: "Business visibility",
|
|
108
|
+
staticEvidence: true,
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
id: "route-identity",
|
|
112
|
+
familyId: "business-data",
|
|
113
|
+
title: "Route identity",
|
|
114
|
+
staticEvidence: true,
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
id: "public-output-safety",
|
|
118
|
+
familyId: "output-safety",
|
|
119
|
+
title: "Public output hygiene",
|
|
120
|
+
staticEvidence: true,
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
id: "source-safety",
|
|
124
|
+
familyId: "output-safety",
|
|
125
|
+
title: "Protected source behaviour",
|
|
126
|
+
staticEvidence: true,
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
id: "browser-accessibility",
|
|
130
|
+
familyId: "accessibility",
|
|
131
|
+
title: "Browser accessibility",
|
|
132
|
+
staticEvidence: false,
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
id: "deployment-binding",
|
|
136
|
+
familyId: "deployment-provenance",
|
|
137
|
+
title: "Deployment binding",
|
|
138
|
+
staticEvidence: false,
|
|
139
|
+
},
|
|
140
|
+
];
|
|
141
|
+
export function applyBrandlabVerificationPolicy(findings, suppliedEvidence = []) {
|
|
142
|
+
const operationalFindings = findings.map((finding) => {
|
|
143
|
+
const checkId = checkForFinding(finding);
|
|
144
|
+
const familyId = familyForCheck(checkId);
|
|
145
|
+
return { ...finding, familyId, checkId };
|
|
146
|
+
});
|
|
147
|
+
const checks = BRANDLAB_VERIFICATION_CHECKS.map((definition) => {
|
|
148
|
+
const scopedFindings = operationalFindings.filter((finding) => finding.checkId === definition.id);
|
|
149
|
+
const scopedEvidence = suppliedEvidence.filter((evidence) => evidence.checkId === definition.id);
|
|
150
|
+
const blockers = scopedFindings.filter((finding) => finding.severity === "blocker").length +
|
|
151
|
+
scopedEvidence.filter((evidence) => evidence.status === "blocked").length;
|
|
152
|
+
const warnings = scopedFindings.filter((finding) => finding.severity === "warning").length +
|
|
153
|
+
scopedEvidence.filter((evidence) => evidence.status === "warning").length;
|
|
154
|
+
const info = scopedFindings.filter((finding) => finding.severity === "info").length;
|
|
155
|
+
const sourceRuleIds = [
|
|
156
|
+
...new Set([
|
|
157
|
+
...scopedFindings.map((finding) => finding.ruleId),
|
|
158
|
+
...scopedEvidence.flatMap((evidence) => evidence.sourceRuleIds),
|
|
159
|
+
]),
|
|
160
|
+
].sort();
|
|
161
|
+
const status = blockers > 0
|
|
162
|
+
? "blocked"
|
|
163
|
+
: warnings > 0
|
|
164
|
+
? "warning"
|
|
165
|
+
: definition.staticEvidence ||
|
|
166
|
+
scopedEvidence.some((evidence) => evidence.status === "pass")
|
|
167
|
+
? "pass"
|
|
168
|
+
: "not_run";
|
|
169
|
+
const evidenceKind = scopedEvidence.at(-1)?.evidenceKind ??
|
|
170
|
+
(definition.staticEvidence ? "static" : expectedEvidenceKind(definition.id));
|
|
171
|
+
const message = blockers > 0
|
|
172
|
+
? `${blockers} blocking finding(s).`
|
|
173
|
+
: warnings > 0
|
|
174
|
+
? `${warnings} warning finding(s).`
|
|
175
|
+
: scopedEvidence.at(-1)?.message ??
|
|
176
|
+
(definition.staticEvidence
|
|
177
|
+
? "The static evaluator completed this check without an operational finding."
|
|
178
|
+
: "This check was not executed by the static evaluator.");
|
|
179
|
+
return {
|
|
180
|
+
checkId: definition.id,
|
|
181
|
+
title: definition.title,
|
|
182
|
+
status,
|
|
183
|
+
evidenceKind,
|
|
184
|
+
message,
|
|
185
|
+
sourceRuleIds,
|
|
186
|
+
blockers,
|
|
187
|
+
warnings,
|
|
188
|
+
info,
|
|
189
|
+
};
|
|
190
|
+
});
|
|
191
|
+
const families = BRANDLAB_VERIFICATION_FAMILIES.map((definition) => {
|
|
192
|
+
const scoped = checks.filter((check) => BRANDLAB_VERIFICATION_CHECKS.some((candidate) => candidate.id === check.checkId && candidate.familyId === definition.id));
|
|
193
|
+
const blockers = scoped.reduce((total, check) => total + check.blockers, 0);
|
|
194
|
+
const warnings = scoped.reduce((total, check) => total + check.warnings, 0);
|
|
195
|
+
const info = scoped.reduce((total, check) => total + check.info, 0);
|
|
196
|
+
return {
|
|
197
|
+
...definition,
|
|
198
|
+
status: aggregateStatus(scoped.map((check) => check.status)),
|
|
199
|
+
blockers,
|
|
200
|
+
warnings,
|
|
201
|
+
info,
|
|
202
|
+
sourceRuleIds: [...new Set(scoped.flatMap((check) => check.sourceRuleIds))].sort(),
|
|
203
|
+
checkIds: scoped.map((check) => check.checkId),
|
|
204
|
+
};
|
|
205
|
+
});
|
|
206
|
+
const summary = summarizeVerificationFindings(operationalFindings);
|
|
207
|
+
return {
|
|
208
|
+
operationalFindings,
|
|
209
|
+
verification: {
|
|
210
|
+
policyVersion: BRANDLAB_VERIFICATION_POLICY_VERSION,
|
|
211
|
+
familyCount: BRANDLAB_VERIFICATION_FAMILIES.length,
|
|
212
|
+
checkCount: BRANDLAB_VERIFICATION_CHECKS.length,
|
|
213
|
+
status: aggregateStatus(families.map((family) => family.status)),
|
|
214
|
+
summary,
|
|
215
|
+
checks,
|
|
216
|
+
families,
|
|
217
|
+
},
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
function familyForCheck(checkId) {
|
|
221
|
+
return BRANDLAB_VERIFICATION_CHECKS.find((check) => check.id === checkId)
|
|
222
|
+
.familyId;
|
|
223
|
+
}
|
|
224
|
+
function checkForFinding(finding) {
|
|
225
|
+
const id = finding.ruleId;
|
|
226
|
+
const surface = finding.surface;
|
|
227
|
+
if (surface === "release")
|
|
228
|
+
return "release-consistency";
|
|
229
|
+
if (surface === "runtime" ||
|
|
230
|
+
surface === "headers" ||
|
|
231
|
+
surface === "security" ||
|
|
232
|
+
surface === "environment") {
|
|
233
|
+
return "runtime-supported";
|
|
234
|
+
}
|
|
235
|
+
if (surface === "project-verification" || surface === "build") {
|
|
236
|
+
return "project-build";
|
|
237
|
+
}
|
|
238
|
+
if (surface.startsWith("contact")) {
|
|
239
|
+
return /(?:provider|resend|returned-error|delivery-error)/i.test(id)
|
|
240
|
+
? "contact-provider-errors"
|
|
241
|
+
: "contact-runtime";
|
|
242
|
+
}
|
|
243
|
+
if (surface === "analytics") {
|
|
244
|
+
return /\b(?:pii|privacy|email|phone-number|lead-diagnostic|public-env)\b/i.test(id)
|
|
245
|
+
? "analytics-privacy"
|
|
246
|
+
: "analytics-conversion";
|
|
247
|
+
}
|
|
248
|
+
if (surface === "metadata" ||
|
|
249
|
+
surface === "sitemap" ||
|
|
250
|
+
surface === "sitemap-catalog" ||
|
|
251
|
+
surface === "robots" ||
|
|
252
|
+
surface === "breadcrumbs" ||
|
|
253
|
+
surface === "search-certification") {
|
|
254
|
+
return "search-routing";
|
|
255
|
+
}
|
|
256
|
+
if (surface === "schema" ||
|
|
257
|
+
surface === "schema-graph" ||
|
|
258
|
+
surface === "reviews" ||
|
|
259
|
+
surface === "project-reviews" ||
|
|
260
|
+
surface.startsWith("google-reviews")) {
|
|
261
|
+
return "structured-data";
|
|
262
|
+
}
|
|
263
|
+
if (surface === "business" || surface === "location-proof") {
|
|
264
|
+
return "business-identity";
|
|
265
|
+
}
|
|
266
|
+
if (surface === "routes")
|
|
267
|
+
return "route-identity";
|
|
268
|
+
if (surface === "accessibility")
|
|
269
|
+
return "browser-accessibility";
|
|
270
|
+
if (surface === "deployment")
|
|
271
|
+
return "deployment-binding";
|
|
272
|
+
if (surface === "content" ||
|
|
273
|
+
surface === "location-quality" ||
|
|
274
|
+
id.includes("rendered-content")) {
|
|
275
|
+
return "public-output-safety";
|
|
276
|
+
}
|
|
277
|
+
return "source-safety";
|
|
278
|
+
}
|
|
279
|
+
function expectedEvidenceKind(checkId) {
|
|
280
|
+
if (checkId === "project-build")
|
|
281
|
+
return "build";
|
|
282
|
+
if (checkId === "browser-accessibility")
|
|
283
|
+
return "browser";
|
|
284
|
+
if (checkId === "deployment-binding")
|
|
285
|
+
return "deployment";
|
|
286
|
+
return "static";
|
|
287
|
+
}
|
|
288
|
+
function aggregateStatus(statuses) {
|
|
289
|
+
if (statuses.includes("blocked"))
|
|
290
|
+
return "blocked";
|
|
291
|
+
if (statuses.includes("warning"))
|
|
292
|
+
return "warning";
|
|
293
|
+
if (statuses.includes("not_run"))
|
|
294
|
+
return "not_run";
|
|
295
|
+
return "pass";
|
|
296
|
+
}
|
|
297
|
+
export function summarizeVerificationFindings(findings) {
|
|
298
|
+
const blockers = findings.filter((finding) => finding.severity === "blocker").length;
|
|
299
|
+
const warnings = findings.filter((finding) => finding.severity === "warning").length;
|
|
300
|
+
const info = findings.filter((finding) => finding.severity === "info").length;
|
|
301
|
+
return {
|
|
302
|
+
status: blockers > 0 ? "blocked" : warnings > 0 ? "warning" : "pass",
|
|
303
|
+
total: findings.length,
|
|
304
|
+
blockers,
|
|
305
|
+
warnings,
|
|
306
|
+
info,
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
//# sourceMappingURL=verification-policy.js.map
|