@brandlab-digital/conformance 3.12.1 → 3.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +41 -1
- package/dist/analytics-e2e.d.ts +10 -16
- package/dist/analytics-e2e.d.ts.map +1 -1
- package/dist/analytics-e2e.js +61 -3
- package/dist/analytics-e2e.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +4 -1
- package/dist/cli.js.map +1 -1
- package/dist/current-release.js +5 -5
- package/dist/current-release.js.map +1 -1
- package/dist/edge-probe-cli.d.ts +57 -0
- package/dist/edge-probe-cli.d.ts.map +1 -0
- package/dist/edge-probe-cli.js +134 -0
- package/dist/edge-probe-cli.js.map +1 -0
- package/dist/engineering.d.ts +4 -1
- package/dist/engineering.d.ts.map +1 -1
- package/dist/engineering.js +19 -14
- package/dist/engineering.js.map +1 -1
- package/dist/estate-sample.d.ts +60 -0
- package/dist/estate-sample.d.ts.map +1 -0
- package/dist/estate-sample.js +144 -0
- package/dist/estate-sample.js.map +1 -0
- package/dist/estate.d.ts +45 -0
- package/dist/estate.d.ts.map +1 -1
- package/dist/estate.js +82 -22
- package/dist/estate.js.map +1 -1
- package/dist/origin-region.d.ts +14 -0
- package/dist/origin-region.d.ts.map +1 -0
- package/dist/origin-region.js +46 -0
- package/dist/origin-region.js.map +1 -0
- package/dist/prefetch-probe-cli.d.ts +36 -0
- package/dist/prefetch-probe-cli.d.ts.map +1 -0
- package/dist/prefetch-probe-cli.js +163 -0
- package/dist/prefetch-probe-cli.js.map +1 -0
- package/dist/project.d.ts +20 -0
- package/dist/project.d.ts.map +1 -1
- package/dist/project.js +92 -2
- package/dist/project.js.map +1 -1
- package/dist/ready-cli.d.ts.map +1 -1
- package/dist/ready-cli.js +8 -3
- package/dist/ready-cli.js.map +1 -1
- package/dist/rule-registry.d.ts +5 -0
- package/dist/rule-registry.d.ts.map +1 -1
- package/dist/rule-registry.js +1 -0
- package/dist/rule-registry.js.map +1 -1
- package/dist/site-system/checks.d.ts +62 -1
- package/dist/site-system/checks.d.ts.map +1 -1
- package/dist/site-system/checks.js +9 -0
- package/dist/site-system/checks.js.map +1 -1
- package/dist/site-system/config.d.ts +27 -0
- package/dist/site-system/config.d.ts.map +1 -1
- package/dist/site-system/config.js +50 -1
- package/dist/site-system/config.js.map +1 -1
- package/dist/site-system/index.d.ts +1 -1
- package/dist/site-system/index.d.ts.map +1 -1
- package/dist/site-system/index.js +1 -1
- package/dist/site-system/index.js.map +1 -1
- package/dist/site-system/journeys/plan.d.ts +9 -2
- package/dist/site-system/journeys/plan.d.ts.map +1 -1
- package/dist/site-system/journeys/plan.js +11 -1
- package/dist/site-system/journeys/plan.js.map +1 -1
- package/dist/site-system/journeys/prefetch-measure.d.ts +28 -0
- package/dist/site-system/journeys/prefetch-measure.d.ts.map +1 -0
- package/dist/site-system/journeys/prefetch-measure.js +94 -0
- package/dist/site-system/journeys/prefetch-measure.js.map +1 -0
- package/dist/site-system/journeys/prefetch-policy.d.ts +85 -0
- package/dist/site-system/journeys/prefetch-policy.d.ts.map +1 -0
- package/dist/site-system/journeys/prefetch-policy.js +152 -0
- package/dist/site-system/journeys/prefetch-policy.js.map +1 -0
- package/dist/site-system/journeys/prefetch.d.ts +3 -0
- package/dist/site-system/journeys/prefetch.d.ts.map +1 -0
- package/dist/site-system/journeys/prefetch.js +37 -0
- package/dist/site-system/journeys/prefetch.js.map +1 -0
- package/dist/site-system/journeys/variants.d.ts +34 -0
- package/dist/site-system/journeys/variants.d.ts.map +1 -0
- package/dist/site-system/journeys/variants.js +139 -0
- package/dist/site-system/journeys/variants.js.map +1 -0
- package/dist/site-system/report.d.ts.map +1 -1
- package/dist/site-system/report.js +19 -5
- package/dist/site-system/report.js.map +1 -1
- package/dist/site-system/rule-modules.d.ts.map +1 -1
- package/dist/site-system/rule-modules.js +10 -0
- package/dist/site-system/rule-modules.js.map +1 -1
- package/dist/site-system/rules/delivery/h13-inbox.d.ts +23 -0
- package/dist/site-system/rules/delivery/h13-inbox.d.ts.map +1 -0
- package/dist/site-system/rules/delivery/h13-inbox.js +208 -0
- package/dist/site-system/rules/delivery/h13-inbox.js.map +1 -0
- package/dist/site-system/rules/delivery/index.d.ts +12 -0
- package/dist/site-system/rules/delivery/index.d.ts.map +1 -0
- package/dist/site-system/rules/delivery/index.js +7 -0
- package/dist/site-system/rules/delivery/index.js.map +1 -0
- package/dist/site-system/rules/full/common.d.ts +3 -0
- package/dist/site-system/rules/full/common.d.ts.map +1 -1
- package/dist/site-system/rules/full/common.js.map +1 -1
- package/dist/site-system/rules/full/e1-pattern.d.ts +4 -0
- package/dist/site-system/rules/full/e1-pattern.d.ts.map +1 -0
- package/dist/site-system/rules/full/e1-pattern.js +47 -0
- package/dist/site-system/rules/full/e1-pattern.js.map +1 -0
- package/dist/site-system/rules/full/estate.d.ts +20 -1
- package/dist/site-system/rules/full/estate.d.ts.map +1 -1
- package/dist/site-system/rules/full/estate.js +33 -5
- package/dist/site-system/rules/full/estate.js.map +1 -1
- package/dist/site-system/rules/full/geography.d.ts +8 -2
- package/dist/site-system/rules/full/geography.d.ts.map +1 -1
- package/dist/site-system/rules/full/geography.js +6 -2
- package/dist/site-system/rules/full/geography.js.map +1 -1
- package/dist/site-system/rules/full/index.d.ts +3 -2
- package/dist/site-system/rules/full/index.d.ts.map +1 -1
- package/dist/site-system/rules/full/index.js +27 -9
- package/dist/site-system/rules/full/index.js.map +1 -1
- package/dist/site-system/rules/full/k12-slots.d.ts +4 -0
- package/dist/site-system/rules/full/k12-slots.d.ts.map +1 -0
- package/dist/site-system/rules/full/k12-slots.js +40 -0
- package/dist/site-system/rules/full/k12-slots.js.map +1 -0
- package/dist/site-system/rules/full/loc-index.d.ts +5 -0
- package/dist/site-system/rules/full/loc-index.d.ts.map +1 -1
- package/dist/site-system/rules/full/loc-index.js +43 -2
- package/dist/site-system/rules/full/loc-index.js.map +1 -1
- package/dist/site-system/rules/full/strategy.d.ts +4 -0
- package/dist/site-system/rules/full/strategy.d.ts.map +1 -1
- package/dist/site-system/rules/full/strategy.js +19 -3
- package/dist/site-system/rules/full/strategy.js.map +1 -1
- package/dist/site-system/rules/full/w3-refresh.d.ts +22 -0
- package/dist/site-system/rules/full/w3-refresh.d.ts.map +1 -0
- package/dist/site-system/rules/full/w3-refresh.js +70 -0
- package/dist/site-system/rules/full/w3-refresh.js.map +1 -0
- package/dist/site-system/rules/guards/index.d.ts +22 -0
- package/dist/site-system/rules/guards/index.d.ts.map +1 -0
- package/dist/site-system/rules/guards/index.js +109 -0
- package/dist/site-system/rules/guards/index.js.map +1 -0
- package/dist/site-system/rules/guards/w10-crawlers.d.ts +55 -0
- package/dist/site-system/rules/guards/w10-crawlers.d.ts.map +1 -0
- package/dist/site-system/rules/guards/w10-crawlers.js +182 -0
- package/dist/site-system/rules/guards/w10-crawlers.js.map +1 -0
- package/dist/site-system/rules/guards/w9-params.d.ts +19 -0
- package/dist/site-system/rules/guards/w9-params.d.ts.map +1 -0
- package/dist/site-system/rules/guards/w9-params.js +84 -0
- package/dist/site-system/rules/guards/w9-params.js.map +1 -0
- package/dist/site-system/rules/sections/k2-skin-coverage.d.ts +2 -2
- package/dist/site-system/rules/sections/k2-skin-coverage.d.ts.map +1 -1
- package/dist/site-system/rules/sections/k2-skin-coverage.js +6 -2
- package/dist/site-system/rules/sections/k2-skin-coverage.js.map +1 -1
- package/dist/site-system/runners.js +1 -1
- package/dist/site-system/runners.js.map +1 -1
- package/dist/site-system-e2e.d.ts +1 -0
- package/dist/site-system-e2e.d.ts.map +1 -1
- package/dist/site-system-e2e.js +8 -2
- package/dist/site-system-e2e.js.map +1 -1
- package/package.json +6 -4
|
@@ -1 +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"]}
|
|
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 = launchOnlyNotRun(\n check,\n blocks,\n input.launch === true,\n mergeFindings(findings.filter((finding) => finding.checkId === check.id), notRunMessage(check)),\n );\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 * A check whose not-run blocks only at launch (H13): under `--launch` a blocking not-run is a failure; otherwise it\n * stays not run with a note, and the verdict does not count it.\n */\nfunction launchOnlyNotRun(\n check: SiteSystemCheck,\n blocks: boolean,\n launch: boolean,\n merged: { status: SiteSystemReadyStatus; messages: string[]; warnings: string[] },\n): { status: SiteSystemReadyStatus; messages: string[]; warnings: string[] } {\n if (check.notRunBlocks !== \"launch\" || merged.status !== \"not-run\" || !blocks) return merged;\n if (launch) return { ...merged, status: \"fail\", messages: merged.messages.map((message) => `Launch needs this check to run. ${message}`) };\n return { ...merged, messages: [...merged.messages, \"It blocks only at launch (brandlab-ready --launch).\"] };\n}\n\n/**\n * Any fail fails the check; otherwise a rule's own not-run result (naming what it needed) leaves it not run; otherwise\n * any pass passes it; only not-applicable results make it not applicable. A warning is a pass that carries its message\n * 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\", \"not-run\", \"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 launchOnly = new Set<string>((SITE_SYSTEM_CHECKS as readonly SiteSystemCheck[]).filter((check) => check.notRunBlocks === \"launch\").map((check) => check.id));\n // A launch-only check that did not run is reported, not counted: under --launch it is already a failure.\n const notRun = blocking.filter((check) => check.status === \"not-run\" && !launchOnly.has(check.id)).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"]}
|
|
@@ -1 +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;
|
|
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;AAsC7D,eAAO,MAAM,4BAA4B,EAAE,SAAS,wBAAwB,EAW3E,CAAC"}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { runDeliveryRules } from "./rules/delivery/index.js";
|
|
1
2
|
import { runDesignRules } from "./rules/design/index.js";
|
|
2
3
|
import { runEnquiryRenderedRules } from "./rules/enquiry/index.js";
|
|
3
4
|
import { runFormRules } from "./rules/form/index.js";
|
|
4
5
|
import { runFullTierRules } from "./rules/full/index.js";
|
|
6
|
+
import { runCostGuardRules } from "./rules/guards/index.js";
|
|
5
7
|
import { runLibraryRules } from "./rules/library/index.js";
|
|
6
8
|
import { runNavigationRules } from "./rules/navigation/index.js";
|
|
7
9
|
import { runSectionRules } from "./rules/sections/index.js";
|
|
@@ -25,6 +27,12 @@ const sectionRules = function sectionRules(input) {
|
|
|
25
27
|
const fullTierRules = function fullTierRules(input) {
|
|
26
28
|
return runFullTierRules(input);
|
|
27
29
|
};
|
|
30
|
+
const costGuardRules = function costGuardRules(input) {
|
|
31
|
+
return runCostGuardRules(input);
|
|
32
|
+
};
|
|
33
|
+
const deliveryRules = function deliveryRules(input) {
|
|
34
|
+
return runDeliveryRules(input);
|
|
35
|
+
};
|
|
28
36
|
export const SITE_SYSTEM_RENDERED_RUNNERS = [
|
|
29
37
|
shellRules,
|
|
30
38
|
enquiryRules,
|
|
@@ -34,5 +42,7 @@ export const SITE_SYSTEM_RENDERED_RUNNERS = [
|
|
|
34
42
|
formRules,
|
|
35
43
|
sectionRules,
|
|
36
44
|
fullTierRules,
|
|
45
|
+
costGuardRules,
|
|
46
|
+
deliveryRules,
|
|
37
47
|
];
|
|
38
48
|
//# sourceMappingURL=rule-modules.js.map
|
|
@@ -1 +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,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AACjE,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC5D,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;AACF,MAAM,YAAY,GAA6B,SAAS,YAAY,CAAC,KAAK;IACxE,OAAO,eAAe,CAAC,KAAK,CAAC,CAAC;AAChC,CAAC,CAAC;AACF,MAAM,SAAS,GAA6B,SAAS,SAAS,CAAC,KAAK;IAClE,OAAO,YAAY,CAAC,KAAK,CAAC,CAAC;AAC7B,CAAC,CAAC;AACF,MAAM,YAAY,GAA6B,SAAS,YAAY,CAAC,KAAK;IACxE,OAAO,eAAe,CAAC,KAAK,CAAC,CAAC;AAChC,CAAC,CAAC;AACF,MAAM,aAAa,GAA6B,SAAS,aAAa,CAAC,KAAK;IAC1E,OAAO,gBAAgB,CAAC,KAAK,CAAC,CAAC;AACjC,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,4BAA4B,GAAwC;IAC/E,UAAU;IACV,YAAY;IACZ,cAAc;IACd,kBAAkB;IAClB,YAAY;IACZ,SAAS;IACT,YAAY;IACZ,aAAa;CACd,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 { runFormRules } from \"./rules/form/index.js\";\nimport { runFullTierRules } from \"./rules/full/index.js\";\nimport { runLibraryRules } from \"./rules/library/index.js\";\nimport { runNavigationRules } from \"./rules/navigation/index.js\";\nimport { runSectionRules } from \"./rules/sections/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};\nconst libraryRules: SiteSystemRenderedRunner = function libraryRules(input) {\n return runLibraryRules(input);\n};\nconst formRules: SiteSystemRenderedRunner = function formRules(input) {\n return runFormRules(input);\n};\nconst sectionRules: SiteSystemRenderedRunner = function sectionRules(input) {\n return runSectionRules(input);\n};\nconst fullTierRules: SiteSystemRenderedRunner = function fullTierRules(input) {\n return runFullTierRules(input);\n};\n\nexport const SITE_SYSTEM_RENDERED_RUNNERS: readonly SiteSystemRenderedRunner[] = [\n shellRules,\n enquiryRules,\n runDesignRules,\n runNavigationRules,\n libraryRules,\n formRules,\n sectionRules,\n fullTierRules,\n];\n"]}
|
|
1
|
+
{"version":3,"file":"rule-modules.js","sourceRoot":"","sources":["../../src/site-system/rule-modules.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AACjE,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC5D,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;AACF,MAAM,YAAY,GAA6B,SAAS,YAAY,CAAC,KAAK;IACxE,OAAO,eAAe,CAAC,KAAK,CAAC,CAAC;AAChC,CAAC,CAAC;AACF,MAAM,SAAS,GAA6B,SAAS,SAAS,CAAC,KAAK;IAClE,OAAO,YAAY,CAAC,KAAK,CAAC,CAAC;AAC7B,CAAC,CAAC;AACF,MAAM,YAAY,GAA6B,SAAS,YAAY,CAAC,KAAK;IACxE,OAAO,eAAe,CAAC,KAAK,CAAC,CAAC;AAChC,CAAC,CAAC;AACF,MAAM,aAAa,GAA6B,SAAS,aAAa,CAAC,KAAK;IAC1E,OAAO,gBAAgB,CAAC,KAAK,CAAC,CAAC;AACjC,CAAC,CAAC;AACF,MAAM,cAAc,GAA6B,SAAS,cAAc,CAAC,KAAK;IAC5E,OAAO,iBAAiB,CAAC,KAAK,CAAC,CAAC;AAClC,CAAC,CAAC;AACF,MAAM,aAAa,GAA6B,SAAS,aAAa,CAAC,KAAK;IAC1E,OAAO,gBAAgB,CAAC,KAAK,CAAC,CAAC;AACjC,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,4BAA4B,GAAwC;IAC/E,UAAU;IACV,YAAY;IACZ,cAAc;IACd,kBAAkB;IAClB,YAAY;IACZ,SAAS;IACT,YAAY;IACZ,aAAa;IACb,cAAc;IACd,aAAa;CACd,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 { runDeliveryRules } from \"./rules/delivery/index.js\";\nimport { runDesignRules } from \"./rules/design/index.js\";\nimport { runEnquiryRenderedRules } from \"./rules/enquiry/index.js\";\nimport { runFormRules } from \"./rules/form/index.js\";\nimport { runFullTierRules } from \"./rules/full/index.js\";\nimport { runCostGuardRules } from \"./rules/guards/index.js\";\nimport { runLibraryRules } from \"./rules/library/index.js\";\nimport { runNavigationRules } from \"./rules/navigation/index.js\";\nimport { runSectionRules } from \"./rules/sections/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};\nconst libraryRules: SiteSystemRenderedRunner = function libraryRules(input) {\n return runLibraryRules(input);\n};\nconst formRules: SiteSystemRenderedRunner = function formRules(input) {\n return runFormRules(input);\n};\nconst sectionRules: SiteSystemRenderedRunner = function sectionRules(input) {\n return runSectionRules(input);\n};\nconst fullTierRules: SiteSystemRenderedRunner = function fullTierRules(input) {\n return runFullTierRules(input);\n};\nconst costGuardRules: SiteSystemRenderedRunner = function costGuardRules(input) {\n return runCostGuardRules(input);\n};\nconst deliveryRules: SiteSystemRenderedRunner = function deliveryRules(input) {\n return runDeliveryRules(input);\n};\n\nexport const SITE_SYSTEM_RENDERED_RUNNERS: readonly SiteSystemRenderedRunner[] = [\n shellRules,\n enquiryRules,\n runDesignRules,\n runNavigationRules,\n libraryRules,\n formRules,\n sectionRules,\n fullTierRules,\n costGuardRules,\n deliveryRules,\n];\n"]}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { SiteSystemFinding } from "../../checks.js";
|
|
2
|
+
export interface H13Dependencies {
|
|
3
|
+
/** The shell environment (`process.env`): the inbox variable and RESEND_API_KEY. */
|
|
4
|
+
readonly environment: Readonly<Record<string, string | undefined>>;
|
|
5
|
+
readonly resolveMx: (domain: string) => Promise<readonly {
|
|
6
|
+
exchange: string;
|
|
7
|
+
priority: number;
|
|
8
|
+
}[]>;
|
|
9
|
+
readonly fetch: typeof globalThis.fetch;
|
|
10
|
+
}
|
|
11
|
+
export interface ContactDelivery {
|
|
12
|
+
readonly path: string;
|
|
13
|
+
readonly sender: string | null;
|
|
14
|
+
readonly recipientEnv: string | null;
|
|
15
|
+
}
|
|
16
|
+
/** The first contact contract's delivery settings, read from the syntax tree; null when the project has none. */
|
|
17
|
+
export declare function readContactDelivery(projectRoot: string): ContactDelivery | null;
|
|
18
|
+
/** Why an address cannot carry real mail (reserved or sandbox domain), or null when it can. */
|
|
19
|
+
export declare function reservedMailDomainProblem(value: string): string | null;
|
|
20
|
+
export declare function checkH13(projectRoot: string, dependencies: H13Dependencies): Promise<SiteSystemFinding[]>;
|
|
21
|
+
/** The live dependencies: the shell environment, DNS and the network. */
|
|
22
|
+
export declare function liveH13Dependencies(): H13Dependencies;
|
|
23
|
+
//# sourceMappingURL=h13-inbox.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"h13-inbox.d.ts","sourceRoot":"","sources":["../../../../src/site-system/rules/delivery/h13-inbox.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAkBzD,MAAM,WAAW,eAAe;IAC9B,oFAAoF;IACpF,QAAQ,CAAC,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC;IACnE,QAAQ,CAAC,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,SAAS;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC,CAAC;IACnG,QAAQ,CAAC,KAAK,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;CACzC;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CACtC;AAED,iHAAiH;AACjH,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CAa/E;AAUD,+FAA+F;AAC/F,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAStE;AAsED,wBAAsB,QAAQ,CAAC,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,eAAe,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAsE/G;AAED,yEAAyE;AACzE,wBAAgB,mBAAmB,IAAI,eAAe,CAMrD"}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* H13 (3.14.0): the business inbox can receive enquiries before launch. The cheap pre-launch half of contact delivery
|
|
3
|
+
* truth: an accepted send to Resend is only queued, and an inbox with no mail server, a sender domain Resend has not
|
|
4
|
+
* verified, or an inbox on Resend's suppression list loses every enquiry while the site still says "sent".
|
|
5
|
+
*
|
|
6
|
+
* - The contract's `delivery.sender` is a real address: not a reserved (`.invalid`, `.example`, `.test`, `.localhost`,
|
|
7
|
+
* `example.com/net/org`) or Resend sandbox (`resend.dev`) domain. This part needs nothing but the source.
|
|
8
|
+
* - The business inbox (the value of the contract's `delivery.recipientEnv`, read from the shell environment, never
|
|
9
|
+
* from an env file) has MX records.
|
|
10
|
+
* - With a full-access Resend key in the shell (`RESEND_LOOKUP_API_KEY`, else `RESEND_API_KEY`): the sender domain is a
|
|
11
|
+
* verified domain in the Resend account, and the inbox is not on Resend's suppression list.
|
|
12
|
+
* A part that cannot run (no inbox variable, no key, a sending-only key, a failed lookup) makes the check "not run" with
|
|
13
|
+
* what to supply. Not run never blocks the tier; under `brandlab-ready --launch` it is a failure (`notRunBlocks`). Addresses are shown masked; the key is never written.
|
|
14
|
+
*/
|
|
15
|
+
import { resolveMx as dnsResolveMx } from "node:dns/promises";
|
|
16
|
+
import { relative } from "node:path";
|
|
17
|
+
import { fail, notApplicable, pass } from "../enquiry/common.js";
|
|
18
|
+
import { collectBrandlabSource, findImportedCalls, objectPropertyValue, stringLiteralValue, } from "../../../source-files.js";
|
|
19
|
+
const CHECK = "H13";
|
|
20
|
+
const SITE_CORE_CONTACT = /^@brandlab-digital\/site-core\/contact$/;
|
|
21
|
+
const RESEND_API = "https://api.resend.com";
|
|
22
|
+
const MAILBOX = /^[^\s<>,@]+@([^\s<>,@]+\.[^\s<>,@]+)$/;
|
|
23
|
+
const RESERVED_SUFFIXES = [".invalid", ".example", ".test", ".localhost", ".local"];
|
|
24
|
+
const RESERVED_DOMAINS = new Set(["example.com", "example.net", "example.org", "localhost"]);
|
|
25
|
+
const SANDBOX_DOMAINS = new Set(["resend.dev"]);
|
|
26
|
+
const LOOKUP_TIMEOUT_MS = 15_000;
|
|
27
|
+
/** The first contact contract's delivery settings, read from the syntax tree; null when the project has none. */
|
|
28
|
+
export function readContactDelivery(projectRoot) {
|
|
29
|
+
const source = collectBrandlabSource(projectRoot);
|
|
30
|
+
for (const file of source.files) {
|
|
31
|
+
const [call] = findImportedCalls(file, SITE_CORE_CONTACT, "defineContactContract");
|
|
32
|
+
if (!call)
|
|
33
|
+
continue;
|
|
34
|
+
const delivery = objectPropertyValue(call.arguments[0], "delivery");
|
|
35
|
+
return {
|
|
36
|
+
path: file.path,
|
|
37
|
+
sender: stringLiteralValue(objectPropertyValue(delivery, "sender")),
|
|
38
|
+
recipientEnv: stringLiteralValue(objectPropertyValue(delivery, "recipientEnv")),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
/** The address inside `Name <address>` or a bare address, lower-cased; null when it is not one. */
|
|
44
|
+
function mailbox(value) {
|
|
45
|
+
const trimmed = value.trim();
|
|
46
|
+
const address = (trimmed.match(/<([^<>]+)>\s*$/)?.[1] ?? trimmed).trim().toLowerCase();
|
|
47
|
+
const domain = address.match(MAILBOX)?.[1];
|
|
48
|
+
return domain ? { address, domain } : null;
|
|
49
|
+
}
|
|
50
|
+
/** Why an address cannot carry real mail (reserved or sandbox domain), or null when it can. */
|
|
51
|
+
export function reservedMailDomainProblem(value) {
|
|
52
|
+
const parsed = mailbox(value);
|
|
53
|
+
if (!parsed)
|
|
54
|
+
return "is not an email address";
|
|
55
|
+
const { domain } = parsed;
|
|
56
|
+
if (RESERVED_DOMAINS.has(domain) || RESERVED_SUFFIXES.some((suffix) => domain.endsWith(suffix))) {
|
|
57
|
+
return `uses ${domain}, a reserved domain that never receives or sends real mail`;
|
|
58
|
+
}
|
|
59
|
+
if (SANDBOX_DOMAINS.has(domain))
|
|
60
|
+
return `uses ${domain}, Resend's sandbox domain, which only delivers to the account owner`;
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
function masked(address) {
|
|
64
|
+
const [local = "", domain = ""] = address.split("@");
|
|
65
|
+
return `${local.slice(0, 1)}…@${domain}`;
|
|
66
|
+
}
|
|
67
|
+
async function resendGet(dependencies, apiKey, path) {
|
|
68
|
+
try {
|
|
69
|
+
const response = await dependencies.fetch(`${RESEND_API}${path}`, {
|
|
70
|
+
method: "GET",
|
|
71
|
+
headers: { authorization: `Bearer ${apiKey}`, accept: "application/json" },
|
|
72
|
+
signal: AbortSignal.timeout(LOOKUP_TIMEOUT_MS),
|
|
73
|
+
});
|
|
74
|
+
let body;
|
|
75
|
+
try {
|
|
76
|
+
body = await response.json();
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
body = undefined;
|
|
80
|
+
}
|
|
81
|
+
return { status: response.status, body: body && typeof body === "object" && !Array.isArray(body) ? body : {} };
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
return { error: error instanceof Error ? error.name : "error" };
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
function refusal(what, answer) {
|
|
88
|
+
if ("error" in answer)
|
|
89
|
+
return `Not run: the Resend ${what} lookup failed (${answer.error}); run brandlab-ready again online.`;
|
|
90
|
+
const name = typeof answer.body.name === "string" && /^[a-z_]{1,64}$/.test(answer.body.name) ? answer.body.name : "";
|
|
91
|
+
if (name === "restricted_api_key") {
|
|
92
|
+
return "Not run: the Resend key is sending-only, and Resend's domain and suppression lookups need a full-access key; set one in RESEND_LOOKUP_API_KEY for this run only.";
|
|
93
|
+
}
|
|
94
|
+
return `Not run: Resend answered ${answer.status}${name ? ` ${name}` : ""} to the ${what} lookup.`;
|
|
95
|
+
}
|
|
96
|
+
async function senderDomainStatus(dependencies, apiKey, domain) {
|
|
97
|
+
const answer = await resendGet(dependencies, apiKey, "/domains");
|
|
98
|
+
if ("error" in answer || answer.status !== 200)
|
|
99
|
+
return { ok: false, notRun: refusal("domains", answer) };
|
|
100
|
+
const domains = Array.isArray(answer.body.data) ? answer.body.data : [];
|
|
101
|
+
for (const entry of domains) {
|
|
102
|
+
const record = entry && typeof entry === "object" ? entry : {};
|
|
103
|
+
if (typeof record.name === "string" && record.name.toLowerCase() === domain) {
|
|
104
|
+
return { ok: true, value: typeof record.status === "string" ? record.status : "unknown" };
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return { ok: true, value: null };
|
|
108
|
+
}
|
|
109
|
+
async function suppression(dependencies, apiKey, address) {
|
|
110
|
+
const answer = await resendGet(dependencies, apiKey, `/suppressions/${encodeURIComponent(address)}`);
|
|
111
|
+
if (!("error" in answer) && answer.status === 404)
|
|
112
|
+
return { ok: true, value: null };
|
|
113
|
+
if ("error" in answer || answer.status !== 200)
|
|
114
|
+
return { ok: false, notRun: refusal("suppression", answer) };
|
|
115
|
+
const origin = typeof answer.body.origin === "string" && /^[a-z_]{1,32}$/.test(answer.body.origin) ? answer.body.origin : "unknown origin";
|
|
116
|
+
const since = typeof answer.body.created_at === "string" ? answer.body.created_at.slice(0, 10) : "";
|
|
117
|
+
return { ok: true, value: { origin, since: /^\d{4}-\d{2}-\d{2}$/.test(since) ? since : "an unknown date" } };
|
|
118
|
+
}
|
|
119
|
+
async function mxProblem(dependencies, domain) {
|
|
120
|
+
try {
|
|
121
|
+
const records = await dependencies.resolveMx(domain);
|
|
122
|
+
return { ok: true, value: records.some((record) => record.exchange && record.exchange !== ".") ? null : `${domain} has no MX records` };
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
const code = error?.code;
|
|
126
|
+
if (code === "ENODATA" || code === "ENOTFOUND" || code === "NXDOMAIN")
|
|
127
|
+
return { ok: true, value: `${domain} has no MX records` };
|
|
128
|
+
return { ok: false, notRun: `Not run: the MX lookup for ${domain} failed (${typeof code === "string" ? code : "error"}); run brandlab-ready again online.` };
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
export async function checkH13(projectRoot, dependencies) {
|
|
132
|
+
const delivery = readContactDelivery(projectRoot);
|
|
133
|
+
if (!delivery)
|
|
134
|
+
return [notApplicable(CHECK, "The project has no contact contract, so there is no business inbox to check.")];
|
|
135
|
+
const target = relative(projectRoot, `${projectRoot}/${delivery.path}`);
|
|
136
|
+
const failures = [];
|
|
137
|
+
const notRun = [];
|
|
138
|
+
const senderProblem = delivery.sender === null ? "could not be read as a literal" : reservedMailDomainProblem(delivery.sender);
|
|
139
|
+
if (senderProblem) {
|
|
140
|
+
failures.push(fail(CHECK, target, `The contact contract's delivery.sender ${senderProblem}; use the client's domain verified in Resend.`));
|
|
141
|
+
}
|
|
142
|
+
const sender = delivery.sender === null ? null : mailbox(delivery.sender);
|
|
143
|
+
const recipientEnv = delivery.recipientEnv;
|
|
144
|
+
const rawRecipients = recipientEnv ? dependencies.environment[recipientEnv] ?? "" : "";
|
|
145
|
+
const recipients = rawRecipients.split(",").map((value) => value.trim()).filter(Boolean);
|
|
146
|
+
if (!recipientEnv) {
|
|
147
|
+
failures.push(fail(CHECK, target, "The contact contract's delivery.recipientEnv could not be read as a literal."));
|
|
148
|
+
}
|
|
149
|
+
else if (recipients.length === 0) {
|
|
150
|
+
notRun.push(`Not run: ${recipientEnv} is not set in this shell; set it to the production business inbox (from the Vercel project's Production environment) for this run, then run brandlab-ready again.`);
|
|
151
|
+
}
|
|
152
|
+
const inboxes = [];
|
|
153
|
+
for (const recipient of recipients) {
|
|
154
|
+
const problem = reservedMailDomainProblem(recipient);
|
|
155
|
+
const parsed = mailbox(recipient);
|
|
156
|
+
if (problem || !parsed) {
|
|
157
|
+
failures.push(fail(CHECK, target, `The business inbox in ${recipientEnv} ${problem ?? "is not an email address"}.`));
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
inboxes.push(parsed);
|
|
161
|
+
}
|
|
162
|
+
for (const domain of [...new Set(inboxes.map((inbox) => inbox.domain))]) {
|
|
163
|
+
const mx = await mxProblem(dependencies, domain);
|
|
164
|
+
if (!mx.ok)
|
|
165
|
+
notRun.push(mx.notRun);
|
|
166
|
+
else if (mx.value)
|
|
167
|
+
failures.push(fail(CHECK, target, `The business inbox's domain ${mx.value}, so no enquiry email can reach it.`));
|
|
168
|
+
}
|
|
169
|
+
// The lookup key (full access) when set, so the sending key can stay sending-only; otherwise the sending key.
|
|
170
|
+
const apiKey = dependencies.environment.RESEND_LOOKUP_API_KEY?.trim() || dependencies.environment.RESEND_API_KEY?.trim() || "";
|
|
171
|
+
if (!apiKey) {
|
|
172
|
+
notRun.push("Not run: neither RESEND_LOOKUP_API_KEY nor RESEND_API_KEY is set in this shell, so the Resend domain and suppression checks did not run; set a full-access key in RESEND_LOOKUP_API_KEY for this run only.");
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
if (sender && !senderProblem) {
|
|
176
|
+
const status = await senderDomainStatus(dependencies, apiKey, sender.domain);
|
|
177
|
+
if (!status.ok)
|
|
178
|
+
notRun.push(status.notRun);
|
|
179
|
+
else if (status.value === null)
|
|
180
|
+
failures.push(fail(CHECK, target, `The sender domain ${sender.domain} is not a domain in the Resend account; add and verify it in Resend (SPF and DKIM).`));
|
|
181
|
+
else if (status.value !== "verified")
|
|
182
|
+
failures.push(fail(CHECK, target, `The sender domain ${sender.domain} is ${status.value} in Resend, not verified; finish its DNS records in Resend.`));
|
|
183
|
+
}
|
|
184
|
+
for (const inbox of inboxes) {
|
|
185
|
+
const suppressed = await suppression(dependencies, apiKey, inbox.address);
|
|
186
|
+
if (!suppressed.ok)
|
|
187
|
+
notRun.push(suppressed.notRun);
|
|
188
|
+
else if (suppressed.value) {
|
|
189
|
+
failures.push(fail(CHECK, target, `The business inbox ${masked(inbox.address)} is on Resend's suppression list (${suppressed.value.origin}, since ${suppressed.value.since}), so Resend drops every enquiry to it; fix the inbox, then remove it in Resend → Suppressions.`));
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
if (failures.length > 0)
|
|
194
|
+
return failures;
|
|
195
|
+
if (notRun.length > 0)
|
|
196
|
+
return [...new Set(notRun)].map((message) => ({ checkId: CHECK, status: "not-run", target, message }));
|
|
197
|
+
const inboxList = inboxes.map((inbox) => masked(inbox.address)).join(", ");
|
|
198
|
+
return [pass(CHECK, `The business inbox (${inboxList}) has MX records and is not on Resend's suppression list, and the sender domain ${sender?.domain ?? ""} is verified in Resend.`, target)];
|
|
199
|
+
}
|
|
200
|
+
/** The live dependencies: the shell environment, DNS and the network. */
|
|
201
|
+
export function liveH13Dependencies() {
|
|
202
|
+
return {
|
|
203
|
+
environment: process.env,
|
|
204
|
+
resolveMx: (domain) => dnsResolveMx(domain),
|
|
205
|
+
fetch: (...args) => globalThis.fetch(...args),
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
//# sourceMappingURL=h13-inbox.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"h13-inbox.js","sourceRoot":"","sources":["../../../../src/site-system/rules/delivery/h13-inbox.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * H13 (3.14.0): the business inbox can receive enquiries before launch. The cheap pre-launch half of contact delivery\n * truth: an accepted send to Resend is only queued, and an inbox with no mail server, a sender domain Resend has not\n * verified, or an inbox on Resend's suppression list loses every enquiry while the site still says \"sent\".\n *\n * - The contract's `delivery.sender` is a real address: not a reserved (`.invalid`, `.example`, `.test`, `.localhost`,\n * `example.com/net/org`) or Resend sandbox (`resend.dev`) domain. This part needs nothing but the source.\n * - The business inbox (the value of the contract's `delivery.recipientEnv`, read from the shell environment, never\n * from an env file) has MX records.\n * - With a full-access Resend key in the shell (`RESEND_LOOKUP_API_KEY`, else `RESEND_API_KEY`): the sender domain is a\n * verified domain in the Resend account, and the inbox is not on Resend's suppression list.\n * A part that cannot run (no inbox variable, no key, a sending-only key, a failed lookup) makes the check \"not run\" with\n * what to supply. Not run never blocks the tier; under `brandlab-ready --launch` it is a failure (`notRunBlocks`). Addresses are shown masked; the key is never written.\n */\nimport { resolveMx as dnsResolveMx } from \"node:dns/promises\";\nimport { relative } from \"node:path\";\nimport type { SiteSystemFinding } from \"../../checks.js\";\nimport { fail, notApplicable, pass } from \"../enquiry/common.js\";\nimport {\n collectBrandlabSource,\n findImportedCalls,\n objectPropertyValue,\n stringLiteralValue,\n} from \"../../../source-files.js\";\n\nconst CHECK = \"H13\" as const;\nconst SITE_CORE_CONTACT = /^@brandlab-digital\\/site-core\\/contact$/;\nconst RESEND_API = \"https://api.resend.com\";\nconst MAILBOX = /^[^\\s<>,@]+@([^\\s<>,@]+\\.[^\\s<>,@]+)$/;\nconst RESERVED_SUFFIXES = [\".invalid\", \".example\", \".test\", \".localhost\", \".local\"];\nconst RESERVED_DOMAINS = new Set([\"example.com\", \"example.net\", \"example.org\", \"localhost\"]);\nconst SANDBOX_DOMAINS = new Set([\"resend.dev\"]);\nconst LOOKUP_TIMEOUT_MS = 15_000;\n\nexport interface H13Dependencies {\n /** The shell environment (`process.env`): the inbox variable and RESEND_API_KEY. */\n readonly environment: Readonly<Record<string, string | undefined>>;\n readonly resolveMx: (domain: string) => Promise<readonly { exchange: string; priority: number }[]>;\n readonly fetch: typeof globalThis.fetch;\n}\n\nexport interface ContactDelivery {\n readonly path: string;\n readonly sender: string | null;\n readonly recipientEnv: string | null;\n}\n\n/** The first contact contract's delivery settings, read from the syntax tree; null when the project has none. */\nexport function readContactDelivery(projectRoot: string): ContactDelivery | null {\n const source = collectBrandlabSource(projectRoot);\n for (const file of source.files) {\n const [call] = findImportedCalls(file, SITE_CORE_CONTACT, \"defineContactContract\");\n if (!call) continue;\n const delivery = objectPropertyValue(call.arguments[0], \"delivery\");\n return {\n path: file.path,\n sender: stringLiteralValue(objectPropertyValue(delivery, \"sender\")),\n recipientEnv: stringLiteralValue(objectPropertyValue(delivery, \"recipientEnv\")),\n };\n }\n return null;\n}\n\n/** The address inside `Name <address>` or a bare address, lower-cased; null when it is not one. */\nfunction mailbox(value: string): { address: string; domain: string } | null {\n const trimmed = value.trim();\n const address = (trimmed.match(/<([^<>]+)>\\s*$/)?.[1] ?? trimmed).trim().toLowerCase();\n const domain = address.match(MAILBOX)?.[1];\n return domain ? { address, domain } : null;\n}\n\n/** Why an address cannot carry real mail (reserved or sandbox domain), or null when it can. */\nexport function reservedMailDomainProblem(value: string): string | null {\n const parsed = mailbox(value);\n if (!parsed) return \"is not an email address\";\n const { domain } = parsed;\n if (RESERVED_DOMAINS.has(domain) || RESERVED_SUFFIXES.some((suffix) => domain.endsWith(suffix))) {\n return `uses ${domain}, a reserved domain that never receives or sends real mail`;\n }\n if (SANDBOX_DOMAINS.has(domain)) return `uses ${domain}, Resend's sandbox domain, which only delivers to the account owner`;\n return null;\n}\n\nfunction masked(address: string): string {\n const [local = \"\", domain = \"\"] = address.split(\"@\");\n return `${local.slice(0, 1)}…@${domain}`;\n}\n\ntype Lookup<T> = { ok: true; value: T } | { ok: false; notRun: string };\n\nasync function resendGet(dependencies: H13Dependencies, apiKey: string, path: string): Promise<{ status: number; body: Record<string, unknown> } | { error: string }> {\n try {\n const response = await dependencies.fetch(`${RESEND_API}${path}`, {\n method: \"GET\",\n headers: { authorization: `Bearer ${apiKey}`, accept: \"application/json\" },\n signal: AbortSignal.timeout(LOOKUP_TIMEOUT_MS),\n });\n let body: unknown;\n try {\n body = await response.json();\n } catch {\n body = undefined;\n }\n return { status: response.status, body: body && typeof body === \"object\" && !Array.isArray(body) ? (body as Record<string, unknown>) : {} };\n } catch (error) {\n return { error: error instanceof Error ? error.name : \"error\" };\n }\n}\n\nfunction refusal(what: string, answer: { status: number; body: Record<string, unknown> } | { error: string }): string {\n if (\"error\" in answer) return `Not run: the Resend ${what} lookup failed (${answer.error}); run brandlab-ready again online.`;\n const name = typeof answer.body.name === \"string\" && /^[a-z_]{1,64}$/.test(answer.body.name) ? answer.body.name : \"\";\n if (name === \"restricted_api_key\") {\n return \"Not run: the Resend key is sending-only, and Resend's domain and suppression lookups need a full-access key; set one in RESEND_LOOKUP_API_KEY for this run only.\";\n }\n return `Not run: Resend answered ${answer.status}${name ? ` ${name}` : \"\"} to the ${what} lookup.`;\n}\n\nasync function senderDomainStatus(dependencies: H13Dependencies, apiKey: string, domain: string): Promise<Lookup<string | null>> {\n const answer = await resendGet(dependencies, apiKey, \"/domains\");\n if (\"error\" in answer || answer.status !== 200) return { ok: false, notRun: refusal(\"domains\", answer) };\n const domains = Array.isArray(answer.body.data) ? answer.body.data : [];\n for (const entry of domains) {\n const record = entry && typeof entry === \"object\" ? (entry as Record<string, unknown>) : {};\n if (typeof record.name === \"string\" && record.name.toLowerCase() === domain) {\n return { ok: true, value: typeof record.status === \"string\" ? record.status : \"unknown\" };\n }\n }\n return { ok: true, value: null };\n}\n\nasync function suppression(dependencies: H13Dependencies, apiKey: string, address: string): Promise<Lookup<{ origin: string; since: string } | null>> {\n const answer = await resendGet(dependencies, apiKey, `/suppressions/${encodeURIComponent(address)}`);\n if (!(\"error\" in answer) && answer.status === 404) return { ok: true, value: null };\n if (\"error\" in answer || answer.status !== 200) return { ok: false, notRun: refusal(\"suppression\", answer) };\n const origin = typeof answer.body.origin === \"string\" && /^[a-z_]{1,32}$/.test(answer.body.origin) ? answer.body.origin : \"unknown origin\";\n const since = typeof answer.body.created_at === \"string\" ? answer.body.created_at.slice(0, 10) : \"\";\n return { ok: true, value: { origin, since: /^\\d{4}-\\d{2}-\\d{2}$/.test(since) ? since : \"an unknown date\" } };\n}\n\nasync function mxProblem(dependencies: H13Dependencies, domain: string): Promise<Lookup<string | null>> {\n try {\n const records = await dependencies.resolveMx(domain);\n return { ok: true, value: records.some((record) => record.exchange && record.exchange !== \".\") ? null : `${domain} has no MX records` };\n } catch (error) {\n const code = (error as { code?: unknown })?.code;\n if (code === \"ENODATA\" || code === \"ENOTFOUND\" || code === \"NXDOMAIN\") return { ok: true, value: `${domain} has no MX records` };\n return { ok: false, notRun: `Not run: the MX lookup for ${domain} failed (${typeof code === \"string\" ? code : \"error\"}); run brandlab-ready again online.` };\n }\n}\n\nexport async function checkH13(projectRoot: string, dependencies: H13Dependencies): Promise<SiteSystemFinding[]> {\n const delivery = readContactDelivery(projectRoot);\n if (!delivery) return [notApplicable(CHECK, \"The project has no contact contract, so there is no business inbox to check.\")];\n const target = relative(projectRoot, `${projectRoot}/${delivery.path}`);\n const failures: SiteSystemFinding[] = [];\n const notRun: string[] = [];\n\n const senderProblem = delivery.sender === null ? \"could not be read as a literal\" : reservedMailDomainProblem(delivery.sender);\n if (senderProblem) {\n failures.push(fail(CHECK, target, `The contact contract's delivery.sender ${senderProblem}; use the client's domain verified in Resend.`));\n }\n const sender = delivery.sender === null ? null : mailbox(delivery.sender);\n\n const recipientEnv = delivery.recipientEnv;\n const rawRecipients = recipientEnv ? dependencies.environment[recipientEnv] ?? \"\" : \"\";\n const recipients = rawRecipients.split(\",\").map((value) => value.trim()).filter(Boolean);\n if (!recipientEnv) {\n failures.push(fail(CHECK, target, \"The contact contract's delivery.recipientEnv could not be read as a literal.\"));\n } else if (recipients.length === 0) {\n notRun.push(`Not run: ${recipientEnv} is not set in this shell; set it to the production business inbox (from the Vercel project's Production environment) for this run, then run brandlab-ready again.`);\n }\n\n const inboxes: { address: string; domain: string }[] = [];\n for (const recipient of recipients) {\n const problem = reservedMailDomainProblem(recipient);\n const parsed = mailbox(recipient);\n if (problem || !parsed) {\n failures.push(fail(CHECK, target, `The business inbox in ${recipientEnv} ${problem ?? \"is not an email address\"}.`));\n continue;\n }\n inboxes.push(parsed);\n }\n for (const domain of [...new Set(inboxes.map((inbox) => inbox.domain))]) {\n const mx = await mxProblem(dependencies, domain);\n if (!mx.ok) notRun.push(mx.notRun);\n else if (mx.value) failures.push(fail(CHECK, target, `The business inbox's domain ${mx.value}, so no enquiry email can reach it.`));\n }\n\n // The lookup key (full access) when set, so the sending key can stay sending-only; otherwise the sending key.\n const apiKey = dependencies.environment.RESEND_LOOKUP_API_KEY?.trim() || dependencies.environment.RESEND_API_KEY?.trim() || \"\";\n if (!apiKey) {\n notRun.push(\"Not run: neither RESEND_LOOKUP_API_KEY nor RESEND_API_KEY is set in this shell, so the Resend domain and suppression checks did not run; set a full-access key in RESEND_LOOKUP_API_KEY for this run only.\");\n } else {\n if (sender && !senderProblem) {\n const status = await senderDomainStatus(dependencies, apiKey, sender.domain);\n if (!status.ok) notRun.push(status.notRun);\n else if (status.value === null) failures.push(fail(CHECK, target, `The sender domain ${sender.domain} is not a domain in the Resend account; add and verify it in Resend (SPF and DKIM).`));\n else if (status.value !== \"verified\") failures.push(fail(CHECK, target, `The sender domain ${sender.domain} is ${status.value} in Resend, not verified; finish its DNS records in Resend.`));\n }\n for (const inbox of inboxes) {\n const suppressed = await suppression(dependencies, apiKey, inbox.address);\n if (!suppressed.ok) notRun.push(suppressed.notRun);\n else if (suppressed.value) {\n failures.push(fail(\n CHECK,\n target,\n `The business inbox ${masked(inbox.address)} is on Resend's suppression list (${suppressed.value.origin}, since ${suppressed.value.since}), so Resend drops every enquiry to it; fix the inbox, then remove it in Resend → Suppressions.`,\n ));\n }\n }\n }\n\n if (failures.length > 0) return failures;\n if (notRun.length > 0) return [...new Set(notRun)].map((message) => ({ checkId: CHECK, status: \"not-run\" as const, target, message }));\n const inboxList = inboxes.map((inbox) => masked(inbox.address)).join(\", \");\n return [pass(\n CHECK,\n `The business inbox (${inboxList}) has MX records and is not on Resend's suppression list, and the sender domain ${sender?.domain ?? \"\"} is verified in Resend.`,\n target,\n )];\n}\n\n/** The live dependencies: the shell environment, DNS and the network. */\nexport function liveH13Dependencies(): H13Dependencies {\n return {\n environment: process.env,\n resolveMx: (domain) => dnsResolveMx(domain),\n fetch: (...args) => globalThis.fetch(...args),\n };\n}\n"]}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The delivery rule module (release 3.14.0): H13, the business inbox can receive enquiries (a real sender, MX records,
|
|
3
|
+
* the sender domain verified in Resend and the inbox not suppressed). Static and network: it reads the contact contract
|
|
4
|
+
* from the source, then asks DNS and Resend with the shell's own environment.
|
|
5
|
+
*/
|
|
6
|
+
import type { SiteSystemFinding } from "../../checks.js";
|
|
7
|
+
import type { SiteSystemRenderedInput } from "../../runners.js";
|
|
8
|
+
import { type H13Dependencies } from "./h13-inbox.js";
|
|
9
|
+
export { checkH13, liveH13Dependencies, readContactDelivery, reservedMailDomainProblem, type ContactDelivery, type H13Dependencies } from "./h13-inbox.js";
|
|
10
|
+
/** Runs H13 against the project; tests pass their own DNS, fetch and environment. */
|
|
11
|
+
export declare function runDeliveryRules(input: Pick<SiteSystemRenderedInput, "projectRoot">, dependencies?: H13Dependencies): Promise<readonly SiteSystemFinding[]>;
|
|
12
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/site-system/rules/delivery/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAChE,OAAO,EAAiC,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAErF,OAAO,EAAE,QAAQ,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,yBAAyB,EAAE,KAAK,eAAe,EAAE,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAE3J,qFAAqF;AACrF,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,IAAI,CAAC,uBAAuB,EAAE,aAAa,CAAC,EACnD,YAAY,GAAE,eAAuC,GACpD,OAAO,CAAC,SAAS,iBAAiB,EAAE,CAAC,CAEvC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { checkH13, liveH13Dependencies } from "./h13-inbox.js";
|
|
2
|
+
export { checkH13, liveH13Dependencies, readContactDelivery, reservedMailDomainProblem } from "./h13-inbox.js";
|
|
3
|
+
/** Runs H13 against the project; tests pass their own DNS, fetch and environment. */
|
|
4
|
+
export async function runDeliveryRules(input, dependencies = liveH13Dependencies()) {
|
|
5
|
+
return checkH13(input.projectRoot, dependencies);
|
|
6
|
+
}
|
|
7
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/site-system/rules/delivery/index.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,QAAQ,EAAE,mBAAmB,EAAwB,MAAM,gBAAgB,CAAC;AAErF,OAAO,EAAE,QAAQ,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,yBAAyB,EAA8C,MAAM,gBAAgB,CAAC;AAE3J,qFAAqF;AACrF,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,KAAmD,EACnD,eAAgC,mBAAmB,EAAE;IAErD,OAAO,QAAQ,CAAC,KAAK,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC;AACnD,CAAC","sourcesContent":["/**\n * The delivery rule module (release 3.14.0): H13, the business inbox can receive enquiries (a real sender, MX records,\n * the sender domain verified in Resend and the inbox not suppressed). Static and network: it reads the contact contract\n * from the source, then asks DNS and Resend with the shell's own environment.\n */\nimport type { SiteSystemFinding } from \"../../checks.js\";\nimport type { SiteSystemRenderedInput } from \"../../runners.js\";\nimport { checkH13, liveH13Dependencies, type H13Dependencies } from \"./h13-inbox.js\";\n\nexport { checkH13, liveH13Dependencies, readContactDelivery, reservedMailDomainProblem, type ContactDelivery, type H13Dependencies } from \"./h13-inbox.js\";\n\n/** Runs H13 against the project; tests pass their own DNS, fetch and environment. */\nexport async function runDeliveryRules(\n input: Pick<SiteSystemRenderedInput, \"projectRoot\">,\n dependencies: H13Dependencies = liveH13Dependencies(),\n): Promise<readonly SiteSystemFinding[]> {\n return checkH13(input.projectRoot, dependencies);\n}\n"]}
|