@brandlab-digital/conformance 3.7.0 → 3.8.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/README.md +26 -0
- package/dist/current-release.js +5 -5
- package/dist/current-release.js.map +1 -1
- package/dist/public-copy.d.ts +7 -0
- package/dist/public-copy.d.ts.map +1 -1
- package/dist/public-copy.js +8 -3
- package/dist/public-copy.js.map +1 -1
- package/dist/ready-cli.d.ts +38 -0
- package/dist/ready-cli.d.ts.map +1 -0
- package/dist/ready-cli.js +327 -0
- package/dist/ready-cli.js.map +1 -0
- package/dist/site-system/checks.d.ts +389 -0
- package/dist/site-system/checks.d.ts.map +1 -0
- package/dist/site-system/checks.js +96 -0
- package/dist/site-system/checks.js.map +1 -0
- package/dist/site-system/config.d.ts +40 -0
- package/dist/site-system/config.d.ts.map +1 -0
- package/dist/site-system/config.js +214 -0
- package/dist/site-system/config.js.map +1 -0
- package/dist/site-system/index.d.ts +14 -0
- package/dist/site-system/index.d.ts.map +1 -0
- package/dist/site-system/index.js +14 -0
- package/dist/site-system/index.js.map +1 -0
- package/dist/site-system/journeys/access.d.ts +7 -0
- package/dist/site-system/journeys/access.d.ts.map +1 -0
- package/dist/site-system/journeys/access.js +263 -0
- package/dist/site-system/journeys/access.js.map +1 -0
- package/dist/site-system/journeys/browser.d.ts +79 -0
- package/dist/site-system/journeys/browser.d.ts.map +1 -0
- package/dist/site-system/journeys/browser.js +218 -0
- package/dist/site-system/journeys/browser.js.map +1 -0
- package/dist/site-system/journeys/conversion.d.ts +3 -0
- package/dist/site-system/journeys/conversion.d.ts.map +1 -0
- package/dist/site-system/journeys/conversion.js +152 -0
- package/dist/site-system/journeys/conversion.js.map +1 -0
- package/dist/site-system/journeys/menu.d.ts +11 -0
- package/dist/site-system/journeys/menu.d.ts.map +1 -0
- package/dist/site-system/journeys/menu.js +395 -0
- package/dist/site-system/journeys/menu.js.map +1 -0
- package/dist/site-system/journeys/page-helpers.d.ts +146 -0
- package/dist/site-system/journeys/page-helpers.d.ts.map +1 -0
- package/dist/site-system/journeys/page-helpers.js +426 -0
- package/dist/site-system/journeys/page-helpers.js.map +1 -0
- package/dist/site-system/journeys/plan.d.ts +82 -0
- package/dist/site-system/journeys/plan.d.ts.map +1 -0
- package/dist/site-system/journeys/plan.js +112 -0
- package/dist/site-system/journeys/plan.js.map +1 -0
- package/dist/site-system/journeys/shell.d.ts +24 -0
- package/dist/site-system/journeys/shell.d.ts.map +1 -0
- package/dist/site-system/journeys/shell.js +269 -0
- package/dist/site-system/journeys/shell.js.map +1 -0
- package/dist/site-system/playwright-report.d.ts +22 -0
- package/dist/site-system/playwright-report.d.ts.map +1 -0
- package/dist/site-system/playwright-report.js +131 -0
- package/dist/site-system/playwright-report.js.map +1 -0
- package/dist/site-system/records.d.ts +30 -0
- package/dist/site-system/records.d.ts.map +1 -0
- package/dist/site-system/records.js +98 -0
- package/dist/site-system/records.js.map +1 -0
- package/dist/site-system/report.d.ts +116 -0
- package/dist/site-system/report.d.ts.map +1 -0
- package/dist/site-system/report.js +366 -0
- package/dist/site-system/report.js.map +1 -0
- package/dist/site-system/rule-modules.d.ts +8 -0
- package/dist/site-system/rule-modules.d.ts.map +1 -0
- package/dist/site-system/rule-modules.js +12 -0
- package/dist/site-system/rule-modules.js.map +1 -0
- package/dist/site-system/rules/design/d4-distinctness.d.ts +36 -0
- package/dist/site-system/rules/design/d4-distinctness.d.ts.map +1 -0
- package/dist/site-system/rules/design/d4-distinctness.js +207 -0
- package/dist/site-system/rules/design/d4-distinctness.js.map +1 -0
- package/dist/site-system/rules/design/index.d.ts +17 -0
- package/dist/site-system/rules/design/index.d.ts.map +1 -0
- package/dist/site-system/rules/design/index.js +7 -0
- package/dist/site-system/rules/design/index.js.map +1 -0
- package/dist/site-system/rules/enquiry/c2-quote-form.d.ts +31 -0
- package/dist/site-system/rules/enquiry/c2-quote-form.d.ts.map +1 -0
- package/dist/site-system/rules/enquiry/c2-quote-form.js +211 -0
- package/dist/site-system/rules/enquiry/c2-quote-form.js.map +1 -0
- package/dist/site-system/rules/enquiry/common.d.ts +37 -0
- package/dist/site-system/rules/enquiry/common.d.ts.map +1 -0
- package/dist/site-system/rules/enquiry/common.js +59 -0
- package/dist/site-system/rules/enquiry/common.js.map +1 -0
- package/dist/site-system/rules/enquiry/e1-recorded-titles.d.ts +8 -0
- package/dist/site-system/rules/enquiry/e1-recorded-titles.d.ts.map +1 -0
- package/dist/site-system/rules/enquiry/e1-recorded-titles.js +59 -0
- package/dist/site-system/rules/enquiry/e1-recorded-titles.js.map +1 -0
- package/dist/site-system/rules/enquiry/e9-cost-guides.d.ts +22 -0
- package/dist/site-system/rules/enquiry/e9-cost-guides.d.ts.map +1 -0
- package/dist/site-system/rules/enquiry/e9-cost-guides.js +55 -0
- package/dist/site-system/rules/enquiry/e9-cost-guides.js.map +1 -0
- package/dist/site-system/rules/enquiry/html.d.ts +53 -0
- package/dist/site-system/rules/enquiry/html.d.ts.map +1 -0
- package/dist/site-system/rules/enquiry/html.js +294 -0
- package/dist/site-system/rules/enquiry/html.js.map +1 -0
- package/dist/site-system/rules/enquiry/index.d.ts +17 -0
- package/dist/site-system/rules/enquiry/index.d.ts.map +1 -0
- package/dist/site-system/rules/enquiry/index.js +26 -0
- package/dist/site-system/rules/enquiry/index.js.map +1 -0
- package/dist/site-system/rules/enquiry/k8-sections.d.ts +17 -0
- package/dist/site-system/rules/enquiry/k8-sections.d.ts.map +1 -0
- package/dist/site-system/rules/enquiry/k8-sections.js +76 -0
- package/dist/site-system/rules/enquiry/k8-sections.js.map +1 -0
- package/dist/site-system/rules/enquiry/literal.d.ts +29 -0
- package/dist/site-system/rules/enquiry/literal.d.ts.map +1 -0
- package/dist/site-system/rules/enquiry/literal.js +189 -0
- package/dist/site-system/rules/enquiry/literal.js.map +1 -0
- package/dist/site-system/rules/enquiry/t11-licence-classes.d.ts +73 -0
- package/dist/site-system/rules/enquiry/t11-licence-classes.d.ts.map +1 -0
- package/dist/site-system/rules/enquiry/t11-licence-classes.js +275 -0
- package/dist/site-system/rules/enquiry/t11-licence-classes.js.map +1 -0
- package/dist/site-system/rules/enquiry/t5-reviews.d.ts +23 -0
- package/dist/site-system/rules/enquiry/t5-reviews.d.ts.map +1 -0
- package/dist/site-system/rules/enquiry/t5-reviews.js +91 -0
- package/dist/site-system/rules/enquiry/t5-reviews.js.map +1 -0
- package/dist/site-system/rules/shell/banned-wording.d.ts +24 -0
- package/dist/site-system/rules/shell/banned-wording.d.ts.map +1 -0
- package/dist/site-system/rules/shell/banned-wording.js +212 -0
- package/dist/site-system/rules/shell/banned-wording.js.map +1 -0
- package/dist/site-system/rules/shell/budgets.d.ts +43 -0
- package/dist/site-system/rules/shell/budgets.d.ts.map +1 -0
- package/dist/site-system/rules/shell/budgets.js +297 -0
- package/dist/site-system/rules/shell/budgets.js.map +1 -0
- package/dist/site-system/rules/shell/cta-labels.d.ts +17 -0
- package/dist/site-system/rules/shell/cta-labels.d.ts.map +1 -0
- package/dist/site-system/rules/shell/cta-labels.js +86 -0
- package/dist/site-system/rules/shell/cta-labels.js.map +1 -0
- package/dist/site-system/rules/shell/facts.d.ts +33 -0
- package/dist/site-system/rules/shell/facts.d.ts.map +1 -0
- package/dist/site-system/rules/shell/facts.js +217 -0
- package/dist/site-system/rules/shell/facts.js.map +1 -0
- package/dist/site-system/rules/shell/headings.d.ts +5 -0
- package/dist/site-system/rules/shell/headings.d.ts.map +1 -0
- package/dist/site-system/rules/shell/headings.js +23 -0
- package/dist/site-system/rules/shell/headings.js.map +1 -0
- package/dist/site-system/rules/shell/hero-location.d.ts +4 -0
- package/dist/site-system/rules/shell/hero-location.d.ts.map +1 -0
- package/dist/site-system/rules/shell/hero-location.js +45 -0
- package/dist/site-system/rules/shell/hero-location.js.map +1 -0
- package/dist/site-system/rules/shell/html.d.ts +53 -0
- package/dist/site-system/rules/shell/html.d.ts.map +1 -0
- package/dist/site-system/rules/shell/html.js +341 -0
- package/dist/site-system/rules/shell/html.js.map +1 -0
- package/dist/site-system/rules/shell/index.d.ts +18 -0
- package/dist/site-system/rules/shell/index.d.ts.map +1 -0
- package/dist/site-system/rules/shell/index.js +65 -0
- package/dist/site-system/rules/shell/index.js.map +1 -0
- package/dist/site-system/rules/shell/licence.d.ts +4 -0
- package/dist/site-system/rules/shell/licence.d.ts.map +1 -0
- package/dist/site-system/rules/shell/licence.js +60 -0
- package/dist/site-system/rules/shell/licence.js.map +1 -0
- package/dist/site-system/rules/shell/location-wording.d.ts +13 -0
- package/dist/site-system/rules/shell/location-wording.d.ts.map +1 -0
- package/dist/site-system/rules/shell/location-wording.js +15 -0
- package/dist/site-system/rules/shell/location-wording.js.map +1 -0
- package/dist/site-system/rules/shell/navigation.d.ts +5 -0
- package/dist/site-system/rules/shell/navigation.d.ts.map +1 -0
- package/dist/site-system/rules/shell/navigation.js +272 -0
- package/dist/site-system/rules/shell/navigation.js.map +1 -0
- package/dist/site-system/rules/shell/overlays.d.ts +4 -0
- package/dist/site-system/rules/shell/overlays.d.ts.map +1 -0
- package/dist/site-system/rules/shell/overlays.js +93 -0
- package/dist/site-system/rules/shell/overlays.js.map +1 -0
- package/dist/site-system/rules/shell/page.d.ts +51 -0
- package/dist/site-system/rules/shell/page.d.ts.map +1 -0
- package/dist/site-system/rules/shell/page.js +256 -0
- package/dist/site-system/rules/shell/page.js.map +1 -0
- package/dist/site-system/rules/shell/phone.d.ts +13 -0
- package/dist/site-system/rules/shell/phone.d.ts.map +1 -0
- package/dist/site-system/rules/shell/phone.js +99 -0
- package/dist/site-system/rules/shell/phone.js.map +1 -0
- package/dist/site-system/rules/shell/proof-numbers.d.ts +7 -0
- package/dist/site-system/rules/shell/proof-numbers.d.ts.map +1 -0
- package/dist/site-system/rules/shell/proof-numbers.js +81 -0
- package/dist/site-system/rules/shell/proof-numbers.js.map +1 -0
- package/dist/site-system/rules/shell/types.d.ts +80 -0
- package/dist/site-system/rules/shell/types.d.ts.map +1 -0
- package/dist/site-system/rules/shell/types.js +17 -0
- package/dist/site-system/rules/shell/types.js.map +1 -0
- package/dist/site-system/runners.d.ts +37 -0
- package/dist/site-system/runners.d.ts.map +1 -0
- package/dist/site-system/runners.js +48 -0
- package/dist/site-system/runners.js.map +1 -0
- package/dist/site-system/samples.d.ts +43 -0
- package/dist/site-system/samples.d.ts.map +1 -0
- package/dist/site-system/samples.js +99 -0
- package/dist/site-system/samples.js.map +1 -0
- package/dist/site-system/steps.d.ts +66 -0
- package/dist/site-system/steps.d.ts.map +1 -0
- package/dist/site-system/steps.js +45 -0
- package/dist/site-system/steps.js.map +1 -0
- package/dist/site-system/streamed-html.d.ts +9 -0
- package/dist/site-system/streamed-html.d.ts.map +1 -0
- package/dist/site-system/streamed-html.js +43 -0
- package/dist/site-system/streamed-html.js.map +1 -0
- package/dist/site-system-e2e.d.ts +6 -0
- package/dist/site-system-e2e.d.ts.map +1 -0
- package/dist/site-system-e2e.js +54 -0
- package/dist/site-system-e2e.js.map +1 -0
- package/package.json +22 -5
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
import { SITE_SYSTEM_CHECKS, SITE_SYSTEM_TIERS, siteSystemCheckBlocks, } from "./checks.js";
|
|
2
|
+
import { latestSiteSystemRecords, SITE_SYSTEM_RECORD_PATHS } from "./records.js";
|
|
3
|
+
import { SITE_SYSTEM_HUMAN_STEPS, SITE_SYSTEM_LAUNCH_STEPS, SITE_SYSTEM_RECORDED_CHECK_IDS, } from "./steps.js";
|
|
4
|
+
export const SITE_SYSTEM_READY_SCHEMA = "brandlab.ready.v1";
|
|
5
|
+
export const SITE_SYSTEM_READY_REPORT_PATHS = {
|
|
6
|
+
markdown: "docs/ready/ready-report.md",
|
|
7
|
+
json: "docs/ready/ready-report.json",
|
|
8
|
+
};
|
|
9
|
+
const VERIFY_FAMILIES = ["search-output", "contact-delivery", "analytics"];
|
|
10
|
+
const VERIFY_SCOPE = { tier: "shell", from: "3.8.0" };
|
|
11
|
+
export function buildSiteSystemReadyReport(input) {
|
|
12
|
+
const problems = [...input.problems];
|
|
13
|
+
const settings = input.siteSystem;
|
|
14
|
+
const site = settings && input.release
|
|
15
|
+
? { tier: settings.tier, release: input.release, newBuild: settings.newBuild }
|
|
16
|
+
: null;
|
|
17
|
+
if (settings && !input.release)
|
|
18
|
+
problems.push("The installed Brandlab release is unknown, so no check could block.");
|
|
19
|
+
const launchLatest = latestSiteSystemRecords(input.launchRecords);
|
|
20
|
+
const humanLatest = latestSiteSystemRecords(input.humanRecords);
|
|
21
|
+
const launchIds = new Set([...SITE_SYSTEM_LAUNCH_STEPS.map((step) => step.id), ...SITE_SYSTEM_RECORDED_CHECK_IDS]);
|
|
22
|
+
const humanIds = new Set(SITE_SYSTEM_HUMAN_STEPS.map((step) => step.id));
|
|
23
|
+
for (const id of launchLatest.keys()) {
|
|
24
|
+
if (!launchIds.has(id))
|
|
25
|
+
problems.push(`${SITE_SYSTEM_RECORD_PATHS.launch} records ${id}, which is not a launch-day step or recorded check, so it was ignored.`);
|
|
26
|
+
}
|
|
27
|
+
for (const id of humanLatest.keys()) {
|
|
28
|
+
if (!humanIds.has(id))
|
|
29
|
+
problems.push(`${SITE_SYSTEM_RECORD_PATHS.human} records ${id}, which is not a human step, so it was ignored.`);
|
|
30
|
+
}
|
|
31
|
+
const findings = [...input.findings];
|
|
32
|
+
for (const id of SITE_SYSTEM_RECORDED_CHECK_IDS) {
|
|
33
|
+
const record = launchLatest.get(id);
|
|
34
|
+
if (record)
|
|
35
|
+
findings.push({ checkId: id, status: "pass", message: `Recorded ${recordLine(record)}` });
|
|
36
|
+
}
|
|
37
|
+
const exceptionsByRule = new Map();
|
|
38
|
+
for (const exception of input.exceptions) {
|
|
39
|
+
if (!exceptionsByRule.has(exception.ruleId))
|
|
40
|
+
exceptionsByRule.set(exception.ruleId, exception);
|
|
41
|
+
}
|
|
42
|
+
const knownIds = new Set([...SITE_SYSTEM_CHECKS.map((check) => check.id), ...VERIFY_FAMILIES.map((id) => `verify:${id}`)]);
|
|
43
|
+
for (const ruleId of exceptionsByRule.keys()) {
|
|
44
|
+
if (!knownIds.has(ruleId))
|
|
45
|
+
problems.push(`The exception for ${ruleId} names no ready-gate check, so the gate ignored it.`);
|
|
46
|
+
}
|
|
47
|
+
const usedExceptions = [];
|
|
48
|
+
const applyException = (id, blocks, status, messages) => {
|
|
49
|
+
const exception = exceptionsByRule.get(id);
|
|
50
|
+
if (!exception)
|
|
51
|
+
return { status, messages };
|
|
52
|
+
const expired = isExceptionExpired(exception.expiresAt, input.now);
|
|
53
|
+
const unresolved = status === "fail" || status === "not-run";
|
|
54
|
+
const applied = blocks && unresolved && !expired;
|
|
55
|
+
const reason = !blocks ? "the check does not block" : status === "not-applicable" ? "the check does not apply" : "the check passes";
|
|
56
|
+
usedExceptions.push({
|
|
57
|
+
...exception,
|
|
58
|
+
expired,
|
|
59
|
+
applied,
|
|
60
|
+
state: expired ? "expired" : applied ? "applied" : "not-needed",
|
|
61
|
+
...(expired || applied ? {} : { reason }),
|
|
62
|
+
});
|
|
63
|
+
if (!unresolved)
|
|
64
|
+
return { status, messages };
|
|
65
|
+
if (expired)
|
|
66
|
+
return { status, messages: [`The exception expired on ${exception.expiresAt}, so it no longer counts.`, ...messages] };
|
|
67
|
+
if (!applied)
|
|
68
|
+
return { status, messages };
|
|
69
|
+
const until = exception.expiresAt ? ` until ${exception.expiresAt}` : "";
|
|
70
|
+
return { status: "excepted", messages: [`Excepted by Romie${until}: ${exception.rationale}`, ...messages] };
|
|
71
|
+
};
|
|
72
|
+
const checks = SITE_SYSTEM_CHECKS.map((check) => {
|
|
73
|
+
const blocks = site ? siteSystemCheckBlocks(check, site) : false;
|
|
74
|
+
const merged = mergeFindings(findings.filter((finding) => finding.checkId === check.id), notRunMessage(check));
|
|
75
|
+
const { status, messages } = applyException(check.id, blocks, merged.status, merged.messages);
|
|
76
|
+
return {
|
|
77
|
+
id: check.id,
|
|
78
|
+
summary: check.summary,
|
|
79
|
+
tier: check.tier,
|
|
80
|
+
from: check.from,
|
|
81
|
+
kind: check.kind,
|
|
82
|
+
blocks,
|
|
83
|
+
...(blocks ? {} : { reportsOnly: reportsOnlyReason(check, settings) }),
|
|
84
|
+
status,
|
|
85
|
+
messages,
|
|
86
|
+
warnings: merged.warnings,
|
|
87
|
+
};
|
|
88
|
+
});
|
|
89
|
+
for (const familyId of VERIFY_FAMILIES) {
|
|
90
|
+
const id = `verify:${familyId}`;
|
|
91
|
+
const family = input.verify?.verification.families.find((candidate) => candidate.id === familyId);
|
|
92
|
+
const blocks = site ? siteSystemCheckBlocks({ id, ...VERIFY_SCOPE }, site) : false;
|
|
93
|
+
const merged = verifyStatus(family, input.verify?.operationalFindings ?? []);
|
|
94
|
+
const { status, messages } = applyException(id, blocks, merged.status, merged.messages);
|
|
95
|
+
checks.push({
|
|
96
|
+
id,
|
|
97
|
+
summary: family ? `${family.title} (brandlab-verify): ${family.purpose}` : `The brandlab-verify ${familyId} checks.`,
|
|
98
|
+
tier: VERIFY_SCOPE.tier,
|
|
99
|
+
from: VERIFY_SCOPE.from,
|
|
100
|
+
kind: "verify",
|
|
101
|
+
blocks,
|
|
102
|
+
...(blocks ? {} : { reportsOnly: reportsOnlyReason(VERIFY_SCOPE, settings) }),
|
|
103
|
+
status,
|
|
104
|
+
messages,
|
|
105
|
+
warnings: merged.warnings,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
const steps = [
|
|
109
|
+
...SITE_SYSTEM_LAUNCH_STEPS.map((step) => readyStep(step, "launch", launchLatest.get(step.id))),
|
|
110
|
+
...SITE_SYSTEM_HUMAN_STEPS.map((step) => readyStep(step, "human", humanLatest.get(step.id))),
|
|
111
|
+
];
|
|
112
|
+
const verdict = readyVerdict({
|
|
113
|
+
settings,
|
|
114
|
+
release: input.release,
|
|
115
|
+
checks,
|
|
116
|
+
steps,
|
|
117
|
+
launch: input.launch === true,
|
|
118
|
+
blockers: input.blockers ?? [],
|
|
119
|
+
});
|
|
120
|
+
return {
|
|
121
|
+
schemaVersion: SITE_SYSTEM_READY_SCHEMA,
|
|
122
|
+
generatedAt: input.now.toISOString(),
|
|
123
|
+
projectId: input.projectId,
|
|
124
|
+
release: input.release,
|
|
125
|
+
tier: settings?.tier ?? null,
|
|
126
|
+
newBuild: settings?.newBuild ?? false,
|
|
127
|
+
launch: input.launch === true,
|
|
128
|
+
verdict,
|
|
129
|
+
checks,
|
|
130
|
+
steps,
|
|
131
|
+
exceptions: usedExceptions,
|
|
132
|
+
inputs: input.inputs,
|
|
133
|
+
problems,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Any fail fails the check; otherwise any pass passes it; only not-applicable results make it not applicable. A
|
|
138
|
+
* warning is a pass that carries its message as a warning, whatever status it names.
|
|
139
|
+
*/
|
|
140
|
+
function mergeFindings(findings, notRun) {
|
|
141
|
+
const warnings = [...new Set(findings.filter((finding) => finding.severity === "warning").map((finding) => finding.message))];
|
|
142
|
+
const results = findings.filter((finding) => finding.severity !== "warning");
|
|
143
|
+
if (warnings.length > 0 && !results.some((finding) => finding.status !== "not-applicable")) {
|
|
144
|
+
return { status: "pass", messages: [], warnings };
|
|
145
|
+
}
|
|
146
|
+
for (const status of ["fail", "pass", "not-applicable"]) {
|
|
147
|
+
const scoped = results.filter((finding) => finding.status === status);
|
|
148
|
+
if (scoped.length > 0)
|
|
149
|
+
return { status, messages: [...new Set(scoped.map((finding) => finding.message))], warnings };
|
|
150
|
+
}
|
|
151
|
+
return { status: "not-run", messages: [notRun], warnings };
|
|
152
|
+
}
|
|
153
|
+
function verifyStatus(family, findings) {
|
|
154
|
+
if (!family)
|
|
155
|
+
return { status: "not-run", messages: ["brandlab-verify did not run, so these checks have no result."], warnings: [] };
|
|
156
|
+
const scoped = findings
|
|
157
|
+
.filter((finding) => finding.familyId === family.id)
|
|
158
|
+
.map((finding) => {
|
|
159
|
+
const location = finding.path ? ` (${finding.path}${finding.line ? `:${finding.line}` : ""})` : "";
|
|
160
|
+
return { severity: finding.severity, message: `${finding.ruleId}${location}: ${finding.actual}` };
|
|
161
|
+
});
|
|
162
|
+
const warnings = scoped.filter((finding) => finding.severity === "warning").map((finding) => finding.message);
|
|
163
|
+
if (family.status === "blocked") {
|
|
164
|
+
return { status: "fail", messages: scoped.filter((finding) => finding.severity === "blocker").map((finding) => finding.message), warnings };
|
|
165
|
+
}
|
|
166
|
+
if (family.status === "not_run")
|
|
167
|
+
return { status: "not-applicable", messages: ["No brandlab-verify rule in this family applies to this project."], warnings };
|
|
168
|
+
return { status: "pass", messages: [`brandlab-verify found nothing to fix in ${family.title.toLowerCase()}.`], warnings };
|
|
169
|
+
}
|
|
170
|
+
function notRunMessage(check) {
|
|
171
|
+
switch (check.kind) {
|
|
172
|
+
case "journey":
|
|
173
|
+
return "No journey result: run the site-system journeys (npm run test:e2e) before brandlab-ready.";
|
|
174
|
+
case "rendered":
|
|
175
|
+
return "No result: no rule module reported this check for the sampled pages.";
|
|
176
|
+
case "static":
|
|
177
|
+
return "No result: no rule module reported this check.";
|
|
178
|
+
case "record":
|
|
179
|
+
return `Not recorded: run brandlab-ready record ${check.id} --evidence <where the proof is>.`;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
function reportsOnlyReason(check, settings) {
|
|
183
|
+
if (!settings)
|
|
184
|
+
return "no tier declared";
|
|
185
|
+
if (check.tier === "new-build")
|
|
186
|
+
return settings.newBuild ? "blocks new builds from 3.9.0" : "new builds and redesigns only";
|
|
187
|
+
return SITE_SYSTEM_TIERS.indexOf(check.tier) <= SITE_SYSTEM_TIERS.indexOf(settings.tier)
|
|
188
|
+
? `blocks from ${check.from}`
|
|
189
|
+
: `${check.tier} tier`;
|
|
190
|
+
}
|
|
191
|
+
function readyStep(step, file, record) {
|
|
192
|
+
return {
|
|
193
|
+
id: step.id,
|
|
194
|
+
summary: step.summary,
|
|
195
|
+
file,
|
|
196
|
+
recorded: Boolean(record),
|
|
197
|
+
...(record
|
|
198
|
+
? { recordedAt: record.recordedAt, evidence: record.evidence, ...(record.note ? { note: record.note } : {}) }
|
|
199
|
+
: {}),
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
function readyVerdict(input) {
|
|
203
|
+
const blocking = input.checks.filter((check) => check.blocks);
|
|
204
|
+
const failed = blocking.filter((check) => check.status === "fail").map((check) => check.id);
|
|
205
|
+
const notRun = blocking.filter((check) => check.status === "not-run").map((check) => check.id);
|
|
206
|
+
const excepted = blocking.filter((check) => check.status === "excepted").map((check) => check.id);
|
|
207
|
+
const stepsToRecord = input.steps.filter((step) => !step.recorded).map((step) => step.id);
|
|
208
|
+
const adopted = input.settings !== null;
|
|
209
|
+
const ready = adopted && input.release !== null && blocking.length > 0 && failed.length === 0 &&
|
|
210
|
+
notRun.length === 0 && input.blockers.length === 0;
|
|
211
|
+
const launchReady = ready && stepsToRecord.length === 0;
|
|
212
|
+
const launchSteps = input.steps.filter((step) => !step.recorded && step.file === "launch").length;
|
|
213
|
+
const humanSteps = input.steps.filter((step) => !step.recorded && step.file === "human").length;
|
|
214
|
+
const stepCount = joinParts([
|
|
215
|
+
launchSteps > 0 ? `${launchSteps} launch-day` : "",
|
|
216
|
+
humanSteps > 0 ? `${humanSteps} human` : "",
|
|
217
|
+
]);
|
|
218
|
+
const stepNoun = launchSteps + humanSteps === 1 ? "step" : "steps";
|
|
219
|
+
const reasons = [
|
|
220
|
+
failed.length > 0 ? `${plural(failed.length, "blocking check")} failed` : "",
|
|
221
|
+
notRun.length > 0 ? (failed.length > 0 ? `${notRun.length} did not run` : `${plural(notRun.length, "blocking check")} did not run`) : "",
|
|
222
|
+
...input.blockers,
|
|
223
|
+
].filter(Boolean);
|
|
224
|
+
let summary;
|
|
225
|
+
let exitCode = 1;
|
|
226
|
+
if (input.blockers.length > 0 || (adopted && reasons.length > 0)) {
|
|
227
|
+
summary = `Not ready: ${joinParts(reasons)}`;
|
|
228
|
+
}
|
|
229
|
+
else if (!adopted) {
|
|
230
|
+
summary = "Not adopted: brandlab.config.ts declares no siteSystem tier, so no check blocks yet";
|
|
231
|
+
exitCode = 0;
|
|
232
|
+
}
|
|
233
|
+
else if (input.release === null) {
|
|
234
|
+
summary = "Not ready: the installed Brandlab release is unknown";
|
|
235
|
+
}
|
|
236
|
+
else if (input.launch && stepsToRecord.length > 0) {
|
|
237
|
+
summary = `Not ready: ${stepCount} ${stepNoun} not recorded`;
|
|
238
|
+
}
|
|
239
|
+
else if (blocking.length === 0) {
|
|
240
|
+
summary = `No check blocks on release ${input.release}`;
|
|
241
|
+
exitCode = 0;
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
summary = launchReady
|
|
245
|
+
? `Ready at ${input.settings.tier} tier`
|
|
246
|
+
: `Checks pass at ${input.settings.tier} tier; before launch, record ${stepCount} ${stepNoun}`;
|
|
247
|
+
if (excepted.length > 0)
|
|
248
|
+
summary += ` (${plural(excepted.length, "check")} excepted)`;
|
|
249
|
+
exitCode = 0;
|
|
250
|
+
}
|
|
251
|
+
return { ready, launchReady, exitCode, summary, failed, notRun, excepted, blockers: input.blockers, stepsToRecord };
|
|
252
|
+
}
|
|
253
|
+
/** Date-only expiries last to the end of that day in Sydney; timestamps expire at that instant. */
|
|
254
|
+
export function isExceptionExpired(expiresAt, now) {
|
|
255
|
+
if (!expiresAt)
|
|
256
|
+
return false;
|
|
257
|
+
if (/^\d{4}-\d{2}-\d{2}$/.test(expiresAt))
|
|
258
|
+
return sydneyDate(now) > expiresAt;
|
|
259
|
+
const time = Date.parse(expiresAt);
|
|
260
|
+
return Number.isNaN(time) || time <= now.getTime();
|
|
261
|
+
}
|
|
262
|
+
const SYDNEY_DATE = new Intl.DateTimeFormat("en-CA", {
|
|
263
|
+
timeZone: "Australia/Sydney",
|
|
264
|
+
year: "numeric",
|
|
265
|
+
month: "2-digit",
|
|
266
|
+
day: "2-digit",
|
|
267
|
+
});
|
|
268
|
+
/** YYYY-MM-DD in Sydney. */
|
|
269
|
+
export function sydneyDate(date) {
|
|
270
|
+
return SYDNEY_DATE.format(date);
|
|
271
|
+
}
|
|
272
|
+
function recordLine(record) {
|
|
273
|
+
return `${sydneyDate(new Date(record.recordedAt))}: ${record.evidence}${record.note ? ` (${record.note})` : ""}`;
|
|
274
|
+
}
|
|
275
|
+
function plural(count, noun) {
|
|
276
|
+
return `${count} ${noun}${count === 1 ? "" : "s"}`;
|
|
277
|
+
}
|
|
278
|
+
/** "a", "a and b", "a, b and c". */
|
|
279
|
+
function joinParts(parts) {
|
|
280
|
+
const kept = parts.filter(Boolean);
|
|
281
|
+
return kept.length <= 1 ? (kept[0] ?? "") : `${kept.slice(0, -1).join(", ")} and ${kept.at(-1)}`;
|
|
282
|
+
}
|
|
283
|
+
const STATUS_LABELS = {
|
|
284
|
+
pass: "pass",
|
|
285
|
+
fail: "fail",
|
|
286
|
+
"not-run": "not run",
|
|
287
|
+
excepted: "excepted",
|
|
288
|
+
"not-applicable": "n/a",
|
|
289
|
+
};
|
|
290
|
+
/** Plain English for Romie and agents: the verdict first, then the tables. */
|
|
291
|
+
export function renderSiteSystemReadyMarkdown(report) {
|
|
292
|
+
const blocking = report.checks.filter((check) => check.blocks);
|
|
293
|
+
const reportsOnly = report.checks.filter((check) => !check.blocks);
|
|
294
|
+
const lines = [
|
|
295
|
+
`# Ready report${report.projectId ? `: ${report.projectId}` : ""}`,
|
|
296
|
+
"",
|
|
297
|
+
`**${report.verdict.summary}.**`,
|
|
298
|
+
"",
|
|
299
|
+
`Tier: ${report.tier ?? "none declared"} · Release: ${report.release ?? "unknown"} · New build or redesign: ${report.newBuild ? "yes" : "no"} · ${sydneyDate(new Date(report.generatedAt))}`,
|
|
300
|
+
"",
|
|
301
|
+
];
|
|
302
|
+
const { failed, notRun, blockers, stepsToRecord } = report.verdict;
|
|
303
|
+
const todo = [
|
|
304
|
+
// "The home page …", but "brandlab.config.ts …" keeps its file name as written.
|
|
305
|
+
...blockers.map((blocker) => `- ${/^[a-z]+\s/.test(blocker) ? blocker.charAt(0).toUpperCase() + blocker.slice(1) : blocker}.`),
|
|
306
|
+
failed.length > 0 ? `- Failed: ${failed.join(", ")}.` : "",
|
|
307
|
+
notRun.length > 0 ? `- Did not run: ${notRun.join(", ")}.` : "",
|
|
308
|
+
report.verdict.ready && stepsToRecord.length > 0 ? `- To record before launch: ${stepsToRecord.join(", ")}.` : "",
|
|
309
|
+
].filter(Boolean);
|
|
310
|
+
if ((report.tier || blockers.length > 0) && todo.length > 0)
|
|
311
|
+
lines.push(...todo, "");
|
|
312
|
+
lines.push("## Blocking checks", "");
|
|
313
|
+
if (blocking.length === 0) {
|
|
314
|
+
lines.push(report.tier ? "No check blocks on this release." : "Nothing blocks until brandlab.config.ts declares siteSystem: { tier }.");
|
|
315
|
+
}
|
|
316
|
+
else {
|
|
317
|
+
lines.push("| Check | Status | What it checks | Result |", "| --- | --- | --- | --- |");
|
|
318
|
+
for (const check of blocking) {
|
|
319
|
+
lines.push(row([check.id, statusLabel(check), check.summary, resultCell(check)]));
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
lines.push("", "## Reported only", "", "| Check | Status | Reports only because | Result |", "| --- | --- | --- | --- |");
|
|
323
|
+
for (const check of reportsOnly) {
|
|
324
|
+
lines.push(row([check.id, statusLabel(check), check.reportsOnly ?? "", check.status === "not-run" ? "" : resultCell(check)]));
|
|
325
|
+
}
|
|
326
|
+
for (const [file, title] of [["launch", "Launch-day steps"], ["human", "Human steps"]]) {
|
|
327
|
+
lines.push("", `## ${title}`, "", "| Step | Recorded | Evidence | What it covers |", "| --- | --- | --- | --- |");
|
|
328
|
+
for (const step of report.steps.filter((candidate) => candidate.file === file)) {
|
|
329
|
+
const evidence = step.recorded ? `${step.evidence}${step.note ? ` (${step.note})` : ""}` : "";
|
|
330
|
+
lines.push(row([step.id, step.recordedAt ? sydneyDate(new Date(step.recordedAt)) : "not yet", evidence, step.summary]));
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
if (report.exceptions.length > 0) {
|
|
334
|
+
lines.push("", "## Exceptions", "", "| Check | State | Expires | Rationale | Review trigger |", "| --- | --- | --- | --- | --- |");
|
|
335
|
+
for (const exception of report.exceptions) {
|
|
336
|
+
const state = exception.state === "not-needed" ? `not needed (${exception.reason})` : exception.state;
|
|
337
|
+
lines.push(row([exception.ruleId, state, exception.expiresAt ?? "no date", exception.rationale, exception.reviewTrigger]));
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
const inputs = report.inputs;
|
|
341
|
+
const pageList = inputs.pages.length > 12
|
|
342
|
+
? `${inputs.pages.slice(0, 12).join(", ")} and ${inputs.pages.length - 12} more`
|
|
343
|
+
: inputs.pages.join(", ");
|
|
344
|
+
lines.push("", "## Inputs", "", `- Journeys: ${inputs.journeyReport ? `${inputs.journeyReport} (${plural(inputs.journeyTests, "test run")})` : "no Playwright report"}.`, `- Pages: ${inputs.pages.length === 0 ? "none" : `${inputs.pages.length} ${inputs.pageSource === "base-url" ? "fetched from the running site" : "read from the build"} (${pageList})`}.`, `- Rule modules: ${inputs.runners}.`, ...(inputs.findingFiles.length > 0 ? [`- Supplied findings: ${inputs.findingFiles.join(", ")}.`] : []));
|
|
345
|
+
if (report.problems.length > 0) {
|
|
346
|
+
lines.push("", "## Problems", "", ...report.problems.map((problem) => `- ${inline(problem)}`));
|
|
347
|
+
}
|
|
348
|
+
lines.push("", "Record a step with `npx brandlab-ready record <id> --evidence <where the proof is>`.", "");
|
|
349
|
+
return lines.join("\n");
|
|
350
|
+
}
|
|
351
|
+
function statusLabel(check) {
|
|
352
|
+
return check.status === "pass" && check.warnings.length > 0 ? "pass (warning)" : STATUS_LABELS[check.status];
|
|
353
|
+
}
|
|
354
|
+
/** The first message (the first warning for a pass with warnings) and how many more there are. */
|
|
355
|
+
function resultCell(check) {
|
|
356
|
+
const [first = "", ...rest] = check.status === "pass" && check.warnings.length > 0 ? check.warnings : check.messages;
|
|
357
|
+
return rest.length > 0 ? `${first} (and ${rest.length} more)` : first;
|
|
358
|
+
}
|
|
359
|
+
function row(cells) {
|
|
360
|
+
return `| ${cells.map((cell) => inline(cell).replace(/\|/g, "\\|")).join(" | ")} |`;
|
|
361
|
+
}
|
|
362
|
+
/** One line of text that GitHub shows as written: no raw HTML, so "<a href>" stays visible. */
|
|
363
|
+
function inline(text) {
|
|
364
|
+
return text.replace(/\s*\r?\n\s*/g, " ").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
365
|
+
}
|
|
366
|
+
//# sourceMappingURL=report.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report.js","sourceRoot":"","sources":["../../src/site-system/report.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The ready report: every Site System check with its tier, whether it blocks for this site, its status and\n * messages; the existing brandlab-verify search, contact and analytics families (shell tier); the launch-day and\n * human steps; and the exceptions that apply. A blocking check that failed or did not run fails the gate.\n */\nimport type { BrandlabConformanceResult } from \"../project.js\";\nimport {\n SITE_SYSTEM_CHECKS,\n SITE_SYSTEM_TIERS,\n siteSystemCheckBlocks,\n type SiteSystemCheck,\n type SiteSystemCheckKind,\n type SiteSystemCheckScope,\n type SiteSystemFinding,\n type SiteSystemTier,\n} from \"./checks.js\";\nimport type { SiteSystemException, SiteSystemSettings } from \"./config.js\";\nimport { latestSiteSystemRecords, SITE_SYSTEM_RECORD_PATHS, type SiteSystemRecord } from \"./records.js\";\nimport {\n SITE_SYSTEM_HUMAN_STEPS,\n SITE_SYSTEM_LAUNCH_STEPS,\n SITE_SYSTEM_RECORDED_CHECK_IDS,\n type SiteSystemRecordFile,\n type SiteSystemStep,\n} from \"./steps.js\";\n\nexport const SITE_SYSTEM_READY_SCHEMA = \"brandlab.ready.v1\";\n\nexport const SITE_SYSTEM_READY_REPORT_PATHS = {\n markdown: \"docs/ready/ready-report.md\",\n json: \"docs/ready/ready-report.json\",\n} as const;\n\nexport type SiteSystemReadyStatus = \"pass\" | \"fail\" | \"not-run\" | \"excepted\" | \"not-applicable\";\n\nexport interface SiteSystemReadyCheck {\n readonly id: string;\n readonly summary: string;\n readonly tier: SiteSystemCheckScope;\n readonly from: string;\n /** A registry check kind, or `verify` for an existing brandlab-verify family. */\n readonly kind: SiteSystemCheckKind | \"verify\";\n readonly blocks: boolean;\n /** Why the check reports without blocking. */\n readonly reportsOnly?: string;\n readonly status: SiteSystemReadyStatus;\n readonly messages: readonly string[];\n /** Warnings never block; a passing check with warnings shows as \"pass (warning)\". */\n readonly warnings: readonly string[];\n}\n\nexport interface SiteSystemReadyStep {\n readonly id: string;\n readonly summary: string;\n readonly file: SiteSystemRecordFile;\n readonly recorded: boolean;\n readonly recordedAt?: string;\n readonly evidence?: string;\n readonly note?: string;\n}\n\nexport interface SiteSystemReadyException extends SiteSystemException {\n readonly expired: boolean;\n /** True when it turned a failing or unrun blocking check into `excepted`. */\n readonly applied: boolean;\n readonly state: \"applied\" | \"expired\" | \"not-needed\";\n /** Why an exception is not needed: the check passes, does not apply or does not block. */\n readonly reason?: string;\n}\n\nexport interface SiteSystemReadyVerdict {\n /** The declared tier's blocking checks all pass or are excepted. */\n readonly ready: boolean;\n /** Ready, and every launch-day and human step recorded. */\n readonly launchReady: boolean;\n readonly exitCode: 0 | 1;\n readonly summary: string;\n readonly failed: readonly string[];\n readonly notRun: readonly string[];\n readonly excepted: readonly string[];\n /** Reasons the run is not ready besides the checks (an unreadable config, a missing home or contact page). */\n readonly blockers: readonly string[];\n readonly stepsToRecord: readonly string[];\n}\n\nexport interface SiteSystemReadyInputs {\n /** The Playwright JSON report read, relative to the project, or null when there was none. */\n readonly journeyReport: string | null;\n readonly journeyTests: number;\n readonly pageSource: \"base-url\" | \"build\" | \"none\";\n readonly pages: readonly string[];\n readonly runners: number;\n readonly findingFiles: readonly string[];\n}\n\nexport interface SiteSystemReadyReport {\n readonly schemaVersion: typeof SITE_SYSTEM_READY_SCHEMA;\n readonly generatedAt: string;\n readonly projectId: string | null;\n readonly release: string | null;\n readonly tier: SiteSystemTier | null;\n readonly newBuild: boolean;\n /** Whether the run required every step to be recorded (`--launch`). */\n readonly launch: boolean;\n readonly verdict: SiteSystemReadyVerdict;\n readonly checks: readonly SiteSystemReadyCheck[];\n readonly steps: readonly SiteSystemReadyStep[];\n readonly exceptions: readonly SiteSystemReadyException[];\n readonly inputs: SiteSystemReadyInputs;\n readonly problems: readonly string[];\n}\n\n/** The brandlab-verify result the shell tier carries: its search, contact and analytics families. */\nexport type SiteSystemVerifyResult = Pick<BrandlabConformanceResult, \"verification\" | \"operationalFindings\">;\n\nexport interface BuildSiteSystemReadyReportInput {\n readonly projectId: string | null;\n readonly siteSystem: SiteSystemSettings | null;\n readonly release: string | null;\n /** Journey, rule-module and supplied findings. */\n readonly findings: readonly SiteSystemFinding[];\n /** Null when brandlab-verify could not run: its families then did not run. */\n readonly verify: SiteSystemVerifyResult | null;\n readonly launchRecords: readonly SiteSystemRecord[];\n readonly humanRecords: readonly SiteSystemRecord[];\n readonly exceptions: readonly SiteSystemException[];\n readonly inputs: SiteSystemReadyInputs;\n readonly problems: readonly string[];\n readonly now: Date;\n /** Require every launch-day and human step (`brandlab-ready --launch`). */\n readonly launch?: boolean;\n /** Reasons the run is not ready whatever the checks say, as short clauses (\"the home page could not be read\"). */\n readonly blockers?: readonly string[];\n}\n\nconst VERIFY_FAMILIES = [\"search-output\", \"contact-delivery\", \"analytics\"] as const;\nconst VERIFY_SCOPE = { tier: \"shell\", from: \"3.8.0\" } as const;\n\nexport function buildSiteSystemReadyReport(input: BuildSiteSystemReadyReportInput): SiteSystemReadyReport {\n const problems = [...input.problems];\n const settings = input.siteSystem;\n const site = settings && input.release\n ? { tier: settings.tier, release: input.release, newBuild: settings.newBuild }\n : null;\n if (settings && !input.release) problems.push(\"The installed Brandlab release is unknown, so no check could block.\");\n\n const launchLatest = latestSiteSystemRecords(input.launchRecords);\n const humanLatest = latestSiteSystemRecords(input.humanRecords);\n const launchIds = new Set<string>([...SITE_SYSTEM_LAUNCH_STEPS.map((step) => step.id), ...SITE_SYSTEM_RECORDED_CHECK_IDS]);\n const humanIds = new Set<string>(SITE_SYSTEM_HUMAN_STEPS.map((step) => step.id));\n for (const id of launchLatest.keys()) {\n if (!launchIds.has(id)) problems.push(`${SITE_SYSTEM_RECORD_PATHS.launch} records ${id}, which is not a launch-day step or recorded check, so it was ignored.`);\n }\n for (const id of humanLatest.keys()) {\n if (!humanIds.has(id)) problems.push(`${SITE_SYSTEM_RECORD_PATHS.human} records ${id}, which is not a human step, so it was ignored.`);\n }\n\n const findings: SiteSystemFinding[] = [...input.findings];\n for (const id of SITE_SYSTEM_RECORDED_CHECK_IDS) {\n const record = launchLatest.get(id);\n if (record) findings.push({ checkId: id as SiteSystemFinding[\"checkId\"], status: \"pass\", message: `Recorded ${recordLine(record)}` });\n }\n\n const exceptionsByRule = new Map<string, SiteSystemException>();\n for (const exception of input.exceptions) {\n if (!exceptionsByRule.has(exception.ruleId)) exceptionsByRule.set(exception.ruleId, exception);\n }\n const knownIds = new Set<string>([...SITE_SYSTEM_CHECKS.map((check) => check.id), ...VERIFY_FAMILIES.map((id) => `verify:${id}`)]);\n for (const ruleId of exceptionsByRule.keys()) {\n if (!knownIds.has(ruleId)) problems.push(`The exception for ${ruleId} names no ready-gate check, so the gate ignored it.`);\n }\n const usedExceptions: SiteSystemReadyException[] = [];\n const applyException = (id: string, blocks: boolean, status: SiteSystemReadyStatus, messages: string[]) => {\n const exception = exceptionsByRule.get(id);\n if (!exception) return { status, messages };\n const expired = isExceptionExpired(exception.expiresAt, input.now);\n const unresolved = status === \"fail\" || status === \"not-run\";\n const applied = blocks && unresolved && !expired;\n const reason = !blocks ? \"the check does not block\" : status === \"not-applicable\" ? \"the check does not apply\" : \"the check passes\";\n usedExceptions.push({\n ...exception,\n expired,\n applied,\n state: expired ? \"expired\" : applied ? \"applied\" : \"not-needed\",\n ...(expired || applied ? {} : { reason }),\n });\n if (!unresolved) return { status, messages };\n if (expired) return { status, messages: [`The exception expired on ${exception.expiresAt}, so it no longer counts.`, ...messages] };\n if (!applied) return { status, messages };\n const until = exception.expiresAt ? ` until ${exception.expiresAt}` : \"\";\n return { status: \"excepted\" as const, messages: [`Excepted by Romie${until}: ${exception.rationale}`, ...messages] };\n };\n\n const checks: SiteSystemReadyCheck[] = SITE_SYSTEM_CHECKS.map((check: SiteSystemCheck) => {\n const blocks = site ? siteSystemCheckBlocks(check, site) : false;\n const merged = mergeFindings(findings.filter((finding) => finding.checkId === check.id), notRunMessage(check));\n const { status, messages } = applyException(check.id, blocks, merged.status, merged.messages);\n return {\n id: check.id,\n summary: check.summary,\n tier: check.tier,\n from: check.from,\n kind: check.kind,\n blocks,\n ...(blocks ? {} : { reportsOnly: reportsOnlyReason(check, settings) }),\n status,\n messages,\n warnings: merged.warnings,\n };\n });\n\n for (const familyId of VERIFY_FAMILIES) {\n const id = `verify:${familyId}`;\n const family = input.verify?.verification.families.find((candidate) => candidate.id === familyId);\n const blocks = site ? siteSystemCheckBlocks({ id, ...VERIFY_SCOPE }, site) : false;\n const merged = verifyStatus(family, input.verify?.operationalFindings ?? []);\n const { status, messages } = applyException(id, blocks, merged.status, merged.messages);\n checks.push({\n id,\n summary: family ? `${family.title} (brandlab-verify): ${family.purpose}` : `The brandlab-verify ${familyId} checks.`,\n tier: VERIFY_SCOPE.tier,\n from: VERIFY_SCOPE.from,\n kind: \"verify\",\n blocks,\n ...(blocks ? {} : { reportsOnly: reportsOnlyReason(VERIFY_SCOPE, settings) }),\n status,\n messages,\n warnings: merged.warnings,\n });\n }\n\n const steps: SiteSystemReadyStep[] = [\n ...SITE_SYSTEM_LAUNCH_STEPS.map((step) => readyStep(step, \"launch\", launchLatest.get(step.id))),\n ...SITE_SYSTEM_HUMAN_STEPS.map((step) => readyStep(step, \"human\", humanLatest.get(step.id))),\n ];\n\n const verdict = readyVerdict({\n settings,\n release: input.release,\n checks,\n steps,\n launch: input.launch === true,\n blockers: input.blockers ?? [],\n });\n return {\n schemaVersion: SITE_SYSTEM_READY_SCHEMA,\n generatedAt: input.now.toISOString(),\n projectId: input.projectId,\n release: input.release,\n tier: settings?.tier ?? null,\n newBuild: settings?.newBuild ?? false,\n launch: input.launch === true,\n verdict,\n checks,\n steps,\n exceptions: usedExceptions,\n inputs: input.inputs,\n problems,\n };\n}\n\n/**\n * Any fail fails the check; otherwise any pass passes it; only not-applicable results make it not applicable. A\n * warning is a pass that carries its message as a warning, whatever status it names.\n */\nfunction mergeFindings(\n findings: readonly SiteSystemFinding[],\n notRun: string,\n): { status: SiteSystemReadyStatus; messages: string[]; warnings: string[] } {\n const warnings = [...new Set(findings.filter((finding) => finding.severity === \"warning\").map((finding) => finding.message))];\n const results = findings.filter((finding) => finding.severity !== \"warning\");\n if (warnings.length > 0 && !results.some((finding) => finding.status !== \"not-applicable\")) {\n return { status: \"pass\", messages: [], warnings };\n }\n for (const status of [\"fail\", \"pass\", \"not-applicable\"] as const) {\n const scoped = results.filter((finding) => finding.status === status);\n if (scoped.length > 0) return { status, messages: [...new Set(scoped.map((finding) => finding.message))], warnings };\n }\n return { status: \"not-run\", messages: [notRun], warnings };\n}\n\nfunction verifyStatus(\n family: SiteSystemVerifyResult[\"verification\"][\"families\"][number] | undefined,\n findings: SiteSystemVerifyResult[\"operationalFindings\"],\n): { status: SiteSystemReadyStatus; messages: string[]; warnings: string[] } {\n if (!family) return { status: \"not-run\", messages: [\"brandlab-verify did not run, so these checks have no result.\"], warnings: [] };\n const scoped = findings\n .filter((finding) => finding.familyId === family.id)\n .map((finding) => {\n const location = finding.path ? ` (${finding.path}${finding.line ? `:${finding.line}` : \"\"})` : \"\";\n return { severity: finding.severity, message: `${finding.ruleId}${location}: ${finding.actual}` };\n });\n const warnings = scoped.filter((finding) => finding.severity === \"warning\").map((finding) => finding.message);\n if (family.status === \"blocked\") {\n return { status: \"fail\", messages: scoped.filter((finding) => finding.severity === \"blocker\").map((finding) => finding.message), warnings };\n }\n if (family.status === \"not_run\") return { status: \"not-applicable\", messages: [\"No brandlab-verify rule in this family applies to this project.\"], warnings };\n return { status: \"pass\", messages: [`brandlab-verify found nothing to fix in ${family.title.toLowerCase()}.`], warnings };\n}\n\nfunction notRunMessage(check: SiteSystemCheck): string {\n switch (check.kind) {\n case \"journey\":\n return \"No journey result: run the site-system journeys (npm run test:e2e) before brandlab-ready.\";\n case \"rendered\":\n return \"No result: no rule module reported this check for the sampled pages.\";\n case \"static\":\n return \"No result: no rule module reported this check.\";\n case \"record\":\n return `Not recorded: run brandlab-ready record ${check.id} --evidence <where the proof is>.`;\n }\n}\n\nfunction reportsOnlyReason(check: Pick<SiteSystemCheck, \"tier\" | \"from\">, settings: SiteSystemSettings | null): string {\n if (!settings) return \"no tier declared\";\n if (check.tier === \"new-build\") return settings.newBuild ? \"blocks new builds from 3.9.0\" : \"new builds and redesigns only\";\n return SITE_SYSTEM_TIERS.indexOf(check.tier) <= SITE_SYSTEM_TIERS.indexOf(settings.tier)\n ? `blocks from ${check.from}`\n : `${check.tier} tier`;\n}\n\nfunction readyStep(step: SiteSystemStep, file: SiteSystemRecordFile, record: SiteSystemRecord | undefined): SiteSystemReadyStep {\n return {\n id: step.id,\n summary: step.summary,\n file,\n recorded: Boolean(record),\n ...(record\n ? { recordedAt: record.recordedAt, evidence: record.evidence, ...(record.note ? { note: record.note } : {}) }\n : {}),\n };\n}\n\nfunction readyVerdict(input: {\n settings: SiteSystemSettings | null;\n release: string | null;\n checks: readonly SiteSystemReadyCheck[];\n steps: readonly SiteSystemReadyStep[];\n launch: boolean;\n blockers: readonly string[];\n}): SiteSystemReadyVerdict {\n const blocking = input.checks.filter((check) => check.blocks);\n const failed = blocking.filter((check) => check.status === \"fail\").map((check) => check.id);\n const notRun = blocking.filter((check) => check.status === \"not-run\").map((check) => check.id);\n const excepted = blocking.filter((check) => check.status === \"excepted\").map((check) => check.id);\n const stepsToRecord = input.steps.filter((step) => !step.recorded).map((step) => step.id);\n const adopted = input.settings !== null;\n const ready = adopted && input.release !== null && blocking.length > 0 && failed.length === 0 &&\n notRun.length === 0 && input.blockers.length === 0;\n const launchReady = ready && stepsToRecord.length === 0;\n const launchSteps = input.steps.filter((step) => !step.recorded && step.file === \"launch\").length;\n const humanSteps = input.steps.filter((step) => !step.recorded && step.file === \"human\").length;\n const stepCount = joinParts([\n launchSteps > 0 ? `${launchSteps} launch-day` : \"\",\n humanSteps > 0 ? `${humanSteps} human` : \"\",\n ]);\n const stepNoun = launchSteps + humanSteps === 1 ? \"step\" : \"steps\";\n const reasons = [\n failed.length > 0 ? `${plural(failed.length, \"blocking check\")} failed` : \"\",\n notRun.length > 0 ? (failed.length > 0 ? `${notRun.length} did not run` : `${plural(notRun.length, \"blocking check\")} did not run`) : \"\",\n ...input.blockers,\n ].filter(Boolean);\n\n let summary: string;\n let exitCode: 0 | 1 = 1;\n if (input.blockers.length > 0 || (adopted && reasons.length > 0)) {\n summary = `Not ready: ${joinParts(reasons)}`;\n } else if (!adopted) {\n summary = \"Not adopted: brandlab.config.ts declares no siteSystem tier, so no check blocks yet\";\n exitCode = 0;\n } else if (input.release === null) {\n summary = \"Not ready: the installed Brandlab release is unknown\";\n } else if (input.launch && stepsToRecord.length > 0) {\n summary = `Not ready: ${stepCount} ${stepNoun} not recorded`;\n } else if (blocking.length === 0) {\n summary = `No check blocks on release ${input.release}`;\n exitCode = 0;\n } else {\n summary = launchReady\n ? `Ready at ${input.settings!.tier} tier`\n : `Checks pass at ${input.settings!.tier} tier; before launch, record ${stepCount} ${stepNoun}`;\n if (excepted.length > 0) summary += ` (${plural(excepted.length, \"check\")} excepted)`;\n exitCode = 0;\n }\n return { ready, launchReady, exitCode, summary, failed, notRun, excepted, blockers: input.blockers, stepsToRecord };\n}\n\n/** Date-only expiries last to the end of that day in Sydney; timestamps expire at that instant. */\nexport function isExceptionExpired(expiresAt: string | undefined, now: Date): boolean {\n if (!expiresAt) return false;\n if (/^\\d{4}-\\d{2}-\\d{2}$/.test(expiresAt)) return sydneyDate(now) > expiresAt;\n const time = Date.parse(expiresAt);\n return Number.isNaN(time) || time <= now.getTime();\n}\n\nconst SYDNEY_DATE = new Intl.DateTimeFormat(\"en-CA\", {\n timeZone: \"Australia/Sydney\",\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\",\n});\n\n/** YYYY-MM-DD in Sydney. */\nexport function sydneyDate(date: Date): string {\n return SYDNEY_DATE.format(date);\n}\n\nfunction recordLine(record: SiteSystemRecord): string {\n return `${sydneyDate(new Date(record.recordedAt))}: ${record.evidence}${record.note ? ` (${record.note})` : \"\"}`;\n}\n\nfunction plural(count: number, noun: string): string {\n return `${count} ${noun}${count === 1 ? \"\" : \"s\"}`;\n}\n\n/** \"a\", \"a and b\", \"a, b and c\". */\nfunction joinParts(parts: readonly string[]): string {\n const kept = parts.filter(Boolean);\n return kept.length <= 1 ? (kept[0] ?? \"\") : `${kept.slice(0, -1).join(\", \")} and ${kept.at(-1)}`;\n}\n\nconst STATUS_LABELS: Record<SiteSystemReadyStatus, string> = {\n pass: \"pass\",\n fail: \"fail\",\n \"not-run\": \"not run\",\n excepted: \"excepted\",\n \"not-applicable\": \"n/a\",\n};\n\n/** Plain English for Romie and agents: the verdict first, then the tables. */\nexport function renderSiteSystemReadyMarkdown(report: SiteSystemReadyReport): string {\n const blocking = report.checks.filter((check) => check.blocks);\n const reportsOnly = report.checks.filter((check) => !check.blocks);\n const lines = [\n `# Ready report${report.projectId ? `: ${report.projectId}` : \"\"}`,\n \"\",\n `**${report.verdict.summary}.**`,\n \"\",\n `Tier: ${report.tier ?? \"none declared\"} · Release: ${report.release ?? \"unknown\"} · New build or redesign: ${report.newBuild ? \"yes\" : \"no\"} · ${sydneyDate(new Date(report.generatedAt))}`,\n \"\",\n ];\n const { failed, notRun, blockers, stepsToRecord } = report.verdict;\n const todo = [\n // \"The home page …\", but \"brandlab.config.ts …\" keeps its file name as written.\n ...blockers.map((blocker) => `- ${/^[a-z]+\\s/.test(blocker) ? blocker.charAt(0).toUpperCase() + blocker.slice(1) : blocker}.`),\n failed.length > 0 ? `- Failed: ${failed.join(\", \")}.` : \"\",\n notRun.length > 0 ? `- Did not run: ${notRun.join(\", \")}.` : \"\",\n report.verdict.ready && stepsToRecord.length > 0 ? `- To record before launch: ${stepsToRecord.join(\", \")}.` : \"\",\n ].filter(Boolean);\n if ((report.tier || blockers.length > 0) && todo.length > 0) lines.push(...todo, \"\");\n lines.push(\"## Blocking checks\", \"\");\n if (blocking.length === 0) {\n lines.push(report.tier ? \"No check blocks on this release.\" : \"Nothing blocks until brandlab.config.ts declares siteSystem: { tier }.\");\n } else {\n lines.push(\"| Check | Status | What it checks | Result |\", \"| --- | --- | --- | --- |\");\n for (const check of blocking) {\n lines.push(row([check.id, statusLabel(check), check.summary, resultCell(check)]));\n }\n }\n lines.push(\"\", \"## Reported only\", \"\", \"| Check | Status | Reports only because | Result |\", \"| --- | --- | --- | --- |\");\n for (const check of reportsOnly) {\n lines.push(row([check.id, statusLabel(check), check.reportsOnly ?? \"\", check.status === \"not-run\" ? \"\" : resultCell(check)]));\n }\n\n for (const [file, title] of [[\"launch\", \"Launch-day steps\"], [\"human\", \"Human steps\"]] as const) {\n lines.push(\"\", `## ${title}`, \"\", \"| Step | Recorded | Evidence | What it covers |\", \"| --- | --- | --- | --- |\");\n for (const step of report.steps.filter((candidate) => candidate.file === file)) {\n const evidence = step.recorded ? `${step.evidence}${step.note ? ` (${step.note})` : \"\"}` : \"\";\n lines.push(row([step.id, step.recordedAt ? sydneyDate(new Date(step.recordedAt)) : \"not yet\", evidence, step.summary]));\n }\n }\n\n if (report.exceptions.length > 0) {\n lines.push(\"\", \"## Exceptions\", \"\", \"| Check | State | Expires | Rationale | Review trigger |\", \"| --- | --- | --- | --- | --- |\");\n for (const exception of report.exceptions) {\n const state = exception.state === \"not-needed\" ? `not needed (${exception.reason})` : exception.state;\n lines.push(row([exception.ruleId, state, exception.expiresAt ?? \"no date\", exception.rationale, exception.reviewTrigger]));\n }\n }\n\n const inputs = report.inputs;\n const pageList = inputs.pages.length > 12\n ? `${inputs.pages.slice(0, 12).join(\", \")} and ${inputs.pages.length - 12} more`\n : inputs.pages.join(\", \");\n lines.push(\n \"\",\n \"## Inputs\",\n \"\",\n `- Journeys: ${inputs.journeyReport ? `${inputs.journeyReport} (${plural(inputs.journeyTests, \"test run\")})` : \"no Playwright report\"}.`,\n `- Pages: ${inputs.pages.length === 0 ? \"none\" : `${inputs.pages.length} ${inputs.pageSource === \"base-url\" ? \"fetched from the running site\" : \"read from the build\"} (${pageList})`}.`,\n `- Rule modules: ${inputs.runners}.`,\n ...(inputs.findingFiles.length > 0 ? [`- Supplied findings: ${inputs.findingFiles.join(\", \")}.`] : []),\n );\n if (report.problems.length > 0) {\n lines.push(\"\", \"## Problems\", \"\", ...report.problems.map((problem) => `- ${inline(problem)}`));\n }\n lines.push(\"\", \"Record a step with `npx brandlab-ready record <id> --evidence <where the proof is>`.\", \"\");\n return lines.join(\"\\n\");\n}\n\nfunction statusLabel(check: SiteSystemReadyCheck): string {\n return check.status === \"pass\" && check.warnings.length > 0 ? \"pass (warning)\" : STATUS_LABELS[check.status];\n}\n\n/** The first message (the first warning for a pass with warnings) and how many more there are. */\nfunction resultCell(check: SiteSystemReadyCheck): string {\n const [first = \"\", ...rest] = check.status === \"pass\" && check.warnings.length > 0 ? check.warnings : check.messages;\n return rest.length > 0 ? `${first} (and ${rest.length} more)` : first;\n}\n\nfunction row(cells: readonly string[]): string {\n return `| ${cells.map((cell) => inline(cell).replace(/\\|/g, \"\\\\|\")).join(\" | \")} |`;\n}\n\n/** One line of text that GitHub shows as written: no raw HTML, so \"<a href>\" stays visible. */\nfunction inline(text: string): string {\n return text.replace(/\\s*\\r?\\n\\s*/g, \" \").replace(/&/g, \"&\").replace(/</g, \"<\").replace(/>/g, \">\");\n}\n"]}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The rule modules `brandlab-ready` runs. Each module under ./rules/ exports a SiteSystemRenderedRunner: plug it in
|
|
3
|
+
* here with one import line and one list entry. Rule modules import from ./runners.js and ./checks.js, never from
|
|
4
|
+
* this file or ./index.js, so the list adds no import cycle.
|
|
5
|
+
*/
|
|
6
|
+
import type { SiteSystemRenderedRunner } from "./runners.js";
|
|
7
|
+
export declare const SITE_SYSTEM_RENDERED_RUNNERS: readonly SiteSystemRenderedRunner[];
|
|
8
|
+
//# sourceMappingURL=rule-modules.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rule-modules.d.ts","sourceRoot":"","sources":["../../src/site-system/rule-modules.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAC;AAa7D,eAAO,MAAM,4BAA4B,EAAE,SAAS,wBAAwB,EAA+C,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { runDesignRules } from "./rules/design/index.js";
|
|
2
|
+
import { runEnquiryRenderedRules } from "./rules/enquiry/index.js";
|
|
3
|
+
import { runShellRenderedRules } from "./rules/shell/index.js";
|
|
4
|
+
// Named wrappers, so a runner that throws is reported by name.
|
|
5
|
+
const shellRules = function shellRules(input) {
|
|
6
|
+
return runShellRenderedRules(input);
|
|
7
|
+
};
|
|
8
|
+
const enquiryRules = function enquiryRules(input) {
|
|
9
|
+
return runEnquiryRenderedRules(input);
|
|
10
|
+
};
|
|
11
|
+
export const SITE_SYSTEM_RENDERED_RUNNERS = [shellRules, enquiryRules, runDesignRules];
|
|
12
|
+
//# sourceMappingURL=rule-modules.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rule-modules.js","sourceRoot":"","sources":["../../src/site-system/rule-modules.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAE/D,+DAA+D;AAC/D,MAAM,UAAU,GAA6B,SAAS,UAAU,CAAC,KAAK;IACpE,OAAO,qBAAqB,CAAC,KAAK,CAAC,CAAC;AACtC,CAAC,CAAC;AACF,MAAM,YAAY,GAA6B,SAAS,YAAY,CAAC,KAAK;IACxE,OAAO,uBAAuB,CAAC,KAAK,CAAC,CAAC;AACxC,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,4BAA4B,GAAwC,CAAC,UAAU,EAAE,YAAY,EAAE,cAAc,CAAC,CAAC","sourcesContent":["/**\n * The rule modules `brandlab-ready` runs. Each module under ./rules/ exports a SiteSystemRenderedRunner: plug it in\n * here with one import line and one list entry. Rule modules import from ./runners.js and ./checks.js, never from\n * this file or ./index.js, so the list adds no import cycle.\n */\nimport type { SiteSystemRenderedRunner } from \"./runners.js\";\nimport { runDesignRules } from \"./rules/design/index.js\";\nimport { runEnquiryRenderedRules } from \"./rules/enquiry/index.js\";\nimport { runShellRenderedRules } from \"./rules/shell/index.js\";\n\n// Named wrappers, so a runner that throws is reported by name.\nconst shellRules: SiteSystemRenderedRunner = function shellRules(input) {\n return runShellRenderedRules(input);\n};\nconst enquiryRules: SiteSystemRenderedRunner = function enquiryRules(input) {\n return runEnquiryRenderedRules(input);\n};\n\nexport const SITE_SYSTEM_RENDERED_RUNNERS: readonly SiteSystemRenderedRunner[] = [shellRules, enquiryRules, runDesignRules];\n"]}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { SiteSystemFinding } from "../../checks.js";
|
|
2
|
+
import { type Literal } from "../enquiry/literal.js";
|
|
3
|
+
/** Where the site records its strategy. */
|
|
4
|
+
export declare const D4_STRATEGY_FILE = "src/data/strategy.ts";
|
|
5
|
+
/** The recorded levers (spec §7.7), as site-core's `SITE_LOOK_LEVERS` lists them (a test keeps the two lists equal). */
|
|
6
|
+
export declare const D4_LEVERS: readonly ["displayFont", "actionHue", "heroVariant", "headerVariant", "cardStyle", "photoTreatment", "homeSections"];
|
|
7
|
+
export type D4Lever = (typeof D4_LEVERS)[number];
|
|
8
|
+
/** An action hue this close to a competitor's, in degrees, counts as the same. */
|
|
9
|
+
export declare const D4_SAME_HUE_DEGREES = 20;
|
|
10
|
+
/** At least this many levers must differ from every competitor. */
|
|
11
|
+
export declare const D4_MIN_DIFFERING = 4;
|
|
12
|
+
export type RecordedLook = Partial<Record<D4Lever, Literal>>;
|
|
13
|
+
export interface RecordedCompetitor {
|
|
14
|
+
name: string;
|
|
15
|
+
domain: string;
|
|
16
|
+
look: RecordedLook;
|
|
17
|
+
}
|
|
18
|
+
/** Smallest angle between two hues, in degrees, on the colour wheel. */
|
|
19
|
+
export declare function hueDistance(left: number, right: number): number;
|
|
20
|
+
/** Whether one lever is the same in both looks. A lever recorded in the wrong shape is never the same. */
|
|
21
|
+
export declare function sameLever(lever: D4Lever, left: Literal, right: Literal): boolean;
|
|
22
|
+
export interface LookComparison {
|
|
23
|
+
differing: D4Lever[];
|
|
24
|
+
same: D4Lever[];
|
|
25
|
+
/** Levers that are not recorded on one side; they can be neither the same nor different. */
|
|
26
|
+
unrecorded: D4Lever[];
|
|
27
|
+
}
|
|
28
|
+
export declare function compareLooks(own: RecordedLook, rival: RecordedLook): LookComparison;
|
|
29
|
+
export interface RecordedStrategy {
|
|
30
|
+
look: RecordedLook;
|
|
31
|
+
competitors: RecordedCompetitor[];
|
|
32
|
+
}
|
|
33
|
+
/** Reads the strategy record without running the site. Returns a string when it cannot be read as plain data. */
|
|
34
|
+
export declare function readStrategy(projectRoot: string): RecordedStrategy | string | null;
|
|
35
|
+
export declare function checkD4(projectRoot: string): SiteSystemFinding[];
|
|
36
|
+
//# sourceMappingURL=d4-distinctness.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"d4-distinctness.d.ts","sourceRoot":"","sources":["../../../../src/site-system/rules/design/d4-distinctness.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAAmD,KAAK,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAEtG,2CAA2C;AAC3C,eAAO,MAAM,gBAAgB,yBAAyB,CAAC;AAEvD,wHAAwH;AACxH,eAAO,MAAM,SAAS,sHAQZ,CAAC;AACX,MAAM,MAAM,OAAO,GAAG,CAAC,OAAO,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC;AAEjD,kFAAkF;AAClF,eAAO,MAAM,mBAAmB,KAAK,CAAC;AACtC,mEAAmE;AACnE,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAMlC,MAAM,MAAM,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;AAE7D,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,YAAY,CAAC;CACpB;AAWD,wEAAwE;AACxE,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAG/D;AAmCD,0GAA0G;AAC1G,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CA4BhF;AAED,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,OAAO,EAAE,CAAC;IACrB,IAAI,EAAE,OAAO,EAAE,CAAC;IAChB,4FAA4F;IAC5F,UAAU,EAAE,OAAO,EAAE,CAAC;CACvB;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,YAAY,EAAE,KAAK,EAAE,YAAY,GAAG,cAAc,CAUnF;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,YAAY,CAAC;IACnB,WAAW,EAAE,kBAAkB,EAAE,CAAC;CACnC;AAYD,iHAAiH;AACjH,wBAAgB,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,gBAAgB,GAAG,MAAM,GAAG,IAAI,CAwBlF;AAMD,wBAAgB,OAAO,CAAC,WAAW,EAAE,MAAM,GAAG,iBAAiB,EAAE,CAwChE"}
|