@brandlab-digital/conformance 3.6.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,327 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, realpathSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { detectInstalledBrandlabRelease, evaluateBrandlabProject } from "./project.js";
|
|
6
|
+
import { readSiteSystemProjectConfig } from "./site-system/config.js";
|
|
7
|
+
import { readSiteSystemJourneyReport } from "./site-system/playwright-report.js";
|
|
8
|
+
import { appendSiteSystemRecord, readSiteSystemRecords } from "./site-system/records.js";
|
|
9
|
+
import { buildSiteSystemReadyReport, renderSiteSystemReadyMarkdown, SITE_SYSTEM_READY_REPORT_PATHS, } from "./site-system/report.js";
|
|
10
|
+
import { SITE_SYSTEM_RENDERED_RUNNERS } from "./site-system/rule-modules.js";
|
|
11
|
+
import { isSiteSystemFinding, runSiteSystemRenderedRunners, } from "./site-system/runners.js";
|
|
12
|
+
import { loadSiteSystemPages, planSiteSystemSamplePaths } from "./site-system/samples.js";
|
|
13
|
+
import { SITE_SYSTEM_RECORDED_CHECK_IDS } from "./site-system/steps.js";
|
|
14
|
+
const DEFAULT_PLAYWRIGHT_REPORT = "test-results/site-system.json";
|
|
15
|
+
/** The checks a --findings file may set: K10 (the lenient harness) and the other recorded checks. */
|
|
16
|
+
const SUPPLIABLE_IDS = new Set(SITE_SYSTEM_RECORDED_CHECK_IDS);
|
|
17
|
+
const DEFAULT_BUILD_DIR = ".next";
|
|
18
|
+
const HELP = `Brandlab ready gate
|
|
19
|
+
|
|
20
|
+
Writes docs/ready/ready-report.md and .json for the Site System tier declared in
|
|
21
|
+
brandlab.config.ts (siteSystem: { tier }), and records launch-day and human steps.
|
|
22
|
+
|
|
23
|
+
Usage:
|
|
24
|
+
brandlab-ready [--root <path>]
|
|
25
|
+
[--playwright-report <path>] journey results (default ${DEFAULT_PLAYWRIGHT_REPORT})
|
|
26
|
+
[--base-url <url>] fetch the sample pages from the running site
|
|
27
|
+
[--build-dir <path>] otherwise read their HTML from the build (default ${DEFAULT_BUILD_DIR})
|
|
28
|
+
[--findings <path>]... K10 or X1 results as SiteSystemFinding JSON (the lenient harness)
|
|
29
|
+
[--installed-release <version>]
|
|
30
|
+
[--launch] also require every launch-day and human step
|
|
31
|
+
brandlab-ready record <id> --evidence <where the proof is> [--note <text>] [--root <path>]
|
|
32
|
+
|
|
33
|
+
Exit status:
|
|
34
|
+
0 ready at the declared tier, or nothing blocks (no tier declared, or none of its checks released)
|
|
35
|
+
1 a blocking check failed or did not run, home or the contact page could not be read, a --base-url
|
|
36
|
+
sample did not answer with HTML, or brandlab.config.ts could not be read in full
|
|
37
|
+
(with --launch, also a step not recorded)
|
|
38
|
+
2 the gate could not run (bad arguments, an unreadable config or records file)
|
|
39
|
+
`;
|
|
40
|
+
export function parseBrandlabReadyArgs(argv) {
|
|
41
|
+
if (argv.includes("--help") || argv.includes("-h"))
|
|
42
|
+
return { command: "help" };
|
|
43
|
+
if (argv[0] === "record")
|
|
44
|
+
return parseRecordArgs(argv.slice(1));
|
|
45
|
+
const parsed = {
|
|
46
|
+
command: "report",
|
|
47
|
+
root: process.cwd(),
|
|
48
|
+
playwrightReport: DEFAULT_PLAYWRIGHT_REPORT,
|
|
49
|
+
buildDir: DEFAULT_BUILD_DIR,
|
|
50
|
+
findings: [],
|
|
51
|
+
launch: false,
|
|
52
|
+
};
|
|
53
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
54
|
+
const argument = argv[index];
|
|
55
|
+
if (argument === "--root")
|
|
56
|
+
parsed.root = resolve(requireValue(argv, ++index, argument));
|
|
57
|
+
else if (argument === "--playwright-report")
|
|
58
|
+
parsed.playwrightReport = requireValue(argv, ++index, argument);
|
|
59
|
+
else if (argument === "--build-dir")
|
|
60
|
+
parsed.buildDir = requireValue(argv, ++index, argument);
|
|
61
|
+
else if (argument === "--findings")
|
|
62
|
+
parsed.findings.push(requireValue(argv, ++index, argument));
|
|
63
|
+
else if (argument === "--installed-release")
|
|
64
|
+
parsed.installedRelease = requireValue(argv, ++index, argument);
|
|
65
|
+
else if (argument === "--launch")
|
|
66
|
+
parsed.launch = true;
|
|
67
|
+
else if (argument === "--base-url") {
|
|
68
|
+
const value = requireValue(argv, ++index, argument);
|
|
69
|
+
let url;
|
|
70
|
+
try {
|
|
71
|
+
url = new URL(value);
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
throw new Error(`--base-url ${value} is not a URL.`);
|
|
75
|
+
}
|
|
76
|
+
if (url.protocol !== "http:" && url.protocol !== "https:")
|
|
77
|
+
throw new Error("--base-url must be an http or https URL.");
|
|
78
|
+
parsed.baseUrl = value;
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
throw new Error(`Unknown argument ${argument}.`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return parsed;
|
|
85
|
+
}
|
|
86
|
+
function parseRecordArgs(argv) {
|
|
87
|
+
const parsed = { command: "record", root: process.cwd(), id: "" };
|
|
88
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
89
|
+
const argument = argv[index];
|
|
90
|
+
if (argument === "--evidence")
|
|
91
|
+
parsed.evidence = requireValue(argv, ++index, argument);
|
|
92
|
+
else if (argument === "--note")
|
|
93
|
+
parsed.note = requireValue(argv, ++index, argument);
|
|
94
|
+
else if (argument === "--root")
|
|
95
|
+
parsed.root = resolve(requireValue(argv, ++index, argument));
|
|
96
|
+
else if (!argument.startsWith("--") && !parsed.id)
|
|
97
|
+
parsed.id = argument;
|
|
98
|
+
else
|
|
99
|
+
throw new Error(`Unknown argument ${argument}.`);
|
|
100
|
+
}
|
|
101
|
+
if (!parsed.id)
|
|
102
|
+
throw new Error("record needs a step id, for example: brandlab-ready record X3 --evidence <PR link>.");
|
|
103
|
+
if (parsed.evidence === undefined)
|
|
104
|
+
throw new Error(`record ${parsed.id} needs --evidence <where the proof is>.`);
|
|
105
|
+
return parsed;
|
|
106
|
+
}
|
|
107
|
+
export async function runBrandlabReadyCli(argv, io = {
|
|
108
|
+
stdout: (value) => process.stdout.write(value),
|
|
109
|
+
stderr: (value) => process.stderr.write(value),
|
|
110
|
+
}, dependencies = {}) {
|
|
111
|
+
let options;
|
|
112
|
+
try {
|
|
113
|
+
options = parseBrandlabReadyArgs(argv);
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
io.stderr(`${errorMessage(error)}\n\n${HELP}`);
|
|
117
|
+
return 2;
|
|
118
|
+
}
|
|
119
|
+
if (options.command === "help") {
|
|
120
|
+
io.stdout(HELP);
|
|
121
|
+
return 0;
|
|
122
|
+
}
|
|
123
|
+
const now = dependencies.now ?? (() => new Date());
|
|
124
|
+
try {
|
|
125
|
+
if (options.command === "record") {
|
|
126
|
+
const { path, record } = appendSiteSystemRecord(options.root, {
|
|
127
|
+
id: options.id,
|
|
128
|
+
evidence: options.evidence ?? "",
|
|
129
|
+
...(options.note !== undefined ? { note: options.note } : {}),
|
|
130
|
+
now: now(),
|
|
131
|
+
});
|
|
132
|
+
io.stdout(`Recorded ${record.id} in ${path}.\n`);
|
|
133
|
+
return 0;
|
|
134
|
+
}
|
|
135
|
+
const report = await createReadyReport(options, dependencies, now());
|
|
136
|
+
const markdownPath = join(options.root, SITE_SYSTEM_READY_REPORT_PATHS.markdown);
|
|
137
|
+
mkdirSync(dirname(markdownPath), { recursive: true });
|
|
138
|
+
writeFileSync(markdownPath, renderSiteSystemReadyMarkdown(report));
|
|
139
|
+
writeFileSync(join(options.root, SITE_SYSTEM_READY_REPORT_PATHS.json), `${JSON.stringify(report, null, 2)}\n`);
|
|
140
|
+
const problems = report.problems.length;
|
|
141
|
+
io.stdout(`${report.verdict.summary}.\n` +
|
|
142
|
+
(problems > 0 ? `The report lists ${problems} problem${problems === 1 ? "" : "s"} with the inputs.\n` : "") +
|
|
143
|
+
`Wrote ${SITE_SYSTEM_READY_REPORT_PATHS.markdown} and ${SITE_SYSTEM_READY_REPORT_PATHS.json}.\n`);
|
|
144
|
+
return report.verdict.exitCode;
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
io.stderr(`${errorMessage(error)}\n`);
|
|
148
|
+
return 2;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
async function createReadyReport(options, dependencies, now) {
|
|
152
|
+
const root = options.root;
|
|
153
|
+
const config = readSiteSystemProjectConfig(root);
|
|
154
|
+
const release = options.installedRelease ?? detectInstalledBrandlabRelease(readPackageJson(root));
|
|
155
|
+
if (config.siteSystem && !release) {
|
|
156
|
+
throw new Error("The installed Brandlab release could not be read from package.json. Pin all four Brandlab packages to one exact version, or pass --installed-release.");
|
|
157
|
+
}
|
|
158
|
+
const problems = [...config.problems];
|
|
159
|
+
const findings = [];
|
|
160
|
+
// Journeys: the Playwright JSON report of the site-system spec.
|
|
161
|
+
const journeyPath = resolveIn(root, options.playwrightReport);
|
|
162
|
+
let journeyReport = null;
|
|
163
|
+
let journeyTests = 0;
|
|
164
|
+
if (!existsSync(journeyPath)) {
|
|
165
|
+
problems.push(`No Playwright report at ${displayPath(root, journeyPath)}, so the journey checks did not run (run the site-system journeys with the JSON reporter first).`);
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
journeyReport = displayPath(root, journeyPath);
|
|
169
|
+
const parsed = readJson(journeyPath);
|
|
170
|
+
if (parsed === undefined) {
|
|
171
|
+
problems.push(`${journeyReport} is not valid JSON, so the journey checks did not run.`);
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
const journeys = readSiteSystemJourneyReport(parsed);
|
|
175
|
+
findings.push(...journeys.findings);
|
|
176
|
+
problems.push(...journeys.problems);
|
|
177
|
+
journeyTests = journeys.tests;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
// Rendered and static rule modules over the sampled pages.
|
|
181
|
+
const buildDir = resolveIn(root, options.buildDir);
|
|
182
|
+
const prerenderManifest = readJson(join(buildDir, "prerender-manifest.json"));
|
|
183
|
+
const paths = planSiteSystemSamplePaths({
|
|
184
|
+
representativeRoutes: config.representativeRoutes,
|
|
185
|
+
samples: config.siteSystem?.samples ?? [],
|
|
186
|
+
prerenderManifest,
|
|
187
|
+
});
|
|
188
|
+
if (!options.baseUrl && prerenderManifest === undefined) {
|
|
189
|
+
problems.push(`No production build at ${displayPath(root, buildDir)}: run next build first, or pass --base-url.`);
|
|
190
|
+
}
|
|
191
|
+
const loaded = options.baseUrl || prerenderManifest !== undefined
|
|
192
|
+
? await loadSiteSystemPages({ paths, buildDir, baseUrl: options.baseUrl, fetch: dependencies.fetch })
|
|
193
|
+
: { pages: [], missing: paths, source: "build", problems: [] };
|
|
194
|
+
problems.push(...loaded.problems);
|
|
195
|
+
if (loaded.pages.length === 0)
|
|
196
|
+
problems.push("No page HTML was available, so the rule modules ran on the project files only.");
|
|
197
|
+
// Every runner runs: static rules read the project, rendered rules report not-applicable without pages.
|
|
198
|
+
const runners = dependencies.runners ?? SITE_SYSTEM_RENDERED_RUNNERS;
|
|
199
|
+
const rendered = await runSiteSystemRenderedRunners(runners, {
|
|
200
|
+
baseUrl: options.baseUrl ?? "",
|
|
201
|
+
pages: loaded.pages,
|
|
202
|
+
missing: loaded.missing,
|
|
203
|
+
projectRoot: root,
|
|
204
|
+
siteSystem: config.siteSystem,
|
|
205
|
+
});
|
|
206
|
+
findings.push(...rendered.findings);
|
|
207
|
+
problems.push(...rendered.problems);
|
|
208
|
+
// Fail closed: a config the gate cannot read in full, and (for a declared tier) pages it could not read.
|
|
209
|
+
const blockers = [];
|
|
210
|
+
if (!config.complete)
|
|
211
|
+
blockers.push("brandlab.config.ts could not be read in full");
|
|
212
|
+
if (config.siteSystem) {
|
|
213
|
+
if (loaded.source === "base-url" && loaded.missing.length > 0) {
|
|
214
|
+
blockers.push(`${loaded.missing.length} sample page${loaded.missing.length === 1 ? "" : "s"} did not answer with HTML`);
|
|
215
|
+
}
|
|
216
|
+
else {
|
|
217
|
+
if (loaded.missing.includes("/"))
|
|
218
|
+
blockers.push("the home page could not be read");
|
|
219
|
+
const contact = config.representativeRoutes.contact;
|
|
220
|
+
if (contact && loaded.missing.includes(contact))
|
|
221
|
+
blockers.push("the contact page could not be read");
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
// Results other tools wrote as SiteSystemFinding JSON.
|
|
225
|
+
for (const file of options.findings) {
|
|
226
|
+
const path = resolveIn(root, file);
|
|
227
|
+
const parsed = readJson(path);
|
|
228
|
+
const list = Array.isArray(parsed) ? parsed : isRecord(parsed) && Array.isArray(parsed.findings) ? parsed.findings : null;
|
|
229
|
+
if (!list) {
|
|
230
|
+
problems.push(`${displayPath(root, path)} is missing or is not a list of findings, so it was ignored.`);
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
const valid = list.filter(isSiteSystemFinding);
|
|
234
|
+
if (valid.length < list.length) {
|
|
235
|
+
problems.push(`${list.length - valid.length} result(s) in ${displayPath(root, path)} name an unknown check or status, so they were ignored.`);
|
|
236
|
+
}
|
|
237
|
+
// Supplied files carry harness and recorded results only; they cannot pass a journey or rule check.
|
|
238
|
+
const refused = [...new Set(valid.filter((finding) => !SUPPLIABLE_IDS.has(finding.checkId)).map((finding) => finding.checkId))];
|
|
239
|
+
if (refused.length > 0) {
|
|
240
|
+
problems.push(`${displayPath(root, path)} sets ${refused.join(", ")}, but --findings may only set ${[...SUPPLIABLE_IDS].join(" and ")}, so those results were ignored.`);
|
|
241
|
+
}
|
|
242
|
+
findings.push(...valid.filter((finding) => SUPPLIABLE_IDS.has(finding.checkId)));
|
|
243
|
+
}
|
|
244
|
+
// The existing brandlab-verify families the shell tier carries.
|
|
245
|
+
let verify = null;
|
|
246
|
+
try {
|
|
247
|
+
verify = evaluateBrandlabProject({
|
|
248
|
+
root,
|
|
249
|
+
...(options.installedRelease !== undefined ? { installedRelease: options.installedRelease } : {}),
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
catch (error) {
|
|
253
|
+
problems.push(`brandlab-verify could not run (${errorMessage(error)}), so its search, contact and analytics checks did not run.`);
|
|
254
|
+
}
|
|
255
|
+
return buildSiteSystemReadyReport({
|
|
256
|
+
projectId: config.projectId,
|
|
257
|
+
siteSystem: config.siteSystem,
|
|
258
|
+
release,
|
|
259
|
+
findings,
|
|
260
|
+
verify,
|
|
261
|
+
launchRecords: readSiteSystemRecords(root, "launch"),
|
|
262
|
+
humanRecords: readSiteSystemRecords(root, "human"),
|
|
263
|
+
exceptions: config.exceptions,
|
|
264
|
+
inputs: {
|
|
265
|
+
journeyReport,
|
|
266
|
+
journeyTests,
|
|
267
|
+
pageSource: loaded.pages.length === 0 ? "none" : loaded.source,
|
|
268
|
+
pages: loaded.pages.map((page) => page.path),
|
|
269
|
+
runners: runners.length,
|
|
270
|
+
findingFiles: options.findings.map((file) => displayPath(root, resolveIn(root, file))),
|
|
271
|
+
},
|
|
272
|
+
problems,
|
|
273
|
+
now,
|
|
274
|
+
launch: options.launch,
|
|
275
|
+
blockers,
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
function readPackageJson(root) {
|
|
279
|
+
const parsed = readJson(join(root, "package.json"));
|
|
280
|
+
return isRecord(parsed) ? parsed : null;
|
|
281
|
+
}
|
|
282
|
+
/** Parsed JSON, or undefined when the file is missing or invalid. */
|
|
283
|
+
function readJson(path) {
|
|
284
|
+
if (!existsSync(path))
|
|
285
|
+
return undefined;
|
|
286
|
+
try {
|
|
287
|
+
return JSON.parse(readFileSync(path, "utf8"));
|
|
288
|
+
}
|
|
289
|
+
catch {
|
|
290
|
+
return undefined;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
function resolveIn(root, path) {
|
|
294
|
+
return isAbsolute(path) ? path : resolve(root, path);
|
|
295
|
+
}
|
|
296
|
+
function displayPath(root, path) {
|
|
297
|
+
const relativePath = relative(root, path);
|
|
298
|
+
return relativePath && !relativePath.startsWith("..") && !isAbsolute(relativePath) ? relativePath : path;
|
|
299
|
+
}
|
|
300
|
+
function requireValue(argv, index, argument) {
|
|
301
|
+
const value = argv[index];
|
|
302
|
+
if (value === undefined || value.startsWith("--"))
|
|
303
|
+
throw new Error(`${argument} requires a value.`);
|
|
304
|
+
return value;
|
|
305
|
+
}
|
|
306
|
+
function errorMessage(error) {
|
|
307
|
+
return error instanceof Error ? error.message : String(error);
|
|
308
|
+
}
|
|
309
|
+
function isRecord(value) {
|
|
310
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
311
|
+
}
|
|
312
|
+
export function isBrandlabReadyCliEntrypoint(moduleUrl, argvPath) {
|
|
313
|
+
if (!argvPath)
|
|
314
|
+
return false;
|
|
315
|
+
try {
|
|
316
|
+
return realpathSync(fileURLToPath(moduleUrl)) === realpathSync(resolve(argvPath));
|
|
317
|
+
}
|
|
318
|
+
catch {
|
|
319
|
+
return false;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
if (isBrandlabReadyCliEntrypoint(import.meta.url, process.argv[1])) {
|
|
323
|
+
runBrandlabReadyCli(process.argv.slice(2)).then((code) => {
|
|
324
|
+
process.exitCode = code;
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
//# sourceMappingURL=ready-cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ready-cli.js","sourceRoot":"","sources":["../src/ready-cli.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\nimport { existsSync, mkdirSync, readFileSync, realpathSync, writeFileSync } from \"node:fs\";\nimport { dirname, isAbsolute, join, relative, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { detectInstalledBrandlabRelease, evaluateBrandlabProject } from \"./project.js\";\nimport type { SiteSystemFinding } from \"./site-system/checks.js\";\nimport { readSiteSystemProjectConfig } from \"./site-system/config.js\";\nimport { readSiteSystemJourneyReport } from \"./site-system/playwright-report.js\";\nimport { appendSiteSystemRecord, readSiteSystemRecords } from \"./site-system/records.js\";\nimport {\n buildSiteSystemReadyReport,\n renderSiteSystemReadyMarkdown,\n SITE_SYSTEM_READY_REPORT_PATHS,\n type SiteSystemReadyReport,\n type SiteSystemVerifyResult,\n} from \"./site-system/report.js\";\nimport { SITE_SYSTEM_RENDERED_RUNNERS } from \"./site-system/rule-modules.js\";\nimport {\n isSiteSystemFinding,\n runSiteSystemRenderedRunners,\n type SiteSystemRenderedRunner,\n} from \"./site-system/runners.js\";\nimport { loadSiteSystemPages, planSiteSystemSamplePaths, type SiteSystemFetch } from \"./site-system/samples.js\";\nimport { SITE_SYSTEM_RECORDED_CHECK_IDS } from \"./site-system/steps.js\";\n\ninterface ReadyReportOptions {\n command: \"report\";\n root: string;\n playwrightReport: string;\n baseUrl?: string;\n buildDir: string;\n findings: string[];\n installedRelease?: string;\n launch: boolean;\n}\n\ninterface ReadyRecordOptions {\n command: \"record\";\n root: string;\n id: string;\n evidence?: string;\n note?: string;\n}\n\ntype ReadyCliOptions = ReadyReportOptions | ReadyRecordOptions | { command: \"help\" };\n\ninterface CliIo {\n stdout: (value: string) => void;\n stderr: (value: string) => void;\n}\n\nexport interface BrandlabReadyCliDependencies {\n /** The rule modules to run (default: SITE_SYSTEM_RENDERED_RUNNERS). */\n runners?: readonly SiteSystemRenderedRunner[];\n fetch?: SiteSystemFetch;\n now?: () => Date;\n}\n\nconst DEFAULT_PLAYWRIGHT_REPORT = \"test-results/site-system.json\";\n/** The checks a --findings file may set: K10 (the lenient harness) and the other recorded checks. */\nconst SUPPLIABLE_IDS = new Set<string>(SITE_SYSTEM_RECORDED_CHECK_IDS);\nconst DEFAULT_BUILD_DIR = \".next\";\n\nconst HELP = `Brandlab ready gate\n\nWrites docs/ready/ready-report.md and .json for the Site System tier declared in\nbrandlab.config.ts (siteSystem: { tier }), and records launch-day and human steps.\n\nUsage:\n brandlab-ready [--root <path>]\n [--playwright-report <path>] journey results (default ${DEFAULT_PLAYWRIGHT_REPORT})\n [--base-url <url>] fetch the sample pages from the running site\n [--build-dir <path>] otherwise read their HTML from the build (default ${DEFAULT_BUILD_DIR})\n [--findings <path>]... K10 or X1 results as SiteSystemFinding JSON (the lenient harness)\n [--installed-release <version>]\n [--launch] also require every launch-day and human step\n brandlab-ready record <id> --evidence <where the proof is> [--note <text>] [--root <path>]\n\nExit status:\n 0 ready at the declared tier, or nothing blocks (no tier declared, or none of its checks released)\n 1 a blocking check failed or did not run, home or the contact page could not be read, a --base-url\n sample did not answer with HTML, or brandlab.config.ts could not be read in full\n (with --launch, also a step not recorded)\n 2 the gate could not run (bad arguments, an unreadable config or records file)\n`;\n\nexport function parseBrandlabReadyArgs(argv: readonly string[]): ReadyCliOptions {\n if (argv.includes(\"--help\") || argv.includes(\"-h\")) return { command: \"help\" };\n if (argv[0] === \"record\") return parseRecordArgs(argv.slice(1));\n const parsed: ReadyReportOptions = {\n command: \"report\",\n root: process.cwd(),\n playwrightReport: DEFAULT_PLAYWRIGHT_REPORT,\n buildDir: DEFAULT_BUILD_DIR,\n findings: [],\n launch: false,\n };\n for (let index = 0; index < argv.length; index += 1) {\n const argument = argv[index];\n if (argument === \"--root\") parsed.root = resolve(requireValue(argv, ++index, argument));\n else if (argument === \"--playwright-report\") parsed.playwrightReport = requireValue(argv, ++index, argument);\n else if (argument === \"--build-dir\") parsed.buildDir = requireValue(argv, ++index, argument);\n else if (argument === \"--findings\") parsed.findings.push(requireValue(argv, ++index, argument));\n else if (argument === \"--installed-release\") parsed.installedRelease = requireValue(argv, ++index, argument);\n else if (argument === \"--launch\") parsed.launch = true;\n else if (argument === \"--base-url\") {\n const value = requireValue(argv, ++index, argument);\n let url: URL;\n try {\n url = new URL(value);\n } catch {\n throw new Error(`--base-url ${value} is not a URL.`);\n }\n if (url.protocol !== \"http:\" && url.protocol !== \"https:\") throw new Error(\"--base-url must be an http or https URL.\");\n parsed.baseUrl = value;\n } else {\n throw new Error(`Unknown argument ${argument}.`);\n }\n }\n return parsed;\n}\n\nfunction parseRecordArgs(argv: readonly string[]): ReadyRecordOptions {\n const parsed: ReadyRecordOptions = { command: \"record\", root: process.cwd(), id: \"\" };\n for (let index = 0; index < argv.length; index += 1) {\n const argument = argv[index]!;\n if (argument === \"--evidence\") parsed.evidence = requireValue(argv, ++index, argument);\n else if (argument === \"--note\") parsed.note = requireValue(argv, ++index, argument);\n else if (argument === \"--root\") parsed.root = resolve(requireValue(argv, ++index, argument));\n else if (!argument.startsWith(\"--\") && !parsed.id) parsed.id = argument;\n else throw new Error(`Unknown argument ${argument}.`);\n }\n if (!parsed.id) throw new Error(\"record needs a step id, for example: brandlab-ready record X3 --evidence <PR link>.\");\n if (parsed.evidence === undefined) throw new Error(`record ${parsed.id} needs --evidence <where the proof is>.`);\n return parsed;\n}\n\nexport async function runBrandlabReadyCli(\n argv: readonly string[],\n io: CliIo = {\n stdout: (value) => process.stdout.write(value),\n stderr: (value) => process.stderr.write(value),\n },\n dependencies: BrandlabReadyCliDependencies = {},\n): Promise<number> {\n let options: ReadyCliOptions;\n try {\n options = parseBrandlabReadyArgs(argv);\n } catch (error) {\n io.stderr(`${errorMessage(error)}\\n\\n${HELP}`);\n return 2;\n }\n if (options.command === \"help\") {\n io.stdout(HELP);\n return 0;\n }\n const now = dependencies.now ?? (() => new Date());\n try {\n if (options.command === \"record\") {\n const { path, record } = appendSiteSystemRecord(options.root, {\n id: options.id,\n evidence: options.evidence ?? \"\",\n ...(options.note !== undefined ? { note: options.note } : {}),\n now: now(),\n });\n io.stdout(`Recorded ${record.id} in ${path}.\\n`);\n return 0;\n }\n const report = await createReadyReport(options, dependencies, now());\n const markdownPath = join(options.root, SITE_SYSTEM_READY_REPORT_PATHS.markdown);\n mkdirSync(dirname(markdownPath), { recursive: true });\n writeFileSync(markdownPath, renderSiteSystemReadyMarkdown(report));\n writeFileSync(join(options.root, SITE_SYSTEM_READY_REPORT_PATHS.json), `${JSON.stringify(report, null, 2)}\\n`);\n const problems = report.problems.length;\n io.stdout(\n `${report.verdict.summary}.\\n` +\n (problems > 0 ? `The report lists ${problems} problem${problems === 1 ? \"\" : \"s\"} with the inputs.\\n` : \"\") +\n `Wrote ${SITE_SYSTEM_READY_REPORT_PATHS.markdown} and ${SITE_SYSTEM_READY_REPORT_PATHS.json}.\\n`,\n );\n return report.verdict.exitCode;\n } catch (error) {\n io.stderr(`${errorMessage(error)}\\n`);\n return 2;\n }\n}\n\nasync function createReadyReport(\n options: ReadyReportOptions,\n dependencies: BrandlabReadyCliDependencies,\n now: Date,\n): Promise<SiteSystemReadyReport> {\n const root = options.root;\n const config = readSiteSystemProjectConfig(root);\n const release = options.installedRelease ?? detectInstalledBrandlabRelease(readPackageJson(root));\n if (config.siteSystem && !release) {\n throw new Error(\n \"The installed Brandlab release could not be read from package.json. Pin all four Brandlab packages to one exact version, or pass --installed-release.\",\n );\n }\n const problems: string[] = [...config.problems];\n const findings: SiteSystemFinding[] = [];\n\n // Journeys: the Playwright JSON report of the site-system spec.\n const journeyPath = resolveIn(root, options.playwrightReport);\n let journeyReport: string | null = null;\n let journeyTests = 0;\n if (!existsSync(journeyPath)) {\n problems.push(`No Playwright report at ${displayPath(root, journeyPath)}, so the journey checks did not run (run the site-system journeys with the JSON reporter first).`);\n } else {\n journeyReport = displayPath(root, journeyPath);\n const parsed = readJson(journeyPath);\n if (parsed === undefined) {\n problems.push(`${journeyReport} is not valid JSON, so the journey checks did not run.`);\n } else {\n const journeys = readSiteSystemJourneyReport(parsed);\n findings.push(...journeys.findings);\n problems.push(...journeys.problems);\n journeyTests = journeys.tests;\n }\n }\n\n // Rendered and static rule modules over the sampled pages.\n const buildDir = resolveIn(root, options.buildDir);\n const prerenderManifest = readJson(join(buildDir, \"prerender-manifest.json\"));\n const paths = planSiteSystemSamplePaths({\n representativeRoutes: config.representativeRoutes,\n samples: config.siteSystem?.samples ?? [],\n prerenderManifest,\n });\n if (!options.baseUrl && prerenderManifest === undefined) {\n problems.push(`No production build at ${displayPath(root, buildDir)}: run next build first, or pass --base-url.`);\n }\n const loaded = options.baseUrl || prerenderManifest !== undefined\n ? await loadSiteSystemPages({ paths, buildDir, baseUrl: options.baseUrl, fetch: dependencies.fetch })\n : { pages: [], missing: paths, source: \"build\" as const, problems: [] };\n problems.push(...loaded.problems);\n if (loaded.pages.length === 0) problems.push(\"No page HTML was available, so the rule modules ran on the project files only.\");\n // Every runner runs: static rules read the project, rendered rules report not-applicable without pages.\n const runners = dependencies.runners ?? SITE_SYSTEM_RENDERED_RUNNERS;\n const rendered = await runSiteSystemRenderedRunners(runners, {\n baseUrl: options.baseUrl ?? \"\",\n pages: loaded.pages,\n missing: loaded.missing,\n projectRoot: root,\n siteSystem: config.siteSystem,\n });\n findings.push(...rendered.findings);\n problems.push(...rendered.problems);\n\n // Fail closed: a config the gate cannot read in full, and (for a declared tier) pages it could not read.\n const blockers: string[] = [];\n if (!config.complete) blockers.push(\"brandlab.config.ts could not be read in full\");\n if (config.siteSystem) {\n if (loaded.source === \"base-url\" && loaded.missing.length > 0) {\n blockers.push(`${loaded.missing.length} sample page${loaded.missing.length === 1 ? \"\" : \"s\"} did not answer with HTML`);\n } else {\n if (loaded.missing.includes(\"/\")) blockers.push(\"the home page could not be read\");\n const contact = config.representativeRoutes.contact;\n if (contact && loaded.missing.includes(contact)) blockers.push(\"the contact page could not be read\");\n }\n }\n\n // Results other tools wrote as SiteSystemFinding JSON.\n for (const file of options.findings) {\n const path = resolveIn(root, file);\n const parsed = readJson(path);\n const list = Array.isArray(parsed) ? parsed : isRecord(parsed) && Array.isArray(parsed.findings) ? parsed.findings : null;\n if (!list) {\n problems.push(`${displayPath(root, path)} is missing or is not a list of findings, so it was ignored.`);\n continue;\n }\n const valid = list.filter(isSiteSystemFinding);\n if (valid.length < list.length) {\n problems.push(`${list.length - valid.length} result(s) in ${displayPath(root, path)} name an unknown check or status, so they were ignored.`);\n }\n // Supplied files carry harness and recorded results only; they cannot pass a journey or rule check.\n const refused = [...new Set(valid.filter((finding) => !SUPPLIABLE_IDS.has(finding.checkId)).map((finding) => finding.checkId))];\n if (refused.length > 0) {\n problems.push(`${displayPath(root, path)} sets ${refused.join(\", \")}, but --findings may only set ${[...SUPPLIABLE_IDS].join(\" and \")}, so those results were ignored.`);\n }\n findings.push(...valid.filter((finding) => SUPPLIABLE_IDS.has(finding.checkId)));\n }\n\n // The existing brandlab-verify families the shell tier carries.\n let verify: SiteSystemVerifyResult | null = null;\n try {\n verify = evaluateBrandlabProject({\n root,\n ...(options.installedRelease !== undefined ? { installedRelease: options.installedRelease } : {}),\n });\n } catch (error) {\n problems.push(`brandlab-verify could not run (${errorMessage(error)}), so its search, contact and analytics checks did not run.`);\n }\n\n return buildSiteSystemReadyReport({\n projectId: config.projectId,\n siteSystem: config.siteSystem,\n release,\n findings,\n verify,\n launchRecords: readSiteSystemRecords(root, \"launch\"),\n humanRecords: readSiteSystemRecords(root, \"human\"),\n exceptions: config.exceptions,\n inputs: {\n journeyReport,\n journeyTests,\n pageSource: loaded.pages.length === 0 ? \"none\" : loaded.source,\n pages: loaded.pages.map((page) => page.path),\n runners: runners.length,\n findingFiles: options.findings.map((file) => displayPath(root, resolveIn(root, file))),\n },\n problems,\n now,\n launch: options.launch,\n blockers,\n });\n}\n\nfunction readPackageJson(root: string): Parameters<typeof detectInstalledBrandlabRelease>[0] {\n const parsed = readJson(join(root, \"package.json\"));\n return isRecord(parsed) ? (parsed as Parameters<typeof detectInstalledBrandlabRelease>[0]) : null;\n}\n\n/** Parsed JSON, or undefined when the file is missing or invalid. */\nfunction readJson(path: string): unknown {\n if (!existsSync(path)) return undefined;\n try {\n return JSON.parse(readFileSync(path, \"utf8\"));\n } catch {\n return undefined;\n }\n}\n\nfunction resolveIn(root: string, path: string): string {\n return isAbsolute(path) ? path : resolve(root, path);\n}\n\nfunction displayPath(root: string, path: string): string {\n const relativePath = relative(root, path);\n return relativePath && !relativePath.startsWith(\"..\") && !isAbsolute(relativePath) ? relativePath : path;\n}\n\nfunction requireValue(argv: readonly string[], index: number, argument: string): string {\n const value = argv[index];\n if (value === undefined || value.startsWith(\"--\")) throw new Error(`${argument} requires a value.`);\n return value;\n}\n\nfunction errorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return Boolean(value) && typeof value === \"object\" && !Array.isArray(value);\n}\n\nexport function isBrandlabReadyCliEntrypoint(moduleUrl: string, argvPath: string | undefined): boolean {\n if (!argvPath) return false;\n try {\n return realpathSync(fileURLToPath(moduleUrl)) === realpathSync(resolve(argvPath));\n } catch {\n return false;\n }\n}\n\nif (isBrandlabReadyCliEntrypoint(import.meta.url, process.argv[1])) {\n runBrandlabReadyCli(process.argv.slice(2)).then((code) => {\n process.exitCode = code;\n });\n}\n"]}
|