@brandlab-digital/conformance 3.18.0 → 3.19.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.
@@ -23,19 +23,19 @@ export const BRANDLAB_CURRENT_RUNTIME_RELEASE = Object.freeze({
23
23
  "kind": "brandlab-runtime-release-contract",
24
24
  "packages": {
25
25
  "@brandlab-digital/conformance": {
26
- "version": "3.18.0"
26
+ "version": "3.19.0"
27
27
  },
28
28
  "@brandlab-digital/eslint-plugin": {
29
- "version": "3.18.0"
29
+ "version": "3.19.0"
30
30
  },
31
31
  "@brandlab-digital/site-core": {
32
- "version": "3.18.0"
32
+ "version": "3.19.0"
33
33
  },
34
34
  "@brandlab-digital/site-next": {
35
- "version": "3.18.0"
35
+ "version": "3.19.0"
36
36
  }
37
37
  },
38
- "releaseVersion": "3.18.0",
38
+ "releaseVersion": "3.19.0",
39
39
  "runtime": {
40
40
  "bundler": "turbopack",
41
41
  "cacheComponents": false,
@@ -1 +1 @@
1
- {"version":3,"file":"current-release.js","sourceRoot":"","sources":["../src/current-release.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,wEAAwE;AACxE,OAAO,EAEL,sCAAsC,GACvC,MAAM,uBAAuB,CAAC;AAE/B,MAAM,CAAC,MAAM,gCAAgC,GAAG,MAAM,CAAC,MAAM,CAC7D;IACE,cAAc,EAAE;QACd,uCAAuC;QACvC,8BAA8B;QAC9B,yBAAyB;QACzB,6BAA6B;QAC7B,0BAA0B;QAC1B,4BAA4B;QAC5B,kCAAkC;QAClC,wCAAwC;QACxC,yBAAyB;KAC1B;IACD,WAAW,EAAE;QACX,WAAW,EAAE,cAAc;QAC3B,cAAc,EAAE,iBAAiB;QACjC,eAAe,EAAE,mBAAmB;QACpC,SAAS,EAAE,YAAY;QACvB,KAAK,EAAE,QAAQ;KAChB;IACD,MAAM,EAAE,mCAAmC;IAC3C,UAAU,EAAE;QACV,+BAA+B,EAAE;YAC/B,SAAS,EAAE,QAAQ;SACpB;QACD,iCAAiC,EAAE;YACjC,SAAS,EAAE,QAAQ;SACpB;QACD,6BAA6B,EAAE;YAC7B,SAAS,EAAE,QAAQ;SACpB;QACD,6BAA6B,EAAE;YAC7B,SAAS,EAAE,QAAQ;SACpB;KACF;IACD,gBAAgB,EAAE,QAAQ;IAC1B,SAAS,EAAE;QACT,SAAS,EAAE,WAAW;QACtB,iBAAiB,EAAE,KAAK;QACxB,MAAM,EAAE,QAAQ;QAChB,mBAAmB,EAAE,OAAO;QAC5B,MAAM,EAAE,MAAM;QACd,gBAAgB,EAAE,aAAa;QAC/B,OAAO,EAAE,QAAQ;QACjB,UAAU,EAAE,QAAQ;QACpB,QAAQ,EAAE,KAAK;QACf,OAAO,EAAE,QAAQ;QACjB,YAAY,EAAE,OAAO;KACtB;IACD,eAAe,EAAE,sCAAsC;CACxD,CAC6C,CAAC;AAE/C,sCAAsC,CAAC,gCAAgC,CAAC,CAAC","sourcesContent":["// Generated by standards/scripts/generate-conformance-current-release.mjs.\n// Each exact package release verifies its own current runtime contract.\nimport {\n type BrandlabRuntimeReleaseContract,\n validateBrandlabRuntimeReleaseContract,\n} from \"./release-manifest.js\";\n\nexport const BRANDLAB_CURRENT_RUNTIME_RELEASE = Object.freeze(\n{\n \"capabilities\": [\n \"typed-project-business-and-route-data\",\n \"essential-search-foundations\",\n \"shared-contact-delivery\",\n \"privacy-safe-lead-analytics\",\n \"security-patched-runtime\",\n \"live-google-review-summary\",\n \"high-signal-project-verification\",\n \"packed-positive-and-negative-consumers\",\n \"changesets-oidc-release\"\n ],\n \"contracts\": {\n \"analytics\": \"analytics-v4\",\n \"architecture\": \"architecture-v7\",\n \"configuration\": \"project-config-v7\",\n \"contact\": \"contact-v4\",\n \"seo\": \"seo-v4\"\n },\n \"kind\": \"brandlab-runtime-release-contract\",\n \"packages\": {\n \"@brandlab-digital/conformance\": {\n \"version\": \"3.18.0\"\n },\n \"@brandlab-digital/eslint-plugin\": {\n \"version\": \"3.18.0\"\n },\n \"@brandlab-digital/site-core\": {\n \"version\": \"3.18.0\"\n },\n \"@brandlab-digital/site-next\": {\n \"version\": \"3.18.0\"\n }\n },\n \"releaseVersion\": \"3.18.0\",\n \"runtime\": {\n \"bundler\": \"turbopack\",\n \"cacheComponents\": false,\n \"next\": \"16.4.2\",\n \"nextVersionPolicy\": \"exact\",\n \"node\": \"24.x\",\n \"packageManager\": \"npm@11.14.1\",\n \"react\": \"19.2.8\",\n \"reactDom\": \"19.2.8\",\n \"router\": \"app\",\n \"sharp\": \"0.35.5\",\n \"typescript\": \"6.0.3\"\n },\n \"schemaVersion\": \"brandlab.runtime-release-contract.v1\"\n},\n) as unknown as BrandlabRuntimeReleaseContract;\n\nvalidateBrandlabRuntimeReleaseContract(BRANDLAB_CURRENT_RUNTIME_RELEASE);\n"]}
1
+ {"version":3,"file":"current-release.js","sourceRoot":"","sources":["../src/current-release.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,wEAAwE;AACxE,OAAO,EAEL,sCAAsC,GACvC,MAAM,uBAAuB,CAAC;AAE/B,MAAM,CAAC,MAAM,gCAAgC,GAAG,MAAM,CAAC,MAAM,CAC7D;IACE,cAAc,EAAE;QACd,uCAAuC;QACvC,8BAA8B;QAC9B,yBAAyB;QACzB,6BAA6B;QAC7B,0BAA0B;QAC1B,4BAA4B;QAC5B,kCAAkC;QAClC,wCAAwC;QACxC,yBAAyB;KAC1B;IACD,WAAW,EAAE;QACX,WAAW,EAAE,cAAc;QAC3B,cAAc,EAAE,iBAAiB;QACjC,eAAe,EAAE,mBAAmB;QACpC,SAAS,EAAE,YAAY;QACvB,KAAK,EAAE,QAAQ;KAChB;IACD,MAAM,EAAE,mCAAmC;IAC3C,UAAU,EAAE;QACV,+BAA+B,EAAE;YAC/B,SAAS,EAAE,QAAQ;SACpB;QACD,iCAAiC,EAAE;YACjC,SAAS,EAAE,QAAQ;SACpB;QACD,6BAA6B,EAAE;YAC7B,SAAS,EAAE,QAAQ;SACpB;QACD,6BAA6B,EAAE;YAC7B,SAAS,EAAE,QAAQ;SACpB;KACF;IACD,gBAAgB,EAAE,QAAQ;IAC1B,SAAS,EAAE;QACT,SAAS,EAAE,WAAW;QACtB,iBAAiB,EAAE,KAAK;QACxB,MAAM,EAAE,QAAQ;QAChB,mBAAmB,EAAE,OAAO;QAC5B,MAAM,EAAE,MAAM;QACd,gBAAgB,EAAE,aAAa;QAC/B,OAAO,EAAE,QAAQ;QACjB,UAAU,EAAE,QAAQ;QACpB,QAAQ,EAAE,KAAK;QACf,OAAO,EAAE,QAAQ;QACjB,YAAY,EAAE,OAAO;KACtB;IACD,eAAe,EAAE,sCAAsC;CACxD,CAC6C,CAAC;AAE/C,sCAAsC,CAAC,gCAAgC,CAAC,CAAC","sourcesContent":["// Generated by standards/scripts/generate-conformance-current-release.mjs.\n// Each exact package release verifies its own current runtime contract.\nimport {\n type BrandlabRuntimeReleaseContract,\n validateBrandlabRuntimeReleaseContract,\n} from \"./release-manifest.js\";\n\nexport const BRANDLAB_CURRENT_RUNTIME_RELEASE = Object.freeze(\n{\n \"capabilities\": [\n \"typed-project-business-and-route-data\",\n \"essential-search-foundations\",\n \"shared-contact-delivery\",\n \"privacy-safe-lead-analytics\",\n \"security-patched-runtime\",\n \"live-google-review-summary\",\n \"high-signal-project-verification\",\n \"packed-positive-and-negative-consumers\",\n \"changesets-oidc-release\"\n ],\n \"contracts\": {\n \"analytics\": \"analytics-v4\",\n \"architecture\": \"architecture-v7\",\n \"configuration\": \"project-config-v7\",\n \"contact\": \"contact-v4\",\n \"seo\": \"seo-v4\"\n },\n \"kind\": \"brandlab-runtime-release-contract\",\n \"packages\": {\n \"@brandlab-digital/conformance\": {\n \"version\": \"3.19.0\"\n },\n \"@brandlab-digital/eslint-plugin\": {\n \"version\": \"3.19.0\"\n },\n \"@brandlab-digital/site-core\": {\n \"version\": \"3.19.0\"\n },\n \"@brandlab-digital/site-next\": {\n \"version\": \"3.19.0\"\n }\n },\n \"releaseVersion\": \"3.19.0\",\n \"runtime\": {\n \"bundler\": \"turbopack\",\n \"cacheComponents\": false,\n \"next\": \"16.4.2\",\n \"nextVersionPolicy\": \"exact\",\n \"node\": \"24.x\",\n \"packageManager\": \"npm@11.14.1\",\n \"react\": \"19.2.8\",\n \"reactDom\": \"19.2.8\",\n \"router\": \"app\",\n \"sharp\": \"0.35.5\",\n \"typescript\": \"6.0.3\"\n },\n \"schemaVersion\": \"brandlab.runtime-release-contract.v1\"\n},\n) as unknown as BrandlabRuntimeReleaseContract;\n\nvalidateBrandlabRuntimeReleaseContract(BRANDLAB_CURRENT_RUNTIME_RELEASE);\n"]}
@@ -326,6 +326,12 @@ export declare const SITE_SYSTEM_CHECKS: readonly [{
326
326
  readonly from: "3.14.0";
327
327
  readonly kind: "rendered";
328
328
  readonly summary: "Trade lines inside sections (matrix part B): for trees, the council permit note in LocalFacts on suburb and service-in-suburb pages and the qualifications and insurance lines on the about page, or a data-backed reason they are hidden.";
329
+ }, {
330
+ readonly id: "K14";
331
+ readonly tier: "shell";
332
+ readonly from: "3.19.0";
333
+ readonly kind: "journey";
334
+ readonly summary: "Neighbouring blocks never share a background: at 1440 px the painted ground of each top-level block in main, the CTA band and the footer differs from its neighbour's on every sampled and family page; a [data-bl-inset] panel belongs to its place.";
329
335
  }, {
330
336
  readonly id: "K13";
331
337
  readonly tier: "full";
@@ -1 +1 @@
1
- {"version":3,"file":"checks.d.ts","sourceRoot":"","sources":["../../src/site-system/checks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,eAAO,MAAM,iBAAiB,uCAAwC,CAAC;AACvE,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEhE,kFAAkF;AAClF,MAAM,MAAM,oBAAoB,GAAG,cAAc,GAAG,WAAW,CAAC;AAEhE,MAAM,MAAM,mBAAmB,GAAG,SAAS,GAAG,UAAU,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAE/E,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,oBAAoB,CAAC;IACpC,+DAA+D;IAC/D,QAAQ,CAAC,IAAI,EAAE,KAAK,MAAM,IAAI,CAAC;IAC/B,QAAQ,CAAC,IAAI,EAAE,mBAAmB,CAAC;IACnC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,QAAQ,CAAC;CAClC;AAED,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA0EgB,CAAC;AAEhD,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC;AAI1E,8DAA8D;AAC9D,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAG7D;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,IAAI,CAAC,eAAe,EAAE,IAAI,GAAG,MAAM,GAAG,MAAM,CAAC,EACpD,IAAI,EAAE;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,GAClE,OAAO,CAIT;AAED,oGAAoG;AACpG,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,OAAO,EAAE,iBAAiB,CAAC;IACpC,4GAA4G;IAC5G,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,GAAG,gBAAgB,CAAC;IAChE,uEAAuE;IACvE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,kEAAkE;IAClE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,4FAA4F;IAC5F,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,CAAC;CAC/B"}
1
+ {"version":3,"file":"checks.d.ts","sourceRoot":"","sources":["../../src/site-system/checks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,eAAO,MAAM,iBAAiB,uCAAwC,CAAC;AACvE,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEhE,kFAAkF;AAClF,MAAM,MAAM,oBAAoB,GAAG,cAAc,GAAG,WAAW,CAAC;AAEhE,MAAM,MAAM,mBAAmB,GAAG,SAAS,GAAG,UAAU,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAE/E,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,oBAAoB,CAAC;IACpC,+DAA+D;IAC/D,QAAQ,CAAC,IAAI,EAAE,KAAK,MAAM,IAAI,CAAC;IAC/B,QAAQ,CAAC,IAAI,EAAE,mBAAmB,CAAC;IACnC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,QAAQ,CAAC;CAClC;AAED,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA2EgB,CAAC;AAEhD,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC;AAI1E,8DAA8D;AAC9D,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAG7D;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,IAAI,CAAC,eAAe,EAAE,IAAI,GAAG,MAAM,GAAG,MAAM,CAAC,EACpD,IAAI,EAAE;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,GAClE,OAAO,CAIT;AAED,oGAAoG;AACpG,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,OAAO,EAAE,iBAAiB,CAAC;IACpC,4GAA4G;IAC5G,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,GAAG,gBAAgB,CAAC;IAChE,uEAAuE;IACvE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,kEAAkE;IAClE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,4FAA4F;IAC5F,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,CAAC;CAC/B"}
@@ -67,6 +67,7 @@ export const SITE_SYSTEM_CHECKS = [
67
67
  // full: page families, location estates, section manifests, links and the data contract
68
68
  { id: "K12", tier: "full", from: "3.12.0", kind: "rendered", summary: "Section manifest: every required section rendered or skipped with a data-backed reason; conditional sections render when their data exists; canonical or recorded order." },
69
69
  { id: "K12-slots", tier: "full", from: "3.14.0", kind: "rendered", summary: "Trade lines inside sections (matrix part B): for trees, the council permit note in LocalFacts on suburb and service-in-suburb pages and the qualifications and insurance lines on the about page, or a data-backed reason they are hidden." },
70
+ { id: "K14", tier: "shell", from: "3.19.0", kind: "journey", summary: "Neighbouring blocks never share a background: at 1440 px the painted ground of each top-level block in main, the CTA band and the footer differs from its neighbour's on every sampled and family page; a [data-bl-inset] panel belongs to its place." },
70
71
  { id: "K13", tier: "full", from: "3.16.0", kind: "rendered", summary: "Signature sections: a name, at most two on the home page and one on a service page, none elsewhere, as the manifest lists them; the home page's are the ones strategy.ts records for D4." },
71
72
  { id: "E5", tier: "full", from: "3.12.0", kind: "static", summary: "No orphan indexable pages, from the route catalogue and link data." },
72
73
  { id: "E10", tier: "full", from: "3.12.0", kind: "static", summary: "Phrase-owner uniqueness in strategy data; no service-in-suburb page for the suburb page phrase; no service-region hub for the main trade." },
@@ -1 +1 @@
1
- {"version":3,"file":"checks.js","sourceRoot":"","sources":["../../src/site-system/checks.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The Site System v2 ready gate: every check, the adoption tier it blocks at and the release that brings it.\n *\n * A site declares `siteSystem: { tier }` in brandlab.config.ts. Checks of that tier and the tiers below it block;\n * checks of higher tiers report without blocking. A check blocks only from the release named here, so new checks\n * arrive with version bumps and never turn the fleet red at once. Holdouts use the project's Romie-owned\n * `exceptions` record. D6 (leave reviews as they are) removes every review-exclusion clause; D7 (no call tracking)\n * means no number-swap check.\n *\n * `kind` says where a check runs:\n * - `journey`: the Playwright site-system journeys (`defineBrandlabSiteSystemTests`), in the site's e2e run;\n * - `rendered`: rules over the built site's server HTML and assets (`brandlab-ready` reads them);\n * - `static`: source and data rules (`brandlab-verify` and `brandlab-ready`);\n * - `record`: launch-day and human steps recorded with `brandlab-ready record <id> --evidence <ref>`.\n */\n\nexport const SITE_SYSTEM_TIERS = [\"shell\", \"enquiry\", \"full\"] as const;\nexport type SiteSystemTier = (typeof SITE_SYSTEM_TIERS)[number];\n\n/** The design check D4 blocks every new build and redesign, whatever its tier. */\nexport type SiteSystemCheckScope = SiteSystemTier | \"new-build\";\n\nexport type SiteSystemCheckKind = \"journey\" | \"rendered\" | \"static\" | \"record\";\n\nexport interface SiteSystemCheck {\n readonly id: string;\n readonly tier: SiteSystemCheckScope;\n /** The first release in which the check blocks at its tier. */\n readonly from: `3.${number}.0`;\n readonly kind: SiteSystemCheckKind;\n readonly summary: string;\n /**\n * `launch`: a not-run result blocks only under `brandlab-ready --launch`, where it counts as a failure. For checks\n * that need production values in the shell (H13), so a CI run reports them as not run without failing the tier.\n */\n readonly notRunBlocks?: \"launch\";\n}\n\nexport const SITE_SYSTEM_CHECKS = [\n // shell: header, nav, call bar, placements, crawlable links, contrast and targets, call links, budgets, legal\n { id: \"N1\", tier: \"shell\", from: \"3.9.0\", kind: \"rendered\", summary: \"Required nav links, About in the first two, services before Areas, Contact last, labels from the closed list, recorded orders kept.\" },\n { id: \"N2\", tier: \"shell\", from: \"3.8.0\", kind: \"rendered\", summary: \"Every dropdown link and hub link is an <a href> in the server HTML.\" },\n { id: \"N3\", tier: \"shell\", from: \"3.8.0\", kind: \"journey\", summary: \"Hover opens after a short delay and a diagonal path stays open; mouse-out closes; nothing sticks open.\" },\n { id: \"N4\", tier: \"shell\", from: \"3.8.0\", kind: \"journey\", summary: \"A chevron click after hover keeps the panel open; a link click closes it; click, Enter and outside click work.\" },\n { id: \"N5\", tier: \"shell\", from: \"3.8.0\", kind: \"journey\", summary: \"Keyboard and ARIA: aria-expanded follows the panel, Arrow keys move, Escape closes and returns focus; axe passes open and closed.\" },\n { id: \"N6\", tier: \"shell\", from: \"3.8.0\", kind: \"journey\", summary: \"The header stays at the top with the phone and quote visible at every desktop width; the compact band (1024 to 1199 px) shows them too.\" },\n { id: \"N8\", tier: \"shell\", from: \"3.9.0\", kind: \"journey\", summary: \"The drawer is a modal dialog that traps focus and resets; the call bar uses placement fab; back-to-top appears (from 3.11.0).\" },\n { id: \"K1\", tier: \"shell\", from: \"3.9.0\", kind: \"rendered\", summary: \"Shared components in use for the parts the tier covers (data-bl present); no literal tel: outside PhoneLink.\" },\n { id: \"K3\", tier: \"shell\", from: \"3.8.0\", kind: \"journey\", summary: \"Text contrast 4.5:1 and non-text 3:1 in the browser.\" },\n { id: \"K4\", tier: \"shell\", from: \"3.8.0\", kind: \"journey\", summary: \"Targets at least 24 px, and 44 px for primary actions (call, quote, menu).\" },\n { id: \"K5\", tier: \"shell\", from: \"3.8.0\", kind: \"journey\", summary: \"Focus is never fully hidden by the sticky header or call bar at 390 px, in Chromium and WebKit.\" },\n { id: \"K6\", tier: \"shell\", from: \"3.8.0\", kind: \"journey\", summary: \"Phone and quote in the same relative order on every page, in the header, hero and call bar.\" },\n { id: \"K7\", tier: \"shell\", from: \"3.8.0\", kind: \"rendered\", summary: \"Dropdown links, accordion and tab panels and full review text are in the server HTML.\" },\n { id: \"K10\", tier: \"shell\", from: \"3.8.0\", kind: \"record\", summary: \"Adoption parity in the lenient harness: no section, link or text lost against the baseline.\" },\n { id: \"C4\", tier: \"shell\", from: \"3.8.0\", kind: \"journey\", summary: \"Phone taps report an allowed placement (header, footer, hero, cta, fab, contact, location, page, emergency).\" },\n { id: \"C5\", tier: \"shell\", from: \"3.8.0\", kind: \"rendered\", summary: \"Call-to-action labels come from the closed list or a recorded label.\" },\n { id: \"C9\", tier: \"shell\", from: \"3.8.0\", kind: \"rendered\", summary: \"No interstitial overlay in the first screen.\" },\n { id: \"C10\", tier: \"shell\", from: \"3.8.0\", kind: \"rendered\", summary: \"Every tel: href is E.164 (national digits for 13, 1300 and 1800) and matches the evidence-file phone.\" },\n { id: \"E1\", tier: \"shell\", from: \"3.8.0\", kind: \"static\", summary: \"Title patterns on new or changed URLs; recorded titles unchanged.\" },\n { id: \"E2\", tier: \"shell\", from: \"3.8.0\", kind: \"rendered\", summary: \"Exactly one H1 on every page, rendered once.\" },\n { id: \"E8\", tier: \"shell\", from: \"3.8.0\", kind: \"rendered\", summary: \"No banned phrase from any of the three layers, environmental claims included.\" },\n { id: \"T3\", tier: \"shell\", from: \"3.8.0\", kind: \"rendered\", summary: \"A licence line renders wherever licensed work is advertised, with the licensee where it differs.\" },\n { id: \"T4\", tier: \"shell\", from: \"3.8.0\", kind: \"rendered\", summary: \"No hard-coded rating or review numbers; proof claims resolve to renderable evidence.\" },\n { id: \"T11\", tier: \"shell\", from: \"3.8.0\", kind: \"static\", summary: \"No page for work over $5,000 outside the licence classes held; licence conditions carried.\" },\n { id: \"X1\", tier: \"shell\", from: \"3.8.0\", kind: \"record\", summary: \"Legacy URLs answer 200 or one redirect hop; earning-URL depth, titles and dollar figures kept.\" },\n { id: \"B1\", tier: \"shell\", from: \"3.8.0\", kind: \"rendered\", summary: \"Lab budgets: JavaScript, third-party requests, hero image and HTML within budget; no unrecorded chat widget, pixel, heatmap or tag manager.\" },\n { id: \"L10-hero\", tier: \"shell\", from: \"3.8.0\", kind: \"rendered\", summary: 'No kilometre figures, \"km from\" or \"based in\" in hero copy.' },\n { id: \"W2\", tier: \"shell\", from: \"3.13.0\", kind: \"journey\", summary: \"Prefetch by link role at 390 x 844 and 1440 x 900: at most 8 pages on landing and 12 after a full scroll; none from footer, area, nearby, map or finder links, hidden links or the current page; prefetch bytes above twice the page's HTML warn.\" },\n { id: \"W5\", tier: \"shell\", from: \"3.13.0\", kind: \"static\", summary: \"vercel.json regions equal hosting.originRegion (syd1 by default); any other region has an origin-region-v1 decision whose reviewBy has not passed.\" },\n { id: \"W10\", tier: \"shell\", from: \"3.13.0\", kind: \"rendered\", summary: \"robots.txt and the recorded firewall rules leave every search and answer crawler open; no Crawl-delay for them.\" },\n\n // enquiry: the form, thank-you page, reviews, proof strip, footer, CTA band, contact page, prices, media, density\n { id: \"C1\", tier: \"enquiry\", from: \"3.8.0\", kind: \"journey\", summary: \"Money pages: the form in the first section; at 390 x 844 the H1, a call and a quote action in the first viewport; at 1440 x 900 the form.\" },\n { id: \"C2\", tier: \"enquiry\", from: \"3.8.0\", kind: \"rendered\", summary: 'Form layout and labels; phone and email required; no helper or reassurance lines, no \"(required)\", no job select.' },\n { id: \"E9\", tier: \"enquiry\", from: \"3.8.0\", kind: \"rendered\", summary: \"Every cost guide carries at least 8 dollar figures in a renderable price state.\" },\n { id: \"T5\", tier: \"enquiry\", from: \"3.8.0\", kind: \"rendered\", summary: \"Reviews from the feed, with Google's rating, count and link beside any selection; none on the contact page; no empty reviews section.\" },\n { id: \"K8\", tier: \"enquiry\", from: \"3.8.0\", kind: \"rendered\", summary: \"Section contract: no empty section, one H2 each, at most one band tone, neighbours differ, proof within the first five sections.\" },\n { id: \"C7\", tier: \"enquiry\", from: \"3.10.0\", kind: \"rendered\", summary: \"Thank-you page: a reply time, a tel: link and noindex, follow.\" },\n { id: \"C8\", tier: \"enquiry\", from: \"3.10.0\", kind: \"rendered\", summary: \"Forgiving form: tolerant phone input (13, 1300 and 1800 too), autocomplete tokens, no capture on the photo input.\" },\n { id: \"C11\", tier: \"enquiry\", from: \"3.10.0\", kind: \"static\", summary: \"The test marker is stripped before delivery and reporting; /api/contact is rate-limited.\" },\n { id: \"N9\", tier: \"enquiry\", from: \"3.11.0\", kind: \"rendered\", summary: 'Footer content: column 1 brand, licences, socials and contact; services, areas and company columns; © name, ABN and legal links; no CTA, \"based in\", hours or guides column.' },\n { id: \"K2\", tier: \"enquiry\", from: \"3.11.0\", kind: \"rendered\", summary: \"Every rendered shared component has a skin entry; no reference-skin class ships.\" },\n { id: \"K11\", tier: \"enquiry\", from: \"3.11.0\", kind: \"rendered\", summary: \"Density: no section with more than one H2, more than 8 cards or more than 2 calls to action; measure and section padding within limits.\" },\n { id: \"K12-contact\", tier: \"enquiry\", from: \"3.11.0\", kind: \"rendered\", summary: \"The contact page is the page header, then the contact block, and nothing else.\" },\n { id: \"T6\", tier: \"enquiry\", from: \"3.11.0\", kind: \"static\", summary: \"Every image has a media.ts entry with provenance.\" },\n { id: \"T7\", tier: \"enquiry\", from: \"3.11.0\", kind: \"rendered\", summary: \"No image is the main image of two pages.\" },\n { id: \"T8\", tier: \"enquiry\", from: \"3.11.0\", kind: \"rendered\", summary: 'Prices: GST flag set; no \"+ GST\" or \"from $X\" teasers on cards and menus; one band per job site-wide.' },\n { id: \"T9\", tier: \"enquiry\", from: \"3.11.0\", kind: \"static\", summary: \"No image in /public carries location metadata.\" },\n { id: \"H13\", tier: \"enquiry\", from: \"3.14.0\", kind: \"static\", notRunBlocks: \"launch\", summary: \"The business inbox can receive enquiries: a real sender (no .invalid, example or sandbox domain), MX records for the inbox, the sender domain verified in Resend and the inbox not on Resend's suppression list (not run without the inbox variable and a full-access Resend key in the shell, which blocks only at launch).\" },\n\n // full: page families, location estates, section manifests, links and the data contract\n { id: \"K12\", tier: \"full\", from: \"3.12.0\", kind: \"rendered\", summary: \"Section manifest: every required section rendered or skipped with a data-backed reason; conditional sections render when their data exists; canonical or recorded order.\" },\n { id: \"K12-slots\", tier: \"full\", from: \"3.14.0\", kind: \"rendered\", summary: \"Trade lines inside sections (matrix part B): for trees, the council permit note in LocalFacts on suburb and service-in-suburb pages and the qualifications and insurance lines on the about page, or a data-backed reason they are hidden.\" },\n { id: \"K13\", tier: \"full\", from: \"3.16.0\", kind: \"rendered\", summary: \"Signature sections: a name, at most two on the home page and one on a service page, none elsewhere, as the manifest lists them; the home page's are the ones strategy.ts records for D4.\" },\n { id: \"E5\", tier: \"full\", from: \"3.12.0\", kind: \"static\", summary: \"No orphan indexable pages, from the route catalogue and link data.\" },\n { id: \"E10\", tier: \"full\", from: \"3.12.0\", kind: \"static\", summary: \"Phrase-owner uniqueness in strategy data; no service-in-suburb page for the suburb page phrase; no service-region hub for the main trade.\" },\n { id: \"L3\", tier: \"full\", from: \"3.12.0\", kind: \"static\", summary: \"Neighbours by real distance.\" },\n { id: \"L4\", tier: \"full\", from: \"3.12.0\", kind: \"rendered\", summary: \"No copy rotated by page address, no name-swapped FAQs, no other suburb's job without the nearby label.\" },\n { id: \"L6\", tier: \"full\", from: \"3.12.0\", kind: \"static\", summary: \"Location pages have 6 or more inbound links from 3 or more page types.\" },\n { id: \"L7\", tier: \"full\", from: \"3.12.0\", kind: \"static\", summary: \"Location pages are 3 clicks or fewer from home.\" },\n { id: \"L8\", tier: \"full\", from: \"3.12.0\", kind: \"static\", summary: \"A hub per family per region.\" },\n { id: \"L9\", tier: \"full\", from: \"3.12.0\", kind: \"rendered\", summary: \"Data contract on service-in-suburb pages: typical prices or the cost-guide link, reviews or their fallback, the quote path (form with the suburb prefilled, phone, call bar); the nearby job card or none.\" },\n { id: \"L10\", tier: \"full\", from: \"3.12.0\", kind: \"rendered\", summary: 'No kilometre figures, \"km from\", \"based in\" or year stamps as local detail in location copy.' },\n { id: \"S4\", tier: \"full\", from: \"3.12.0\", kind: \"static\", summary: \"No hand-written per-service or per-suburb files; no dynamic route with zero pages.\" },\n { id: \"W9\", tier: \"full\", from: \"3.13.0\", kind: \"journey\", summary: \"URL variants: an unknown slug in each family answers the shared 404, the uppercase slug the same 404 or one permanent redirect to its lowercase form; a tracking query serves the same cached page as the clean URL.\" },\n { id: \"W9-params\", tier: \"full\", from: \"3.13.0\", kind: \"static\", summary: \"Pages under a dynamic route never read searchParams, so a query string cannot make an estate page dynamic.\" },\n { id: \"LOC-INDEX\", tier: \"full\", from: \"3.12.0\", kind: \"static\", summary: \"No location route is noindex or left out of the sitemap unless the recorded wave schedule dates it (Romie's 20 Oct decision: every suburb page stays indexed).\" },\n { id: \"E1-pattern\", tier: \"full\", from: \"3.14.0\", kind: \"rendered\", summary: 'Location pages outside the recorded titles have the H1 \"{Service} {Place}\" (no \"in\", no \"the\") and a title that starts with it, at most 60 characters.' },\n { id: \"W3\", tier: \"full\", from: \"3.13.0\", kind: \"static\", summary: \"No estate family (suburb, service-in-suburb, region, council) refreshes more often than every 7 days unless siteSystem.refreshExceptions records the reason and a decision id.\" },\n\n // new builds and redesigns\n { id: \"D4\", tier: \"new-build\", from: \"3.8.0\", kind: \"static\", summary: \"Same-trade distinctness: at least four of seven look levers differ from every listed competitor (advisory in 3.8.0, blocking from 3.9.0).\" },\n] as const satisfies readonly SiteSystemCheck[];\n\nexport type SiteSystemCheckId = (typeof SITE_SYSTEM_CHECKS)[number][\"id\"];\n\nconst TIER_RANK: Record<SiteSystemTier, number> = { shell: 0, enquiry: 1, full: 2 };\n\n/** A release as one comparable number: \"3.8.0\" < \"3.10.0\". */\nexport function siteSystemReleaseRank(version: string): number {\n const [major = 0, minor = 0, patch = 0] = version.split(/[.-]/).map((part) => Number.parseInt(part, 10) || 0);\n return major * 1_000_000 + minor * 1_000 + patch;\n}\n\n/**\n * Whether a check blocks for a site at `tier` on `release`. Checks of higher tiers, and checks newer than the\n * installed release, report without blocking. D4 blocks new builds and redesigns from 3.9.0 whatever their tier.\n */\nexport function siteSystemCheckBlocks(\n check: Pick<SiteSystemCheck, \"id\" | \"tier\" | \"from\">,\n site: { tier: SiteSystemTier; release: string; newBuild?: boolean },\n): boolean {\n if (check.tier === \"new-build\") return site.newBuild === true && siteSystemReleaseRank(site.release) >= siteSystemReleaseRank(\"3.9.0\");\n if (siteSystemReleaseRank(site.release) < siteSystemReleaseRank(check.from)) return false;\n return TIER_RANK[check.tier] <= TIER_RANK[site.tier];\n}\n\n/** One result of one check, from any runner. `brandlab-ready` merges them into the ready report. */\nexport interface SiteSystemFinding {\n readonly checkId: SiteSystemCheckId;\n /** `not-run`: the rule could not judge the check here (a missing key or input); it names what to supply. */\n readonly status: \"pass\" | \"fail\" | \"not-run\" | \"not-applicable\";\n /** The page path or file the result is about, when it is about one. */\n readonly target?: string;\n /** One plain sentence: what passed or what is wrong and where. */\n readonly message: string;\n /** A warning never blocks: the check still passes and the report shows \"pass (warning)\". */\n readonly severity?: \"warning\";\n}\n"]}
1
+ {"version":3,"file":"checks.js","sourceRoot":"","sources":["../../src/site-system/checks.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The Site System v2 ready gate: every check, the adoption tier it blocks at and the release that brings it.\n *\n * A site declares `siteSystem: { tier }` in brandlab.config.ts. Checks of that tier and the tiers below it block;\n * checks of higher tiers report without blocking. A check blocks only from the release named here, so new checks\n * arrive with version bumps and never turn the fleet red at once. Holdouts use the project's Romie-owned\n * `exceptions` record. D6 (leave reviews as they are) removes every review-exclusion clause; D7 (no call tracking)\n * means no number-swap check.\n *\n * `kind` says where a check runs:\n * - `journey`: the Playwright site-system journeys (`defineBrandlabSiteSystemTests`), in the site's e2e run;\n * - `rendered`: rules over the built site's server HTML and assets (`brandlab-ready` reads them);\n * - `static`: source and data rules (`brandlab-verify` and `brandlab-ready`);\n * - `record`: launch-day and human steps recorded with `brandlab-ready record <id> --evidence <ref>`.\n */\n\nexport const SITE_SYSTEM_TIERS = [\"shell\", \"enquiry\", \"full\"] as const;\nexport type SiteSystemTier = (typeof SITE_SYSTEM_TIERS)[number];\n\n/** The design check D4 blocks every new build and redesign, whatever its tier. */\nexport type SiteSystemCheckScope = SiteSystemTier | \"new-build\";\n\nexport type SiteSystemCheckKind = \"journey\" | \"rendered\" | \"static\" | \"record\";\n\nexport interface SiteSystemCheck {\n readonly id: string;\n readonly tier: SiteSystemCheckScope;\n /** The first release in which the check blocks at its tier. */\n readonly from: `3.${number}.0`;\n readonly kind: SiteSystemCheckKind;\n readonly summary: string;\n /**\n * `launch`: a not-run result blocks only under `brandlab-ready --launch`, where it counts as a failure. For checks\n * that need production values in the shell (H13), so a CI run reports them as not run without failing the tier.\n */\n readonly notRunBlocks?: \"launch\";\n}\n\nexport const SITE_SYSTEM_CHECKS = [\n // shell: header, nav, call bar, placements, crawlable links, contrast and targets, call links, budgets, legal\n { id: \"N1\", tier: \"shell\", from: \"3.9.0\", kind: \"rendered\", summary: \"Required nav links, About in the first two, services before Areas, Contact last, labels from the closed list, recorded orders kept.\" },\n { id: \"N2\", tier: \"shell\", from: \"3.8.0\", kind: \"rendered\", summary: \"Every dropdown link and hub link is an <a href> in the server HTML.\" },\n { id: \"N3\", tier: \"shell\", from: \"3.8.0\", kind: \"journey\", summary: \"Hover opens after a short delay and a diagonal path stays open; mouse-out closes; nothing sticks open.\" },\n { id: \"N4\", tier: \"shell\", from: \"3.8.0\", kind: \"journey\", summary: \"A chevron click after hover keeps the panel open; a link click closes it; click, Enter and outside click work.\" },\n { id: \"N5\", tier: \"shell\", from: \"3.8.0\", kind: \"journey\", summary: \"Keyboard and ARIA: aria-expanded follows the panel, Arrow keys move, Escape closes and returns focus; axe passes open and closed.\" },\n { id: \"N6\", tier: \"shell\", from: \"3.8.0\", kind: \"journey\", summary: \"The header stays at the top with the phone and quote visible at every desktop width; the compact band (1024 to 1199 px) shows them too.\" },\n { id: \"N8\", tier: \"shell\", from: \"3.9.0\", kind: \"journey\", summary: \"The drawer is a modal dialog that traps focus and resets; the call bar uses placement fab; back-to-top appears (from 3.11.0).\" },\n { id: \"K1\", tier: \"shell\", from: \"3.9.0\", kind: \"rendered\", summary: \"Shared components in use for the parts the tier covers (data-bl present); no literal tel: outside PhoneLink.\" },\n { id: \"K3\", tier: \"shell\", from: \"3.8.0\", kind: \"journey\", summary: \"Text contrast 4.5:1 and non-text 3:1 in the browser.\" },\n { id: \"K4\", tier: \"shell\", from: \"3.8.0\", kind: \"journey\", summary: \"Targets at least 24 px, and 44 px for primary actions (call, quote, menu).\" },\n { id: \"K5\", tier: \"shell\", from: \"3.8.0\", kind: \"journey\", summary: \"Focus is never fully hidden by the sticky header or call bar at 390 px, in Chromium and WebKit.\" },\n { id: \"K6\", tier: \"shell\", from: \"3.8.0\", kind: \"journey\", summary: \"Phone and quote in the same relative order on every page, in the header, hero and call bar.\" },\n { id: \"K7\", tier: \"shell\", from: \"3.8.0\", kind: \"rendered\", summary: \"Dropdown links, accordion and tab panels and full review text are in the server HTML.\" },\n { id: \"K10\", tier: \"shell\", from: \"3.8.0\", kind: \"record\", summary: \"Adoption parity in the lenient harness: no section, link or text lost against the baseline.\" },\n { id: \"C4\", tier: \"shell\", from: \"3.8.0\", kind: \"journey\", summary: \"Phone taps report an allowed placement (header, footer, hero, cta, fab, contact, location, page, emergency).\" },\n { id: \"C5\", tier: \"shell\", from: \"3.8.0\", kind: \"rendered\", summary: \"Call-to-action labels come from the closed list or a recorded label.\" },\n { id: \"C9\", tier: \"shell\", from: \"3.8.0\", kind: \"rendered\", summary: \"No interstitial overlay in the first screen.\" },\n { id: \"C10\", tier: \"shell\", from: \"3.8.0\", kind: \"rendered\", summary: \"Every tel: href is E.164 (national digits for 13, 1300 and 1800) and matches the evidence-file phone.\" },\n { id: \"E1\", tier: \"shell\", from: \"3.8.0\", kind: \"static\", summary: \"Title patterns on new or changed URLs; recorded titles unchanged.\" },\n { id: \"E2\", tier: \"shell\", from: \"3.8.0\", kind: \"rendered\", summary: \"Exactly one H1 on every page, rendered once.\" },\n { id: \"E8\", tier: \"shell\", from: \"3.8.0\", kind: \"rendered\", summary: \"No banned phrase from any of the three layers, environmental claims included.\" },\n { id: \"T3\", tier: \"shell\", from: \"3.8.0\", kind: \"rendered\", summary: \"A licence line renders wherever licensed work is advertised, with the licensee where it differs.\" },\n { id: \"T4\", tier: \"shell\", from: \"3.8.0\", kind: \"rendered\", summary: \"No hard-coded rating or review numbers; proof claims resolve to renderable evidence.\" },\n { id: \"T11\", tier: \"shell\", from: \"3.8.0\", kind: \"static\", summary: \"No page for work over $5,000 outside the licence classes held; licence conditions carried.\" },\n { id: \"X1\", tier: \"shell\", from: \"3.8.0\", kind: \"record\", summary: \"Legacy URLs answer 200 or one redirect hop; earning-URL depth, titles and dollar figures kept.\" },\n { id: \"B1\", tier: \"shell\", from: \"3.8.0\", kind: \"rendered\", summary: \"Lab budgets: JavaScript, third-party requests, hero image and HTML within budget; no unrecorded chat widget, pixel, heatmap or tag manager.\" },\n { id: \"L10-hero\", tier: \"shell\", from: \"3.8.0\", kind: \"rendered\", summary: 'No kilometre figures, \"km from\" or \"based in\" in hero copy.' },\n { id: \"W2\", tier: \"shell\", from: \"3.13.0\", kind: \"journey\", summary: \"Prefetch by link role at 390 x 844 and 1440 x 900: at most 8 pages on landing and 12 after a full scroll; none from footer, area, nearby, map or finder links, hidden links or the current page; prefetch bytes above twice the page's HTML warn.\" },\n { id: \"W5\", tier: \"shell\", from: \"3.13.0\", kind: \"static\", summary: \"vercel.json regions equal hosting.originRegion (syd1 by default); any other region has an origin-region-v1 decision whose reviewBy has not passed.\" },\n { id: \"W10\", tier: \"shell\", from: \"3.13.0\", kind: \"rendered\", summary: \"robots.txt and the recorded firewall rules leave every search and answer crawler open; no Crawl-delay for them.\" },\n\n // enquiry: the form, thank-you page, reviews, proof strip, footer, CTA band, contact page, prices, media, density\n { id: \"C1\", tier: \"enquiry\", from: \"3.8.0\", kind: \"journey\", summary: \"Money pages: the form in the first section; at 390 x 844 the H1, a call and a quote action in the first viewport; at 1440 x 900 the form.\" },\n { id: \"C2\", tier: \"enquiry\", from: \"3.8.0\", kind: \"rendered\", summary: 'Form layout and labels; phone and email required; no helper or reassurance lines, no \"(required)\", no job select.' },\n { id: \"E9\", tier: \"enquiry\", from: \"3.8.0\", kind: \"rendered\", summary: \"Every cost guide carries at least 8 dollar figures in a renderable price state.\" },\n { id: \"T5\", tier: \"enquiry\", from: \"3.8.0\", kind: \"rendered\", summary: \"Reviews from the feed, with Google's rating, count and link beside any selection; none on the contact page; no empty reviews section.\" },\n { id: \"K8\", tier: \"enquiry\", from: \"3.8.0\", kind: \"rendered\", summary: \"Section contract: no empty section, one H2 each, at most one band tone, neighbours differ, proof within the first five sections.\" },\n { id: \"C7\", tier: \"enquiry\", from: \"3.10.0\", kind: \"rendered\", summary: \"Thank-you page: a reply time, a tel: link and noindex, follow.\" },\n { id: \"C8\", tier: \"enquiry\", from: \"3.10.0\", kind: \"rendered\", summary: \"Forgiving form: tolerant phone input (13, 1300 and 1800 too), autocomplete tokens, no capture on the photo input.\" },\n { id: \"C11\", tier: \"enquiry\", from: \"3.10.0\", kind: \"static\", summary: \"The test marker is stripped before delivery and reporting; /api/contact is rate-limited.\" },\n { id: \"N9\", tier: \"enquiry\", from: \"3.11.0\", kind: \"rendered\", summary: 'Footer content: column 1 brand, licences, socials and contact; services, areas and company columns; © name, ABN and legal links; no CTA, \"based in\", hours or guides column.' },\n { id: \"K2\", tier: \"enquiry\", from: \"3.11.0\", kind: \"rendered\", summary: \"Every rendered shared component has a skin entry; no reference-skin class ships.\" },\n { id: \"K11\", tier: \"enquiry\", from: \"3.11.0\", kind: \"rendered\", summary: \"Density: no section with more than one H2, more than 8 cards or more than 2 calls to action; measure and section padding within limits.\" },\n { id: \"K12-contact\", tier: \"enquiry\", from: \"3.11.0\", kind: \"rendered\", summary: \"The contact page is the page header, then the contact block, and nothing else.\" },\n { id: \"T6\", tier: \"enquiry\", from: \"3.11.0\", kind: \"static\", summary: \"Every image has a media.ts entry with provenance.\" },\n { id: \"T7\", tier: \"enquiry\", from: \"3.11.0\", kind: \"rendered\", summary: \"No image is the main image of two pages.\" },\n { id: \"T8\", tier: \"enquiry\", from: \"3.11.0\", kind: \"rendered\", summary: 'Prices: GST flag set; no \"+ GST\" or \"from $X\" teasers on cards and menus; one band per job site-wide.' },\n { id: \"T9\", tier: \"enquiry\", from: \"3.11.0\", kind: \"static\", summary: \"No image in /public carries location metadata.\" },\n { id: \"H13\", tier: \"enquiry\", from: \"3.14.0\", kind: \"static\", notRunBlocks: \"launch\", summary: \"The business inbox can receive enquiries: a real sender (no .invalid, example or sandbox domain), MX records for the inbox, the sender domain verified in Resend and the inbox not on Resend's suppression list (not run without the inbox variable and a full-access Resend key in the shell, which blocks only at launch).\" },\n\n // full: page families, location estates, section manifests, links and the data contract\n { id: \"K12\", tier: \"full\", from: \"3.12.0\", kind: \"rendered\", summary: \"Section manifest: every required section rendered or skipped with a data-backed reason; conditional sections render when their data exists; canonical or recorded order.\" },\n { id: \"K12-slots\", tier: \"full\", from: \"3.14.0\", kind: \"rendered\", summary: \"Trade lines inside sections (matrix part B): for trees, the council permit note in LocalFacts on suburb and service-in-suburb pages and the qualifications and insurance lines on the about page, or a data-backed reason they are hidden.\" },\n { id: \"K14\", tier: \"shell\", from: \"3.19.0\", kind: \"journey\", summary: \"Neighbouring blocks never share a background: at 1440 px the painted ground of each top-level block in main, the CTA band and the footer differs from its neighbour's on every sampled and family page; a [data-bl-inset] panel belongs to its place.\" },\n { id: \"K13\", tier: \"full\", from: \"3.16.0\", kind: \"rendered\", summary: \"Signature sections: a name, at most two on the home page and one on a service page, none elsewhere, as the manifest lists them; the home page's are the ones strategy.ts records for D4.\" },\n { id: \"E5\", tier: \"full\", from: \"3.12.0\", kind: \"static\", summary: \"No orphan indexable pages, from the route catalogue and link data.\" },\n { id: \"E10\", tier: \"full\", from: \"3.12.0\", kind: \"static\", summary: \"Phrase-owner uniqueness in strategy data; no service-in-suburb page for the suburb page phrase; no service-region hub for the main trade.\" },\n { id: \"L3\", tier: \"full\", from: \"3.12.0\", kind: \"static\", summary: \"Neighbours by real distance.\" },\n { id: \"L4\", tier: \"full\", from: \"3.12.0\", kind: \"rendered\", summary: \"No copy rotated by page address, no name-swapped FAQs, no other suburb's job without the nearby label.\" },\n { id: \"L6\", tier: \"full\", from: \"3.12.0\", kind: \"static\", summary: \"Location pages have 6 or more inbound links from 3 or more page types.\" },\n { id: \"L7\", tier: \"full\", from: \"3.12.0\", kind: \"static\", summary: \"Location pages are 3 clicks or fewer from home.\" },\n { id: \"L8\", tier: \"full\", from: \"3.12.0\", kind: \"static\", summary: \"A hub per family per region.\" },\n { id: \"L9\", tier: \"full\", from: \"3.12.0\", kind: \"rendered\", summary: \"Data contract on service-in-suburb pages: typical prices or the cost-guide link, reviews or their fallback, the quote path (form with the suburb prefilled, phone, call bar); the nearby job card or none.\" },\n { id: \"L10\", tier: \"full\", from: \"3.12.0\", kind: \"rendered\", summary: 'No kilometre figures, \"km from\", \"based in\" or year stamps as local detail in location copy.' },\n { id: \"S4\", tier: \"full\", from: \"3.12.0\", kind: \"static\", summary: \"No hand-written per-service or per-suburb files; no dynamic route with zero pages.\" },\n { id: \"W9\", tier: \"full\", from: \"3.13.0\", kind: \"journey\", summary: \"URL variants: an unknown slug in each family answers the shared 404, the uppercase slug the same 404 or one permanent redirect to its lowercase form; a tracking query serves the same cached page as the clean URL.\" },\n { id: \"W9-params\", tier: \"full\", from: \"3.13.0\", kind: \"static\", summary: \"Pages under a dynamic route never read searchParams, so a query string cannot make an estate page dynamic.\" },\n { id: \"LOC-INDEX\", tier: \"full\", from: \"3.12.0\", kind: \"static\", summary: \"No location route is noindex or left out of the sitemap unless the recorded wave schedule dates it (Romie's 20 Oct decision: every suburb page stays indexed).\" },\n { id: \"E1-pattern\", tier: \"full\", from: \"3.14.0\", kind: \"rendered\", summary: 'Location pages outside the recorded titles have the H1 \"{Service} {Place}\" (no \"in\", no \"the\") and a title that starts with it, at most 60 characters.' },\n { id: \"W3\", tier: \"full\", from: \"3.13.0\", kind: \"static\", summary: \"No estate family (suburb, service-in-suburb, region, council) refreshes more often than every 7 days unless siteSystem.refreshExceptions records the reason and a decision id.\" },\n\n // new builds and redesigns\n { id: \"D4\", tier: \"new-build\", from: \"3.8.0\", kind: \"static\", summary: \"Same-trade distinctness: at least four of seven look levers differ from every listed competitor (advisory in 3.8.0, blocking from 3.9.0).\" },\n] as const satisfies readonly SiteSystemCheck[];\n\nexport type SiteSystemCheckId = (typeof SITE_SYSTEM_CHECKS)[number][\"id\"];\n\nconst TIER_RANK: Record<SiteSystemTier, number> = { shell: 0, enquiry: 1, full: 2 };\n\n/** A release as one comparable number: \"3.8.0\" < \"3.10.0\". */\nexport function siteSystemReleaseRank(version: string): number {\n const [major = 0, minor = 0, patch = 0] = version.split(/[.-]/).map((part) => Number.parseInt(part, 10) || 0);\n return major * 1_000_000 + minor * 1_000 + patch;\n}\n\n/**\n * Whether a check blocks for a site at `tier` on `release`. Checks of higher tiers, and checks newer than the\n * installed release, report without blocking. D4 blocks new builds and redesigns from 3.9.0 whatever their tier.\n */\nexport function siteSystemCheckBlocks(\n check: Pick<SiteSystemCheck, \"id\" | \"tier\" | \"from\">,\n site: { tier: SiteSystemTier; release: string; newBuild?: boolean },\n): boolean {\n if (check.tier === \"new-build\") return site.newBuild === true && siteSystemReleaseRank(site.release) >= siteSystemReleaseRank(\"3.9.0\");\n if (siteSystemReleaseRank(site.release) < siteSystemReleaseRank(check.from)) return false;\n return TIER_RANK[check.tier] <= TIER_RANK[site.tier];\n}\n\n/** One result of one check, from any runner. `brandlab-ready` merges them into the ready report. */\nexport interface SiteSystemFinding {\n readonly checkId: SiteSystemCheckId;\n /** `not-run`: the rule could not judge the check here (a missing key or input); it names what to supply. */\n readonly status: \"pass\" | \"fail\" | \"not-run\" | \"not-applicable\";\n /** The page path or file the result is about, when it is about one. */\n readonly target?: string;\n /** One plain sentence: what passed or what is wrong and where. */\n readonly message: string;\n /** A warning never blocks: the check still passes and the report shows \"pass (warning)\". */\n readonly severity?: \"warning\";\n}\n"]}
@@ -0,0 +1,3 @@
1
+ import { type SiteSystemJourneyPlan } from './plan.js';
2
+ export declare function defineGroundJourneys(plan: SiteSystemJourneyPlan, runs: ReadonlySet<string>): void;
3
+ //# sourceMappingURL=grounds.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grounds.d.ts","sourceRoot":"","sources":["../../../src/site-system/journeys/grounds.ts"],"names":[],"mappings":"AAUA,OAAO,EAAiE,KAAK,qBAAqB,EAAE,MAAM,WAAW,CAAC;AAEtH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,qBAAqB,EAAE,IAAI,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,IAAI,CAejG"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * K14 (shell tier, from 3.19.0): neighbouring blocks never share a background (Romie, 27 Oct 2026: "no two sections in
3
+ * a row have the same background color"). At 1440 x 900, on each sampled page, each family page and each page in
4
+ * `pages.grounds`, the painted ground of every top-level block in `main` and of what follows it (the CTA band and the
5
+ * footer) is compared with its neighbour's. A photo or gradient never matches; a `[data-bl-inset]` panel is part of the
6
+ * place before it. K8 checks the manifest's tones; this checks what the browser paints, so a page off SectionSequence,
7
+ * a companion block with its own ground and a section the skin paints by hand are caught too.
8
+ */
9
+ import { expect, test } from '@playwright/test';
10
+ import { openSitePage } from './browser.js';
11
+ import { SITE_SYSTEM_DESKTOP_VIEWPORT, siteSystemJourneyTitle as title } from './plan.js';
12
+ export function defineGroundJourneys(plan, runs) {
13
+ if (!runs.has('K14'))
14
+ return;
15
+ test.describe('alternating backgrounds', () => {
16
+ for (const path of plan.grounds) {
17
+ test(title('K14', `${path}: no two neighbouring blocks share a background at 1440 px`), async ({ page }) => {
18
+ await page.setViewportSize({ ...SITE_SYSTEM_DESKTOP_VIEWPORT });
19
+ await openSitePage(page, path, plan);
20
+ const problems = await page.evaluate((at) => {
21
+ const helpers = window.__brandlabSiteSystem;
22
+ return helpers.groundProblems(at, helpers.groundBlocks());
23
+ }, path);
24
+ expect(problems, `${path} has neighbouring blocks on the same background`).toEqual([]);
25
+ });
26
+ }
27
+ });
28
+ }
29
+ //# sourceMappingURL=grounds.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grounds.js","sourceRoot":"","sources":["../../../src/site-system/journeys/grounds.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,4BAA4B,EAAE,sBAAsB,IAAI,KAAK,EAA8B,MAAM,WAAW,CAAC;AAEtH,MAAM,UAAU,oBAAoB,CAAC,IAA2B,EAAE,IAAyB;IACzF,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC;QAAE,OAAO;IAC7B,IAAI,CAAC,QAAQ,CAAC,yBAAyB,EAAE,GAAG,EAAE;QAC5C,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAChC,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,IAAI,4DAA4D,CAAC,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;gBACzG,MAAM,IAAI,CAAC,eAAe,CAAC,EAAE,GAAG,4BAA4B,EAAE,CAAC,CAAC;gBAChE,MAAM,YAAY,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;gBACrC,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,EAAE;oBAC1C,MAAM,OAAO,GAAG,MAAM,CAAC,oBAAoB,CAAC;oBAC5C,OAAO,OAAO,CAAC,cAAc,CAAC,EAAE,EAAE,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC;gBAC5D,CAAC,EAAE,IAAI,CAAC,CAAC;gBACT,MAAM,CAAC,QAAQ,EAAE,GAAG,IAAI,iDAAiD,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;YACzF,CAAC,CAAC,CAAC;QACL,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["/**\n * K14 (shell tier, from 3.19.0): neighbouring blocks never share a background (Romie, 27 Oct 2026: \"no two sections in\n * a row have the same background color\"). At 1440 x 900, on each sampled page, each family page and each page in\n * `pages.grounds`, the painted ground of every top-level block in `main` and of what follows it (the CTA band and the\n * footer) is compared with its neighbour's. A photo or gradient never matches; a `[data-bl-inset]` panel is part of the\n * place before it. K8 checks the manifest's tones; this checks what the browser paints, so a page off SectionSequence,\n * a companion block with its own ground and a section the skin paints by hand are caught too.\n */\nimport { expect, test } from '@playwright/test';\nimport { openSitePage } from './browser.js';\nimport { SITE_SYSTEM_DESKTOP_VIEWPORT, siteSystemJourneyTitle as title, type SiteSystemJourneyPlan } from './plan.js';\n\nexport function defineGroundJourneys(plan: SiteSystemJourneyPlan, runs: ReadonlySet<string>): void {\n if (!runs.has('K14')) return;\n test.describe('alternating backgrounds', () => {\n for (const path of plan.grounds) {\n test(title('K14', `${path}: no two neighbouring blocks share a background at 1440 px`), async ({ page }) => {\n await page.setViewportSize({ ...SITE_SYSTEM_DESKTOP_VIEWPORT });\n await openSitePage(page, path, plan);\n const problems = await page.evaluate((at) => {\n const helpers = window.__brandlabSiteSystem;\n return helpers.groundProblems(at, helpers.groundBlocks());\n }, path);\n expect(problems, `${path} has neighbouring blocks on the same background`).toEqual([]);\n });\n }\n });\n}\n"]}
@@ -116,6 +116,25 @@ export declare function focusStep(): {
116
116
  export declare function focusStepAfterFrame(): Promise<ReturnType<typeof focusStep>>;
117
117
  /** Elements a Tab press can reach, roughly: enough to bound the K5 walk. */
118
118
  export declare function tabbableCount(): number;
119
+ /** What a K14 block paints: an opaque colour, a photo or gradient (never equal to another block's), or unknown. */
120
+ export interface PaintedGround {
121
+ description: string;
122
+ /** `rgb(r, g, b)`, `image` or `unknown`. */
123
+ ground: string;
124
+ }
125
+ /**
126
+ * The ground a top-level block paints (K14, 3.19.0): its own background image (a photo band or gradient), a photo
127
+ * covering most of it (a fill image), else its background colour composited over what shows through from its
128
+ * ancestors. A transparent block whose one child covers it and paints a colour takes the child's.
129
+ */
130
+ export declare function paintedGround(element: Element): string;
131
+ /**
132
+ * The page's top-level blocks in order (K14): each rendered child of `main` (through a lone wrapper), then what follows
133
+ * `main` up to and including the footer. A `[data-bl-inset]` block is part of the place before it, so it is left out.
134
+ */
135
+ export declare function groundBlocks(): PaintedGround[];
136
+ /** Neighbouring blocks that paint the same colour (K14): a photo or an unknown ground never matches. */
137
+ export declare function groundProblems(path: string, blocks: readonly PaintedGround[]): string[];
119
138
  declare const PAGE_HELPERS: {
120
139
  isRendered: typeof isRendered;
121
140
  describeElement: typeof describeElement;
@@ -137,6 +156,9 @@ declare const PAGE_HELPERS: {
137
156
  focusStep: typeof focusStep;
138
157
  focusStepAfterFrame: typeof focusStepAfterFrame;
139
158
  tabbableCount: typeof tabbableCount;
159
+ paintedGround: typeof paintedGround;
160
+ groundBlocks: typeof groundBlocks;
161
+ groundProblems: typeof groundProblems;
140
162
  };
141
163
  /** `window.__brandlabSiteSystem` in a page the journeys opened. */
142
164
  export type SiteSystemPageHelpers = typeof PAGE_HELPERS;
@@ -1 +1 @@
1
- {"version":3,"file":"page-helpers.d.ts","sourceRoot":"","sources":["../../../src/site-system/journeys/page-helpers.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"page-helpers.d.ts","sourceRoot":"","sources":["../../../src/site-system/journeys/page-helpers.ts"],"names":[],"mappings":"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"}
@@ -413,10 +413,90 @@ export function tabbableCount() {
413
413
  const selector = 'a[href], button:not([disabled]), input:not([disabled]):not([type="hidden"]), select:not([disabled]), textarea:not([disabled]), summary, iframe, [tabindex]:not([tabindex="-1"])';
414
414
  return Array.from(document.querySelectorAll(selector)).filter(isRendered).length;
415
415
  }
416
+ /**
417
+ * The ground a top-level block paints (K14, 3.19.0): its own background image (a photo band or gradient), a photo
418
+ * covering most of it (a fill image), else its background colour composited over what shows through from its
419
+ * ancestors. A transparent block whose one child covers it and paints a colour takes the child's.
420
+ */
421
+ export function paintedGround(element) {
422
+ const box = element.getBoundingClientRect();
423
+ const area = box.width * box.height;
424
+ if (area <= 0)
425
+ return 'unknown';
426
+ if (getComputedStyle(element).backgroundImage !== 'none')
427
+ return 'image';
428
+ for (const media of Array.from(element.querySelectorAll('img, picture, video'))) {
429
+ const rect = media.getBoundingClientRect();
430
+ if (rect.width * rect.height >= 0.8 * area && isRendered(media))
431
+ return 'image';
432
+ }
433
+ let painter = element;
434
+ const own = parseColour(getComputedStyle(element).backgroundColor);
435
+ if (!own || own[3] === 0) {
436
+ const cover = Array.from(element.children).find((child) => {
437
+ const rect = child.getBoundingClientRect();
438
+ const colour = parseColour(getComputedStyle(child).backgroundColor);
439
+ return rect.width * rect.height >= 0.9 * area && colour !== null && colour[3] > 0;
440
+ });
441
+ if (cover) {
442
+ if (getComputedStyle(cover).backgroundImage !== 'none')
443
+ return 'image';
444
+ painter = cover;
445
+ }
446
+ }
447
+ const colour = backgroundBehind(painter);
448
+ if (!colour)
449
+ return 'image';
450
+ return `rgb(${colour.slice(0, 3).map((channel) => Math.round(channel)).join(', ')})`;
451
+ }
452
+ /**
453
+ * The page's top-level blocks in order (K14): each rendered child of `main` (through a lone wrapper), then what follows
454
+ * `main` up to and including the footer. A `[data-bl-inset]` block is part of the place before it, so it is left out.
455
+ */
456
+ export function groundBlocks() {
457
+ const main = document.querySelector('main');
458
+ if (!main)
459
+ return [];
460
+ const usable = (element) => !['SCRIPT', 'STYLE', 'TEMPLATE', 'NOSCRIPT', 'LINK'].includes(element.tagName)
461
+ && !element.hasAttribute('data-bl-inset') && isRendered(element) && element.getBoundingClientRect().height >= 8;
462
+ let blocks = Array.from(main.children).filter(usable);
463
+ while (blocks.length === 1 && Array.from(blocks[0].children).filter(usable).length > 1 && paintedGround(blocks[0]).startsWith('rgb') === false) {
464
+ blocks = Array.from(blocks[0].children).filter(usable);
465
+ }
466
+ const footer = document.querySelector('footer[data-bl="footer"], body footer:not(main footer)');
467
+ for (let node = main.nextElementSibling; node; node = node.nextElementSibling) {
468
+ if (usable(node))
469
+ blocks.push(node);
470
+ if (node === footer || node.contains(footer))
471
+ break;
472
+ }
473
+ if (footer && !blocks.some((block) => block === footer || block.contains(footer)) && isRendered(footer))
474
+ blocks.push(footer);
475
+ return blocks.map((block) => {
476
+ const section = block.getAttribute('data-bl-section') ?? (block.tagName === 'FOOTER' ? 'footer' : null);
477
+ const description = section ? `"${section}"` : `${block.tagName.toLowerCase()}${block.id ? `#${block.id}` : ''}`;
478
+ return { description, ground: paintedGround(block) };
479
+ });
480
+ }
481
+ /** Neighbouring blocks that paint the same colour (K14): a photo or an unknown ground never matches. */
482
+ export function groundProblems(path, blocks) {
483
+ const problems = [];
484
+ const channels = (ground) => ground.slice(4, -1).split(',').map(Number);
485
+ for (let index = 1; index < blocks.length; index += 1) {
486
+ const [before, after] = [blocks[index - 1], blocks[index]];
487
+ if (!before.ground.startsWith('rgb') || !after.ground.startsWith('rgb'))
488
+ continue;
489
+ const [left, right] = [channels(before.ground), channels(after.ground)];
490
+ if (left.every((channel, at) => Math.abs(channel - right[at]) <= 2)) {
491
+ problems.push(`${path}: ${before.description} and ${after.description} share the background ${after.ground}`);
492
+ }
493
+ }
494
+ return problems;
495
+ }
416
496
  const PAGE_HELPERS = {
417
497
  isRendered, describeElement, accessibleName, phonePlacement, parseColour, compositeColour, contrastRatio, backgroundBehind,
418
498
  iconContrastProblems, findCallBar, quoteActions, actionOrder, heroRegion, outsidePoint, markElement, watchDisclosure,
419
- watchState, focusStep, focusStepAfterFrame, tabbableCount,
499
+ watchState, focusStep, focusStepAfterFrame, tabbableCount, paintedGround, groundBlocks, groundProblems,
420
500
  };
421
501
  /** The init script that defines `window.__brandlabSiteSystem` in every document of the page. */
422
502
  export function pageHelpersSource() {
@@ -1 +1 @@
1
- {"version":3,"file":"page-helpers.js","sourceRoot":"","sources":["../../../src/site-system/journeys/page-helpers.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Functions the site-system journeys run inside the page under test. Each one is self-contained: it uses DOM globals\n * and the other helpers in this file only, because `pageHelpersSource()` injects their source into every document\n * (as `window.__brandlabSiteSystem`, through Playwright's init script, which a Content Security Policy does not\n * block). The pure parts (colour, contrast, placement) are unit-tested in Node.\n */\n\n/**\n * The placements `phone_link_click` accepts: site-next's PHONE_CALL_PLACEMENTS (conformance cannot depend on\n * site-next; a test reads the site-next source and compares). Injected into the page with the helpers.\n */\nexport const SITE_SYSTEM_PHONE_PLACEMENTS: readonly string[] = Object.freeze(['header', 'footer', 'hero', 'cta', 'fab', 'contact', 'location', 'page', 'emergency']);\n\nexport type Rgba = [number, number, number, number];\n\n/** What `watchDisclosure` has seen, in `performance.now()` milliseconds. */\nexport interface DisclosureWatch {\n enter: number | null;\n leave: number | null;\n opened: number | null;\n closed: number | null;\n open: boolean;\n}\n\n/**\n * Whether an element is rendered: connected, with a box, not `visibility: hidden`, not fully transparent and not\n * inside a closed `<details>` (outside its summary). Chromium gives the content of a closed `<details>` a box, so\n * the box alone is not enough; `checkVisibility()` settles it where the browser has it.\n */\nexport function isRendered(element: Element | null | undefined): boolean {\n if (!element || !element.isConnected) return false;\n for (let details = element.closest('details:not([open])'); details; details = details.parentElement?.closest('details:not([open])') ?? null) {\n const summary = Array.from(details.children).find((child) => child.tagName === 'SUMMARY');\n if (!summary || !summary.contains(element)) return false;\n }\n if (typeof element.checkVisibility === 'function' && !element.checkVisibility({ checkOpacity: true, checkVisibilityCSS: true })) return false;\n const rect = element.getBoundingClientRect();\n if (rect.width <= 0 || rect.height <= 0) return false;\n if (getComputedStyle(element).visibility !== 'visible') return false;\n for (let node: Element | null = element; node; node = node.parentElement) {\n if (getComputedStyle(node).opacity === '0') return false;\n }\n return true;\n}\n\n/** A short, readable name for an element in a failure message. */\nexport function describeElement(element: Element): string {\n const tag = element.tagName.toLowerCase();\n const label = element.getAttribute('aria-label') ?? (element as HTMLElement).innerText ?? element.textContent ?? '';\n const text = label.replace(/\\s+/g, ' ').trim().slice(0, 40);\n const href = element.getAttribute('href');\n return `${tag}${text ? ` \"${text}\"` : ''}${href ? ` (${href})` : ''}`;\n}\n\n/** The accessible name, approximately: aria-label, aria-labelledby, the rendered text, then the title. */\nexport function accessibleName(element: Element): string {\n const label = element.getAttribute('aria-label');\n if (label && label.trim()) return label.trim();\n const labelledBy = element.getAttribute('aria-labelledby');\n if (labelledBy) {\n const text = labelledBy.split(/\\s+/).map((id) => document.getElementById(id)?.textContent ?? '').join(' ').trim();\n if (text) return text.replace(/\\s+/g, ' ');\n }\n const text = ((element as HTMLElement).innerText ?? element.textContent ?? '').replace(/\\s+/g, ' ').trim();\n return text || (element.getAttribute('title') ?? '').trim();\n}\n\n/**\n * Where a telephone link reports its tap, exactly as site-next's shared listener resolves it: its own valid\n * `data-phone-location`, then a `<header>` or `<footer>` ancestor, then the nearest ancestor with a valid value,\n * otherwise `page`. `invalid` is the nearest declared value when it is not an allowed placement (the listener then\n * reports the link somewhere else, silently).\n */\nexport function phonePlacement(link: Element): { placement: string; invalid: string | null } {\n const valid = (node: Element): string | undefined => {\n const value = node.getAttribute('data-phone-location')?.trim().toLowerCase();\n return value && SITE_SYSTEM_PHONE_PLACEMENTS.includes(value) ? value : undefined;\n };\n const nearest = link.closest('[data-phone-location]');\n const invalid = nearest && !valid(nearest) ? nearest.getAttribute('data-phone-location') : null;\n const own = valid(link);\n if (own) return { placement: own, invalid };\n if (link.closest('header')) return { placement: 'header', invalid };\n if (link.closest('footer')) return { placement: 'footer', invalid };\n for (let node = link.parentElement?.closest('[data-phone-location]') ?? null; node; node = node.parentElement?.closest('[data-phone-location]') ?? null) {\n const value = valid(node);\n if (value) return { placement: value, invalid };\n }\n return { placement: 'page', invalid };\n}\n\n/** Parse a computed colour. `rgb()`/`rgba()` directly; any other syntax (oklch, lab, color()) through a canvas pixel. */\nexport function parseColour(value: string): Rgba | null {\n const text = value.trim().toLowerCase();\n if (text === 'transparent') return [0, 0, 0, 0];\n const match = /^rgba?\\(([^)]+)\\)$/.exec(text);\n if (match) {\n const parts = match[1]!.split(/[\\s,/]+/).filter(Boolean);\n if (parts.length < 3) return null;\n const channel = (part: string) => (part.endsWith('%') ? (Number.parseFloat(part) / 100) * 255 : Number.parseFloat(part));\n const alphaPart = parts[3];\n const alpha = alphaPart === undefined ? 1 : alphaPart.endsWith('%') ? Number.parseFloat(alphaPart) / 100 : Number.parseFloat(alphaPart);\n const rgba: Rgba = [channel(parts[0]!), channel(parts[1]!), channel(parts[2]!), alpha];\n return rgba.every((part) => Number.isFinite(part)) ? rgba : null;\n }\n if (typeof document === 'undefined') return null;\n const canvas = document.createElement('canvas');\n canvas.width = 1;\n canvas.height = 1;\n const context = canvas.getContext('2d', { willReadFrequently: true });\n if (!context) return null;\n context.clearRect(0, 0, 1, 1);\n context.fillStyle = value;\n context.fillRect(0, 0, 1, 1);\n const [red = 0, green = 0, blue = 0, alpha = 0] = Array.from(context.getImageData(0, 0, 1, 1).data);\n return [red, green, blue, alpha / 255];\n}\n\n/** `top` painted over an opaque `bottom`. */\nexport function compositeColour(top: Rgba, bottom: Rgba): Rgba {\n const alpha = top[3];\n return [\n top[0] * alpha + bottom[0] * (1 - alpha),\n top[1] * alpha + bottom[1] * (1 - alpha),\n top[2] * alpha + bottom[2] * (1 - alpha),\n 1,\n ];\n}\n\n/** The WCAG contrast ratio of two opaque colours. */\nexport function contrastRatio(first: Rgba, second: Rgba): number {\n const luminance = (colour: Rgba) => {\n const [red, green, blue] = colour.slice(0, 3).map((channel) => {\n const value = channel / 255;\n return value <= 0.04045 ? value / 12.92 : ((value + 0.055) / 1.055) ** 2.4;\n }) as [number, number, number];\n return 0.2126 * red + 0.7152 * green + 0.0722 * blue;\n };\n const [light, dark] = [luminance(first), luminance(second)].sort((left, right) => right - left) as [number, number];\n return (light + 0.05) / (dark + 0.05);\n}\n\n/**\n * The opaque colour behind an element, from its own and its ancestors' background colours over a white canvas.\n * `null` when a background image (a photo or gradient) is in the way: the contrast cannot be computed there.\n */\nexport function backgroundBehind(element: Element): Rgba | null {\n const layers: Rgba[] = [];\n for (let node: Element | null = element; node; node = node.parentElement) {\n const style = getComputedStyle(node);\n if (style.backgroundImage && style.backgroundImage !== 'none') return null;\n const colour = parseColour(style.backgroundColor);\n if (colour && colour[3] > 0) {\n layers.push(colour);\n if (colour[3] >= 1) break;\n }\n }\n return layers.reverse().reduce<Rgba>((bottom, top) => compositeColour(top, bottom), [255, 255, 255, 1]);\n}\n\n/**\n * Non-text contrast (WCAG 1.4.11) of icon-only controls inside `root`: a link or button with no visible text whose\n * icon is an SVG must reach 3:1 against what is behind it. Returns one line per failure; controls over a photo are\n * counted in `unmeasured`.\n */\nexport function iconContrastProblems(root: Element): { problems: string[]; checked: number; unmeasured: number } {\n const problems: string[] = [];\n let checked = 0;\n let unmeasured = 0;\n const paintOf = (svg: SVGElement): Rgba | null => {\n for (const shape of Array.from(svg.querySelectorAll('path, circle, rect, ellipse, line, polyline, polygon, text, use'))) {\n if (!isRendered(shape)) continue;\n const style = getComputedStyle(shape);\n for (const [paint, opacity] of [[style.fill, style.fillOpacity], [style.stroke, style.strokeOpacity]] as const) {\n if (!paint || paint === 'none' || paint.startsWith('url(')) continue;\n const colour = parseColour(paint);\n const alpha = colour ? colour[3] * Number(opacity || 1) * Number(style.opacity || 1) : 0;\n if (colour && alpha > 0) return [colour[0], colour[1], colour[2], alpha];\n }\n }\n return null;\n };\n for (const control of Array.from(root.querySelectorAll('a[href], button'))) {\n if (!isRendered(control)) continue;\n if (((control as HTMLElement).innerText ?? '').trim()) continue;\n const svg = control.querySelector('svg');\n if (!svg || !isRendered(svg)) continue;\n const paint = paintOf(svg);\n if (!paint) continue;\n const background = backgroundBehind(svg);\n if (!background) {\n unmeasured += 1;\n continue;\n }\n checked += 1;\n const ratio = contrastRatio(compositeColour(paint, background), background);\n if (ratio < 3) problems.push(`${describeElement(control)}: its icon is ${ratio.toFixed(2)}:1 against its background (needs 3:1)`);\n }\n return { problems, checked, unmeasured };\n}\n\n/**\n * The phone call bar: the shared `MobileCallBar`, or any fixed or sticky element along the bottom of the screen that\n * holds a visible telephone link (outside the header and dialogs). `null` when none is on screen.\n */\nexport function findCallBar(): Element | null {\n const tagged = Array.from(document.querySelectorAll('[data-bl=\"call-bar\"], [data-bl=\"callbar\"], [data-bl=\"mobile-call-bar\"]')).find(isRendered);\n if (tagged) return tagged;\n const height = window.innerHeight;\n for (const element of Array.from(document.body.querySelectorAll('*'))) {\n const position = getComputedStyle(element).position;\n if (position !== 'fixed' && position !== 'sticky') continue;\n if (element.closest('header, dialog, [role=\"dialog\"]')) continue;\n const rect = element.getBoundingClientRect();\n if (rect.height <= 0 || rect.bottom < height - 4 || rect.top < height / 2) continue;\n if (!isRendered(element)) continue;\n if (Array.from(element.querySelectorAll('a[href^=\"tel:\"]')).some(isRendered)) return element;\n }\n return null;\n}\n\n/** Visible quote actions in `root`: links or buttons named like the quote action, or the shared quote hooks. */\nexport function quoteActions(root: Element, name: { source: string; flags: string }): Element[] {\n const pattern = new RegExp(name.source, name.flags);\n return Array.from(root.querySelectorAll('a[href], button')).filter((element) => {\n if (!isRendered(element) || element.getAttribute('href')?.startsWith('tel:')) return false;\n if (element.matches('[data-bl=\"quote-button\"], [data-bl-cta^=\"quote\"], [data-lead-cta^=\"quote\"]')) return true;\n return pattern.test(accessibleName(element));\n });\n}\n\n/** The order of the first visible phone link and quote action in `root`. */\nexport function actionOrder(root: Element, name: { source: string; flags: string }): 'phone-first' | 'quote-first' | 'incomplete' {\n const phone = Array.from(root.querySelectorAll('a[href^=\"tel:\"]')).find(isRendered);\n const quote = quoteActions(root, name)[0];\n if (!phone || !quote) return 'incomplete';\n return phone.compareDocumentPosition(quote) & Node.DOCUMENT_POSITION_FOLLOWING ? 'phone-first' : 'quote-first';\n}\n\n/**\n * The hero: the shared hero section, else the smallest block around the page's visible H1 that holds a phone link\n * and a quote action, never above the H1's own section and never `<main>` (without a section, never a block holding\n * an H2). When none holds both, the H1's section (or its parent): its order then reads \"incomplete\".\n */\nexport function heroRegion(name: { source: string; flags: string }): Element | null {\n const heading = Array.from(document.querySelectorAll('h1')).find(isRendered);\n if (!heading) return null;\n const tagged = heading.closest('[data-bl-section=\"hero\"]');\n if (tagged) return tagged;\n const section = heading.closest('section');\n const fallback = section ?? heading.parentElement;\n for (let node = heading.parentElement; node && node !== document.body && node.tagName !== 'MAIN'; node = node.parentElement) {\n if (!section && node.querySelector('h2')) break;\n if (Array.from(node.querySelectorAll('a[href^=\"tel:\"]')).some(isRendered) && quoteActions(node, name).length > 0) return node;\n if (node === section) break;\n }\n return fallback && fallback.tagName !== 'MAIN' ? fallback : heading;\n}\n\n/** A point in the page that is not on an interactive element, a menu or anything in `avoid`. */\nexport function outsidePoint(avoid: Element[]): { x: number; y: number } | null {\n const interactive = 'a[href], button, input, select, textarea, label, summary, details, iframe, video, audio, [role=\"button\"], [role=\"link\"], [role=\"menuitem\"], [role=\"tab\"], [tabindex], [contenteditable=\"\"], [contenteditable=\"true\"]';\n for (let row = 9; row >= 3; row -= 1) {\n for (let column = 1; column <= 9; column += 1) {\n const x = Math.round((window.innerWidth * column) / 10);\n const y = Math.round((window.innerHeight * row) / 10);\n const element = document.elementFromPoint(x, y);\n if (!element || avoid.some((region) => region.contains(element))) continue;\n if (element.closest(interactive) || element.closest('header, nav, dialog, [role=\"dialog\"]')) continue;\n return { x, y };\n }\n }\n return null;\n}\n\n/** Tag an element so Playwright and axe can find it by `[data-brandlab-journey~=\"<name>\"]`. */\nexport function markElement(element: Element, name: string): void {\n const names = new Set((element.getAttribute('data-brandlab-journey') ?? '').split(/\\s+/).filter(Boolean));\n names.add(name);\n element.setAttribute('data-brandlab-journey', [...names].join(' '));\n}\n\n/**\n * Watch one disclosure: when the pointer enters and leaves its item (and panel), and when the panel it controls\n * becomes rendered or hidden, timed with `performance.now()`. Attribute and tree changes are sampled at once; style\n * changes without a DOM change (CSS `:hover`) every animation frame.\n */\nexport function watchDisclosure(trigger: Element, panelId: string): void {\n const item = trigger.closest('li') ?? trigger.parentElement ?? trigger;\n const panel = () => document.getElementById(panelId);\n const inside = (node: EventTarget | null) => node instanceof Node && (item.contains(node) || Boolean(panel()?.contains(node)));\n const state: DisclosureWatch = { enter: null, leave: null, opened: null, closed: null, open: isRendered(panel()) };\n const sample = () => {\n const open = isRendered(panel());\n if (open === state.open) return;\n state.open = open;\n if (open && state.opened === null) state.opened = performance.now();\n if (!open && state.closed === null) state.closed = performance.now();\n };\n const onOver = (event: Event) => {\n if (state.enter === null && inside(event.target)) state.enter = performance.now();\n };\n const onOut = (event: Event) => {\n if (state.leave === null && inside(event.target) && !inside((event as PointerEvent).relatedTarget)) state.leave = performance.now();\n };\n const observer = new MutationObserver(sample);\n observer.observe(document.documentElement, { attributes: true, childList: true, subtree: true });\n let frame = 0;\n const loop = () => {\n sample();\n frame = requestAnimationFrame(loop);\n };\n loop();\n document.addEventListener('pointerover', onOver, true);\n document.addEventListener('pointerout', onOut, true);\n const previous = (window as unknown as { __brandlabSiteSystemWatch?: { stop(): void } }).__brandlabSiteSystemWatch;\n previous?.stop();\n (window as unknown as { __brandlabSiteSystemWatch: unknown }).__brandlabSiteSystemWatch = {\n state,\n stop() {\n observer.disconnect();\n cancelAnimationFrame(frame);\n document.removeEventListener('pointerover', onOver, true);\n document.removeEventListener('pointerout', onOut, true);\n },\n };\n}\n\n/** The current state of `watchDisclosure`'s watch. */\nexport function watchState(): DisclosureWatch | null {\n const watch = (window as unknown as { __brandlabSiteSystemWatch?: { state: DisclosureWatch } }).__brandlabSiteSystemWatch;\n return watch ? { ...watch.state } : null;\n}\n\n/**\n * After a Tab press: is the focused element fully hidden under the sticky header or the call bar? Samples nine points\n * across the visible part of its box; it is hidden when the header or call bar is on top at every one. `repeat` is\n * true once focus comes back to an element it has already visited.\n */\nexport function focusStep(): { done: boolean; repeat: boolean; obscured: string | null; covered: 'header' | 'call bar' | null } {\n const element = document.activeElement;\n const store = window as unknown as { __brandlabSiteSystemFocus?: WeakSet<Element> };\n store.__brandlabSiteSystemFocus ??= new WeakSet<Element>();\n if (!element || element === document.body || element === document.documentElement) return { done: true, repeat: false, obscured: null, covered: null };\n const repeat = store.__brandlabSiteSystemFocus.has(element);\n store.__brandlabSiteSystemFocus.add(element);\n const header = document.querySelector('[data-brandlab-journey~=\"header\"]')\n ?? Array.from(document.querySelectorAll('[data-bl=\"header\"], header')).find((candidate) => isRendered(candidate) && !candidate.closest('main, article, section, aside, footer'))\n ?? null;\n const callBar = findCallBar();\n if (header?.contains(element) || callBar?.contains(element)) return { done: false, repeat, obscured: null, covered: null };\n const rect = element.getBoundingClientRect();\n const left = Math.max(rect.left, 0);\n const right = Math.min(rect.right, window.innerWidth);\n const top = Math.max(rect.top, 0);\n const bottom = Math.min(rect.bottom, window.innerHeight);\n if (right - left < 1 || bottom - top < 1) return { done: false, repeat, obscured: null, covered: null };\n let coveredBy: 'header' | 'call bar' | null = null;\n for (const fx of [0.15, 0.5, 0.85]) {\n for (const fy of [0.15, 0.5, 0.85]) {\n const hit = document.elementFromPoint(left + (right - left) * fx, top + (bottom - top) * fy);\n if (!hit || element.contains(hit)) return { done: false, repeat, obscured: null, covered: null };\n if (header?.contains(hit)) coveredBy ??= 'header';\n else if (callBar?.contains(hit)) coveredBy ??= 'call bar';\n else return { done: false, repeat, obscured: null, covered: null };\n }\n }\n return { done: false, repeat, obscured: describeElement(element), covered: coveredBy };\n}\n\n/**\n * `focusStep` one animation frame after the focus moved (capped at 50 ms, since a background page may not paint), so\n * a sticky bar that hides on its observer's next frame is measured after it moved, not mid-transition.\n */\nexport function focusStepAfterFrame(): Promise<ReturnType<typeof focusStep>> {\n return new Promise((resolve) => {\n let settled = false;\n const finish = () => {\n if (settled) return;\n settled = true;\n resolve(focusStep());\n };\n requestAnimationFrame(() => finish());\n setTimeout(finish, 50);\n });\n}\n\n/** Elements a Tab press can reach, roughly: enough to bound the K5 walk. */\nexport function tabbableCount(): number {\n const selector = 'a[href], button:not([disabled]), input:not([disabled]):not([type=\"hidden\"]), select:not([disabled]), textarea:not([disabled]), summary, iframe, [tabindex]:not([tabindex=\"-1\"])';\n return Array.from(document.querySelectorAll(selector)).filter(isRendered).length;\n}\n\nconst PAGE_HELPERS = {\n isRendered, describeElement, accessibleName, phonePlacement, parseColour, compositeColour, contrastRatio, backgroundBehind,\n iconContrastProblems, findCallBar, quoteActions, actionOrder, heroRegion, outsidePoint, markElement, watchDisclosure,\n watchState, focusStep, focusStepAfterFrame, tabbableCount,\n};\n\n/** `window.__brandlabSiteSystem` in a page the journeys opened. */\nexport type SiteSystemPageHelpers = typeof PAGE_HELPERS;\n\n/** The init script that defines `window.__brandlabSiteSystem` in every document of the page. */\nexport function pageHelpersSource(): string {\n const names = Object.keys(PAGE_HELPERS);\n return `(() => {\\nconst SITE_SYSTEM_PHONE_PLACEMENTS = ${JSON.stringify(SITE_SYSTEM_PHONE_PLACEMENTS)};\\n${Object.values(PAGE_HELPERS).map((helper) => helper.toString()).join('\\n')}\\nwindow.__brandlabSiteSystem = { ${names.join(', ')} };\\n})();`;\n}\n"]}
1
+ {"version":3,"file":"page-helpers.js","sourceRoot":"","sources":["../../../src/site-system/journeys/page-helpers.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Functions the site-system journeys run inside the page under test. Each one is self-contained: it uses DOM globals\n * and the other helpers in this file only, because `pageHelpersSource()` injects their source into every document\n * (as `window.__brandlabSiteSystem`, through Playwright's init script, which a Content Security Policy does not\n * block). The pure parts (colour, contrast, placement) are unit-tested in Node.\n */\n\n/**\n * The placements `phone_link_click` accepts: site-next's PHONE_CALL_PLACEMENTS (conformance cannot depend on\n * site-next; a test reads the site-next source and compares). Injected into the page with the helpers.\n */\nexport const SITE_SYSTEM_PHONE_PLACEMENTS: readonly string[] = Object.freeze(['header', 'footer', 'hero', 'cta', 'fab', 'contact', 'location', 'page', 'emergency']);\n\nexport type Rgba = [number, number, number, number];\n\n/** What `watchDisclosure` has seen, in `performance.now()` milliseconds. */\nexport interface DisclosureWatch {\n enter: number | null;\n leave: number | null;\n opened: number | null;\n closed: number | null;\n open: boolean;\n}\n\n/**\n * Whether an element is rendered: connected, with a box, not `visibility: hidden`, not fully transparent and not\n * inside a closed `<details>` (outside its summary). Chromium gives the content of a closed `<details>` a box, so\n * the box alone is not enough; `checkVisibility()` settles it where the browser has it.\n */\nexport function isRendered(element: Element | null | undefined): boolean {\n if (!element || !element.isConnected) return false;\n for (let details = element.closest('details:not([open])'); details; details = details.parentElement?.closest('details:not([open])') ?? null) {\n const summary = Array.from(details.children).find((child) => child.tagName === 'SUMMARY');\n if (!summary || !summary.contains(element)) return false;\n }\n if (typeof element.checkVisibility === 'function' && !element.checkVisibility({ checkOpacity: true, checkVisibilityCSS: true })) return false;\n const rect = element.getBoundingClientRect();\n if (rect.width <= 0 || rect.height <= 0) return false;\n if (getComputedStyle(element).visibility !== 'visible') return false;\n for (let node: Element | null = element; node; node = node.parentElement) {\n if (getComputedStyle(node).opacity === '0') return false;\n }\n return true;\n}\n\n/** A short, readable name for an element in a failure message. */\nexport function describeElement(element: Element): string {\n const tag = element.tagName.toLowerCase();\n const label = element.getAttribute('aria-label') ?? (element as HTMLElement).innerText ?? element.textContent ?? '';\n const text = label.replace(/\\s+/g, ' ').trim().slice(0, 40);\n const href = element.getAttribute('href');\n return `${tag}${text ? ` \"${text}\"` : ''}${href ? ` (${href})` : ''}`;\n}\n\n/** The accessible name, approximately: aria-label, aria-labelledby, the rendered text, then the title. */\nexport function accessibleName(element: Element): string {\n const label = element.getAttribute('aria-label');\n if (label && label.trim()) return label.trim();\n const labelledBy = element.getAttribute('aria-labelledby');\n if (labelledBy) {\n const text = labelledBy.split(/\\s+/).map((id) => document.getElementById(id)?.textContent ?? '').join(' ').trim();\n if (text) return text.replace(/\\s+/g, ' ');\n }\n const text = ((element as HTMLElement).innerText ?? element.textContent ?? '').replace(/\\s+/g, ' ').trim();\n return text || (element.getAttribute('title') ?? '').trim();\n}\n\n/**\n * Where a telephone link reports its tap, exactly as site-next's shared listener resolves it: its own valid\n * `data-phone-location`, then a `<header>` or `<footer>` ancestor, then the nearest ancestor with a valid value,\n * otherwise `page`. `invalid` is the nearest declared value when it is not an allowed placement (the listener then\n * reports the link somewhere else, silently).\n */\nexport function phonePlacement(link: Element): { placement: string; invalid: string | null } {\n const valid = (node: Element): string | undefined => {\n const value = node.getAttribute('data-phone-location')?.trim().toLowerCase();\n return value && SITE_SYSTEM_PHONE_PLACEMENTS.includes(value) ? value : undefined;\n };\n const nearest = link.closest('[data-phone-location]');\n const invalid = nearest && !valid(nearest) ? nearest.getAttribute('data-phone-location') : null;\n const own = valid(link);\n if (own) return { placement: own, invalid };\n if (link.closest('header')) return { placement: 'header', invalid };\n if (link.closest('footer')) return { placement: 'footer', invalid };\n for (let node = link.parentElement?.closest('[data-phone-location]') ?? null; node; node = node.parentElement?.closest('[data-phone-location]') ?? null) {\n const value = valid(node);\n if (value) return { placement: value, invalid };\n }\n return { placement: 'page', invalid };\n}\n\n/** Parse a computed colour. `rgb()`/`rgba()` directly; any other syntax (oklch, lab, color()) through a canvas pixel. */\nexport function parseColour(value: string): Rgba | null {\n const text = value.trim().toLowerCase();\n if (text === 'transparent') return [0, 0, 0, 0];\n const match = /^rgba?\\(([^)]+)\\)$/.exec(text);\n if (match) {\n const parts = match[1]!.split(/[\\s,/]+/).filter(Boolean);\n if (parts.length < 3) return null;\n const channel = (part: string) => (part.endsWith('%') ? (Number.parseFloat(part) / 100) * 255 : Number.parseFloat(part));\n const alphaPart = parts[3];\n const alpha = alphaPart === undefined ? 1 : alphaPart.endsWith('%') ? Number.parseFloat(alphaPart) / 100 : Number.parseFloat(alphaPart);\n const rgba: Rgba = [channel(parts[0]!), channel(parts[1]!), channel(parts[2]!), alpha];\n return rgba.every((part) => Number.isFinite(part)) ? rgba : null;\n }\n if (typeof document === 'undefined') return null;\n const canvas = document.createElement('canvas');\n canvas.width = 1;\n canvas.height = 1;\n const context = canvas.getContext('2d', { willReadFrequently: true });\n if (!context) return null;\n context.clearRect(0, 0, 1, 1);\n context.fillStyle = value;\n context.fillRect(0, 0, 1, 1);\n const [red = 0, green = 0, blue = 0, alpha = 0] = Array.from(context.getImageData(0, 0, 1, 1).data);\n return [red, green, blue, alpha / 255];\n}\n\n/** `top` painted over an opaque `bottom`. */\nexport function compositeColour(top: Rgba, bottom: Rgba): Rgba {\n const alpha = top[3];\n return [\n top[0] * alpha + bottom[0] * (1 - alpha),\n top[1] * alpha + bottom[1] * (1 - alpha),\n top[2] * alpha + bottom[2] * (1 - alpha),\n 1,\n ];\n}\n\n/** The WCAG contrast ratio of two opaque colours. */\nexport function contrastRatio(first: Rgba, second: Rgba): number {\n const luminance = (colour: Rgba) => {\n const [red, green, blue] = colour.slice(0, 3).map((channel) => {\n const value = channel / 255;\n return value <= 0.04045 ? value / 12.92 : ((value + 0.055) / 1.055) ** 2.4;\n }) as [number, number, number];\n return 0.2126 * red + 0.7152 * green + 0.0722 * blue;\n };\n const [light, dark] = [luminance(first), luminance(second)].sort((left, right) => right - left) as [number, number];\n return (light + 0.05) / (dark + 0.05);\n}\n\n/**\n * The opaque colour behind an element, from its own and its ancestors' background colours over a white canvas.\n * `null` when a background image (a photo or gradient) is in the way: the contrast cannot be computed there.\n */\nexport function backgroundBehind(element: Element): Rgba | null {\n const layers: Rgba[] = [];\n for (let node: Element | null = element; node; node = node.parentElement) {\n const style = getComputedStyle(node);\n if (style.backgroundImage && style.backgroundImage !== 'none') return null;\n const colour = parseColour(style.backgroundColor);\n if (colour && colour[3] > 0) {\n layers.push(colour);\n if (colour[3] >= 1) break;\n }\n }\n return layers.reverse().reduce<Rgba>((bottom, top) => compositeColour(top, bottom), [255, 255, 255, 1]);\n}\n\n/**\n * Non-text contrast (WCAG 1.4.11) of icon-only controls inside `root`: a link or button with no visible text whose\n * icon is an SVG must reach 3:1 against what is behind it. Returns one line per failure; controls over a photo are\n * counted in `unmeasured`.\n */\nexport function iconContrastProblems(root: Element): { problems: string[]; checked: number; unmeasured: number } {\n const problems: string[] = [];\n let checked = 0;\n let unmeasured = 0;\n const paintOf = (svg: SVGElement): Rgba | null => {\n for (const shape of Array.from(svg.querySelectorAll('path, circle, rect, ellipse, line, polyline, polygon, text, use'))) {\n if (!isRendered(shape)) continue;\n const style = getComputedStyle(shape);\n for (const [paint, opacity] of [[style.fill, style.fillOpacity], [style.stroke, style.strokeOpacity]] as const) {\n if (!paint || paint === 'none' || paint.startsWith('url(')) continue;\n const colour = parseColour(paint);\n const alpha = colour ? colour[3] * Number(opacity || 1) * Number(style.opacity || 1) : 0;\n if (colour && alpha > 0) return [colour[0], colour[1], colour[2], alpha];\n }\n }\n return null;\n };\n for (const control of Array.from(root.querySelectorAll('a[href], button'))) {\n if (!isRendered(control)) continue;\n if (((control as HTMLElement).innerText ?? '').trim()) continue;\n const svg = control.querySelector('svg');\n if (!svg || !isRendered(svg)) continue;\n const paint = paintOf(svg);\n if (!paint) continue;\n const background = backgroundBehind(svg);\n if (!background) {\n unmeasured += 1;\n continue;\n }\n checked += 1;\n const ratio = contrastRatio(compositeColour(paint, background), background);\n if (ratio < 3) problems.push(`${describeElement(control)}: its icon is ${ratio.toFixed(2)}:1 against its background (needs 3:1)`);\n }\n return { problems, checked, unmeasured };\n}\n\n/**\n * The phone call bar: the shared `MobileCallBar`, or any fixed or sticky element along the bottom of the screen that\n * holds a visible telephone link (outside the header and dialogs). `null` when none is on screen.\n */\nexport function findCallBar(): Element | null {\n const tagged = Array.from(document.querySelectorAll('[data-bl=\"call-bar\"], [data-bl=\"callbar\"], [data-bl=\"mobile-call-bar\"]')).find(isRendered);\n if (tagged) return tagged;\n const height = window.innerHeight;\n for (const element of Array.from(document.body.querySelectorAll('*'))) {\n const position = getComputedStyle(element).position;\n if (position !== 'fixed' && position !== 'sticky') continue;\n if (element.closest('header, dialog, [role=\"dialog\"]')) continue;\n const rect = element.getBoundingClientRect();\n if (rect.height <= 0 || rect.bottom < height - 4 || rect.top < height / 2) continue;\n if (!isRendered(element)) continue;\n if (Array.from(element.querySelectorAll('a[href^=\"tel:\"]')).some(isRendered)) return element;\n }\n return null;\n}\n\n/** Visible quote actions in `root`: links or buttons named like the quote action, or the shared quote hooks. */\nexport function quoteActions(root: Element, name: { source: string; flags: string }): Element[] {\n const pattern = new RegExp(name.source, name.flags);\n return Array.from(root.querySelectorAll('a[href], button')).filter((element) => {\n if (!isRendered(element) || element.getAttribute('href')?.startsWith('tel:')) return false;\n if (element.matches('[data-bl=\"quote-button\"], [data-bl-cta^=\"quote\"], [data-lead-cta^=\"quote\"]')) return true;\n return pattern.test(accessibleName(element));\n });\n}\n\n/** The order of the first visible phone link and quote action in `root`. */\nexport function actionOrder(root: Element, name: { source: string; flags: string }): 'phone-first' | 'quote-first' | 'incomplete' {\n const phone = Array.from(root.querySelectorAll('a[href^=\"tel:\"]')).find(isRendered);\n const quote = quoteActions(root, name)[0];\n if (!phone || !quote) return 'incomplete';\n return phone.compareDocumentPosition(quote) & Node.DOCUMENT_POSITION_FOLLOWING ? 'phone-first' : 'quote-first';\n}\n\n/**\n * The hero: the shared hero section, else the smallest block around the page's visible H1 that holds a phone link\n * and a quote action, never above the H1's own section and never `<main>` (without a section, never a block holding\n * an H2). When none holds both, the H1's section (or its parent): its order then reads \"incomplete\".\n */\nexport function heroRegion(name: { source: string; flags: string }): Element | null {\n const heading = Array.from(document.querySelectorAll('h1')).find(isRendered);\n if (!heading) return null;\n const tagged = heading.closest('[data-bl-section=\"hero\"]');\n if (tagged) return tagged;\n const section = heading.closest('section');\n const fallback = section ?? heading.parentElement;\n for (let node = heading.parentElement; node && node !== document.body && node.tagName !== 'MAIN'; node = node.parentElement) {\n if (!section && node.querySelector('h2')) break;\n if (Array.from(node.querySelectorAll('a[href^=\"tel:\"]')).some(isRendered) && quoteActions(node, name).length > 0) return node;\n if (node === section) break;\n }\n return fallback && fallback.tagName !== 'MAIN' ? fallback : heading;\n}\n\n/** A point in the page that is not on an interactive element, a menu or anything in `avoid`. */\nexport function outsidePoint(avoid: Element[]): { x: number; y: number } | null {\n const interactive = 'a[href], button, input, select, textarea, label, summary, details, iframe, video, audio, [role=\"button\"], [role=\"link\"], [role=\"menuitem\"], [role=\"tab\"], [tabindex], [contenteditable=\"\"], [contenteditable=\"true\"]';\n for (let row = 9; row >= 3; row -= 1) {\n for (let column = 1; column <= 9; column += 1) {\n const x = Math.round((window.innerWidth * column) / 10);\n const y = Math.round((window.innerHeight * row) / 10);\n const element = document.elementFromPoint(x, y);\n if (!element || avoid.some((region) => region.contains(element))) continue;\n if (element.closest(interactive) || element.closest('header, nav, dialog, [role=\"dialog\"]')) continue;\n return { x, y };\n }\n }\n return null;\n}\n\n/** Tag an element so Playwright and axe can find it by `[data-brandlab-journey~=\"<name>\"]`. */\nexport function markElement(element: Element, name: string): void {\n const names = new Set((element.getAttribute('data-brandlab-journey') ?? '').split(/\\s+/).filter(Boolean));\n names.add(name);\n element.setAttribute('data-brandlab-journey', [...names].join(' '));\n}\n\n/**\n * Watch one disclosure: when the pointer enters and leaves its item (and panel), and when the panel it controls\n * becomes rendered or hidden, timed with `performance.now()`. Attribute and tree changes are sampled at once; style\n * changes without a DOM change (CSS `:hover`) every animation frame.\n */\nexport function watchDisclosure(trigger: Element, panelId: string): void {\n const item = trigger.closest('li') ?? trigger.parentElement ?? trigger;\n const panel = () => document.getElementById(panelId);\n const inside = (node: EventTarget | null) => node instanceof Node && (item.contains(node) || Boolean(panel()?.contains(node)));\n const state: DisclosureWatch = { enter: null, leave: null, opened: null, closed: null, open: isRendered(panel()) };\n const sample = () => {\n const open = isRendered(panel());\n if (open === state.open) return;\n state.open = open;\n if (open && state.opened === null) state.opened = performance.now();\n if (!open && state.closed === null) state.closed = performance.now();\n };\n const onOver = (event: Event) => {\n if (state.enter === null && inside(event.target)) state.enter = performance.now();\n };\n const onOut = (event: Event) => {\n if (state.leave === null && inside(event.target) && !inside((event as PointerEvent).relatedTarget)) state.leave = performance.now();\n };\n const observer = new MutationObserver(sample);\n observer.observe(document.documentElement, { attributes: true, childList: true, subtree: true });\n let frame = 0;\n const loop = () => {\n sample();\n frame = requestAnimationFrame(loop);\n };\n loop();\n document.addEventListener('pointerover', onOver, true);\n document.addEventListener('pointerout', onOut, true);\n const previous = (window as unknown as { __brandlabSiteSystemWatch?: { stop(): void } }).__brandlabSiteSystemWatch;\n previous?.stop();\n (window as unknown as { __brandlabSiteSystemWatch: unknown }).__brandlabSiteSystemWatch = {\n state,\n stop() {\n observer.disconnect();\n cancelAnimationFrame(frame);\n document.removeEventListener('pointerover', onOver, true);\n document.removeEventListener('pointerout', onOut, true);\n },\n };\n}\n\n/** The current state of `watchDisclosure`'s watch. */\nexport function watchState(): DisclosureWatch | null {\n const watch = (window as unknown as { __brandlabSiteSystemWatch?: { state: DisclosureWatch } }).__brandlabSiteSystemWatch;\n return watch ? { ...watch.state } : null;\n}\n\n/**\n * After a Tab press: is the focused element fully hidden under the sticky header or the call bar? Samples nine points\n * across the visible part of its box; it is hidden when the header or call bar is on top at every one. `repeat` is\n * true once focus comes back to an element it has already visited.\n */\nexport function focusStep(): { done: boolean; repeat: boolean; obscured: string | null; covered: 'header' | 'call bar' | null } {\n const element = document.activeElement;\n const store = window as unknown as { __brandlabSiteSystemFocus?: WeakSet<Element> };\n store.__brandlabSiteSystemFocus ??= new WeakSet<Element>();\n if (!element || element === document.body || element === document.documentElement) return { done: true, repeat: false, obscured: null, covered: null };\n const repeat = store.__brandlabSiteSystemFocus.has(element);\n store.__brandlabSiteSystemFocus.add(element);\n const header = document.querySelector('[data-brandlab-journey~=\"header\"]')\n ?? Array.from(document.querySelectorAll('[data-bl=\"header\"], header')).find((candidate) => isRendered(candidate) && !candidate.closest('main, article, section, aside, footer'))\n ?? null;\n const callBar = findCallBar();\n if (header?.contains(element) || callBar?.contains(element)) return { done: false, repeat, obscured: null, covered: null };\n const rect = element.getBoundingClientRect();\n const left = Math.max(rect.left, 0);\n const right = Math.min(rect.right, window.innerWidth);\n const top = Math.max(rect.top, 0);\n const bottom = Math.min(rect.bottom, window.innerHeight);\n if (right - left < 1 || bottom - top < 1) return { done: false, repeat, obscured: null, covered: null };\n let coveredBy: 'header' | 'call bar' | null = null;\n for (const fx of [0.15, 0.5, 0.85]) {\n for (const fy of [0.15, 0.5, 0.85]) {\n const hit = document.elementFromPoint(left + (right - left) * fx, top + (bottom - top) * fy);\n if (!hit || element.contains(hit)) return { done: false, repeat, obscured: null, covered: null };\n if (header?.contains(hit)) coveredBy ??= 'header';\n else if (callBar?.contains(hit)) coveredBy ??= 'call bar';\n else return { done: false, repeat, obscured: null, covered: null };\n }\n }\n return { done: false, repeat, obscured: describeElement(element), covered: coveredBy };\n}\n\n/**\n * `focusStep` one animation frame after the focus moved (capped at 50 ms, since a background page may not paint), so\n * a sticky bar that hides on its observer's next frame is measured after it moved, not mid-transition.\n */\nexport function focusStepAfterFrame(): Promise<ReturnType<typeof focusStep>> {\n return new Promise((resolve) => {\n let settled = false;\n const finish = () => {\n if (settled) return;\n settled = true;\n resolve(focusStep());\n };\n requestAnimationFrame(() => finish());\n setTimeout(finish, 50);\n });\n}\n\n/** Elements a Tab press can reach, roughly: enough to bound the K5 walk. */\nexport function tabbableCount(): number {\n const selector = 'a[href], button:not([disabled]), input:not([disabled]):not([type=\"hidden\"]), select:not([disabled]), textarea:not([disabled]), summary, iframe, [tabindex]:not([tabindex=\"-1\"])';\n return Array.from(document.querySelectorAll(selector)).filter(isRendered).length;\n}\n\n/** What a K14 block paints: an opaque colour, a photo or gradient (never equal to another block's), or unknown. */\nexport interface PaintedGround {\n description: string;\n /** `rgb(r, g, b)`, `image` or `unknown`. */\n ground: string;\n}\n\n/**\n * The ground a top-level block paints (K14, 3.19.0): its own background image (a photo band or gradient), a photo\n * covering most of it (a fill image), else its background colour composited over what shows through from its\n * ancestors. A transparent block whose one child covers it and paints a colour takes the child's.\n */\nexport function paintedGround(element: Element): string {\n const box = element.getBoundingClientRect();\n const area = box.width * box.height;\n if (area <= 0) return 'unknown';\n if (getComputedStyle(element).backgroundImage !== 'none') return 'image';\n for (const media of Array.from(element.querySelectorAll('img, picture, video'))) {\n const rect = media.getBoundingClientRect();\n if (rect.width * rect.height >= 0.8 * area && isRendered(media)) return 'image';\n }\n let painter: Element = element;\n const own = parseColour(getComputedStyle(element).backgroundColor);\n if (!own || own[3] === 0) {\n const cover = Array.from(element.children).find((child) => {\n const rect = child.getBoundingClientRect();\n const colour = parseColour(getComputedStyle(child).backgroundColor);\n return rect.width * rect.height >= 0.9 * area && colour !== null && colour[3] > 0;\n });\n if (cover) {\n if (getComputedStyle(cover).backgroundImage !== 'none') return 'image';\n painter = cover;\n }\n }\n const colour = backgroundBehind(painter);\n if (!colour) return 'image';\n return `rgb(${colour.slice(0, 3).map((channel) => Math.round(channel)).join(', ')})`;\n}\n\n/**\n * The page's top-level blocks in order (K14): each rendered child of `main` (through a lone wrapper), then what follows\n * `main` up to and including the footer. A `[data-bl-inset]` block is part of the place before it, so it is left out.\n */\nexport function groundBlocks(): PaintedGround[] {\n const main = document.querySelector('main');\n if (!main) return [];\n const usable = (element: Element) => !['SCRIPT', 'STYLE', 'TEMPLATE', 'NOSCRIPT', 'LINK'].includes(element.tagName)\n && !element.hasAttribute('data-bl-inset') && isRendered(element) && element.getBoundingClientRect().height >= 8;\n let blocks = Array.from(main.children).filter(usable);\n while (blocks.length === 1 && Array.from(blocks[0]!.children).filter(usable).length > 1 && paintedGround(blocks[0]!).startsWith('rgb') === false) {\n blocks = Array.from(blocks[0]!.children).filter(usable);\n }\n const footer = document.querySelector('footer[data-bl=\"footer\"], body footer:not(main footer)');\n for (let node = main.nextElementSibling; node; node = node.nextElementSibling) {\n if (usable(node)) blocks.push(node);\n if (node === footer || node.contains(footer)) break;\n }\n if (footer && !blocks.some((block) => block === footer || block.contains(footer)) && isRendered(footer)) blocks.push(footer);\n return blocks.map((block) => {\n const section = block.getAttribute('data-bl-section') ?? (block.tagName === 'FOOTER' ? 'footer' : null);\n const description = section ? `\"${section}\"` : `${block.tagName.toLowerCase()}${block.id ? `#${block.id}` : ''}`;\n return { description, ground: paintedGround(block) };\n });\n}\n\n/** Neighbouring blocks that paint the same colour (K14): a photo or an unknown ground never matches. */\nexport function groundProblems(path: string, blocks: readonly PaintedGround[]): string[] {\n const problems: string[] = [];\n const channels = (ground: string) => ground.slice(4, -1).split(',').map(Number);\n for (let index = 1; index < blocks.length; index += 1) {\n const [before, after] = [blocks[index - 1]!, blocks[index]!];\n if (!before.ground.startsWith('rgb') || !after.ground.startsWith('rgb')) continue;\n const [left, right] = [channels(before.ground), channels(after.ground)];\n if (left.every((channel, at) => Math.abs(channel - right[at]!) <= 2)) {\n problems.push(`${path}: ${before.description} and ${after.description} share the background ${after.ground}`);\n }\n }\n return problems;\n}\n\nconst PAGE_HELPERS = {\n isRendered, describeElement, accessibleName, phonePlacement, parseColour, compositeColour, contrastRatio, backgroundBehind,\n iconContrastProblems, findCallBar, quoteActions, actionOrder, heroRegion, outsidePoint, markElement, watchDisclosure,\n watchState, focusStep, focusStepAfterFrame, tabbableCount, paintedGround, groundBlocks, groundProblems,\n};\n\n/** `window.__brandlabSiteSystem` in a page the journeys opened. */\nexport type SiteSystemPageHelpers = typeof PAGE_HELPERS;\n\n/** The init script that defines `window.__brandlabSiteSystem` in every document of the page. */\nexport function pageHelpersSource(): string {\n const names = Object.keys(PAGE_HELPERS);\n return `(() => {\\nconst SITE_SYSTEM_PHONE_PLACEMENTS = ${JSON.stringify(SITE_SYSTEM_PHONE_PLACEMENTS)};\\n${Object.values(PAGE_HELPERS).map((helper) => helper.toString()).join('\\n')}\\nwindow.__brandlabSiteSystem = { ${names.join(', ')} };\\n})();`;\n}\n"]}
@@ -5,7 +5,7 @@ type JourneyCheck = Extract<(typeof SITE_SYSTEM_CHECKS)[number], {
5
5
  }>;
6
6
  export type SiteSystemJourneyId = JourneyCheck['id'];
7
7
  /** Every journey check, in the order the journeys register. */
8
- export declare const SITE_SYSTEM_JOURNEY_IDS: readonly ["N3", "N4", "N5", "N6", "N8", "K3", "K4", "K5", "K6", "C4", "W2", "C1", "W9"];
8
+ export declare const SITE_SYSTEM_JOURNEY_IDS: readonly ["N3", "N4", "N5", "N6", "N8", "K3", "K4", "K5", "K6", "C4", "W2", "K14", "C1", "W9"];
9
9
  /** Desktop widths for N6: both ends and the middle of the compact band, the desktop breakpoint and two wide screens. */
10
10
  export declare const SITE_SYSTEM_DESKTOP_WIDTHS: readonly number[];
11
11
  export declare const SITE_SYSTEM_PHONE_VIEWPORT: Readonly<{
@@ -40,6 +40,11 @@ export interface BrandlabSiteSystemTestOptions {
40
40
  money?: string | readonly string[];
41
41
  contact?: string;
42
42
  families?: string | readonly string[];
43
+ /**
44
+ * More pages for K14 (3.19.0), one per page family the other lists don't cover, for example `/about` and a guide.
45
+ * K14 also runs on home, the money pages, the contact page and the family pages.
46
+ */
47
+ grounds?: string | readonly string[];
43
48
  };
44
49
  /** Desktop widths for N6, each 1024 px or more (default 1024, 1100, 1280, 1440 and 1920). */
45
50
  desktopWidths?: readonly number[];
@@ -69,6 +74,8 @@ export interface SiteSystemJourneyPlan {
69
74
  families: readonly string[];
70
75
  /** Home, the money pages and the contact page, without repeats: K5, K6 and C4 run on each. */
71
76
  sampled: readonly string[];
77
+ /** The sampled pages, the family pages and `pages.grounds`, without repeats: K14 runs on each. */
78
+ grounds: readonly string[];
72
79
  desktopWidths: readonly number[];
73
80
  actionOrder: SiteSystemActionOrder;
74
81
  quoteName: RegExp;
@@ -1 +1 @@
1
- {"version":3,"file":"plan.d.ts","sourceRoot":"","sources":["../../../src/site-system/journeys/plan.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"plan.d.ts","sourceRoot":"","sources":["../../../src/site-system/journeys/plan.ts"],"names":[],"mappings":"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"}
@@ -6,7 +6,7 @@ import { BRANDLAB_CURRENT_RUNTIME_RELEASE } from '../../current-release.js';
6
6
  import { detectBrandlabCanaryRelease, detectInstalledBrandlabRelease } from '../../project.js';
7
7
  import { SITE_SYSTEM_CHECKS, SITE_SYSTEM_TIERS, siteSystemReleaseRank } from '../checks.js';
8
8
  /** Every journey check, in the order the journeys register. */
9
- export const SITE_SYSTEM_JOURNEY_IDS = Object.freeze(['N3', 'N4', 'N5', 'N6', 'N8', 'K3', 'K4', 'K5', 'K6', 'C4', 'W2', 'C1', 'W9']);
9
+ export const SITE_SYSTEM_JOURNEY_IDS = Object.freeze(['N3', 'N4', 'N5', 'N6', 'N8', 'K3', 'K4', 'K5', 'K6', 'C4', 'W2', 'K14', 'C1', 'W9']);
10
10
  /** Desktop widths for N6: both ends and the middle of the compact band, the desktop breakpoint and two wide screens. */
11
11
  export const SITE_SYSTEM_DESKTOP_WIDTHS = Object.freeze([1024, 1100, 1280, 1440, 1920]);
12
12
  export const SITE_SYSTEM_PHONE_VIEWPORT = Object.freeze({ width: 390, height: 844 });
@@ -74,6 +74,12 @@ export function planBrandlabSiteSystemJourneys(options) {
74
74
  if (problem)
75
75
  errors.push(problem);
76
76
  });
77
+ const grounds = pages.grounds === undefined ? [] : typeof pages.grounds === 'string' ? [pages.grounds] : [...pages.grounds];
78
+ grounds.forEach((path, index) => {
79
+ const problem = pathProblem(`pages.grounds[${index}]`, path);
80
+ if (problem)
81
+ errors.push(problem);
82
+ });
77
83
  const desktopWidths = input.desktopWidths?.length ? [...input.desktopWidths] : [...SITE_SYSTEM_DESKTOP_WIDTHS];
78
84
  for (const width of desktopWidths) {
79
85
  if (!Number.isInteger(width) || width < 1024 || width > 3840)
@@ -118,6 +124,7 @@ export function planBrandlabSiteSystemJourneys(options) {
118
124
  contact: pages.contact,
119
125
  families: [...new Set(families)],
120
126
  sampled: [...new Set([pages.home, ...money, ...(pages.contact ? [pages.contact] : [])])],
127
+ grounds: [...new Set([pages.home, ...money, ...(pages.contact ? [pages.contact] : []), ...families, ...grounds])],
121
128
  desktopWidths,
122
129
  actionOrder: input.actionOrder ?? 'phone-first',
123
130
  quoteName,
@@ -1 +1 @@
1
- {"version":3,"file":"plan.js","sourceRoot":"","sources":["../../../src/site-system/journeys/plan.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Options and planning for the site-system journeys: which journeys a tier registers, the sampled pages and the\n * defaults. Pure, so it is unit-tested without a browser.\n */\nimport { BRANDLAB_CURRENT_RUNTIME_RELEASE } from '../../current-release.js';\nimport { detectBrandlabCanaryRelease, detectInstalledBrandlabRelease } from '../../project.js';\nimport { SITE_SYSTEM_CHECKS, SITE_SYSTEM_TIERS, siteSystemReleaseRank, type SiteSystemTier } from '../checks.js';\n\ntype JourneyCheck = Extract<(typeof SITE_SYSTEM_CHECKS)[number], { kind: 'journey' }>;\nexport type SiteSystemJourneyId = JourneyCheck['id'];\n\n/** Every journey check, in the order the journeys register. */\nexport const SITE_SYSTEM_JOURNEY_IDS = Object.freeze(['N3', 'N4', 'N5', 'N6', 'N8', 'K3', 'K4', 'K5', 'K6', 'C4', 'W2', 'C1', 'W9'] as const satisfies readonly SiteSystemJourneyId[]);\n\n/** Desktop widths for N6: both ends and the middle of the compact band, the desktop breakpoint and two wide screens. */\nexport const SITE_SYSTEM_DESKTOP_WIDTHS = Object.freeze([1024, 1100, 1280, 1440, 1920]);\nexport const SITE_SYSTEM_PHONE_VIEWPORT = Object.freeze({ width: 390, height: 844 });\nexport const SITE_SYSTEM_DESKTOP_VIEWPORT = Object.freeze({ width: 1440, height: 900 });\n\n/** The quote action's accessible name when the site does not record one. */\nexport const SITE_SYSTEM_QUOTE_NAME = /\\bquote\\b/i;\n\nexport type SiteSystemActionOrder = 'phone-first' | 'quote-first';\n\nexport interface BrandlabSiteSystemTestOptions {\n /** The adoption tier from brandlab.config.ts (`siteSystem.tier`). C1 registers from `enquiry`. */\n tier: SiteSystemTier;\n /**\n * The release whose journeys register. `defineBrandlabSiteSystemTests` defaults it to the site's installed Brandlab\n * release, read from its package.json as `brandlab-ready` reads it (one exact version, or one canary such as\n * `3.15.0-canary.2`), else this conformance release. A journey registers only from the release that brings it, so a\n * later journey never turns a site's e2e run red before the gate blocks on it, and a check that blocks has its\n * journey. Name a later release to preview its journeys.\n */\n release?: string;\n /**\n * The sampled pages: home, the money pages (service pages with the enquiry form) and the contact page. `families`\n * (W9, full tier from 3.13.0) names one built page per dynamic route family, such as `/services/blocked-drains` and\n * `/locations/ryde`: its URL variants are checked.\n */\n pages: { home: string; money?: string | readonly string[]; contact?: string; families?: string | readonly string[] };\n /** Desktop widths for N6, each 1024 px or more (default 1024, 1100, 1280, 1440 and 1920). */\n desktopWidths?: readonly number[];\n /** The recorded order of the phone and quote actions (K6), default `phone-first`. */\n actionOrder?: SiteSystemActionOrder;\n /** The quote action's accessible name (default /\\bquote\\b/i), for a recorded label such as \"Book an inspection\". */\n quoteName?: string | RegExp;\n /** The main menu's accessible name (default \"Main\"). */\n navName?: string;\n /** The phone menu button's accessible name (default \"Menu\"). */\n menuName?: string | RegExp;\n /** How long a document may take to load, default 45 seconds; a timeout is retried once. */\n navigationTimeoutMs?: number;\n /** How long to wait for the network to settle after a load, default 10 seconds; a busy network is not a failure. */\n settleTimeoutMs?: number;\n /** The most Tab presses K5 makes on one page (default 400); a longer page is reported, not failed. */\n maxTabStops?: number;\n}\n\nexport interface SiteSystemJourneyPlan {\n tier: SiteSystemTier;\n release: string;\n journeys: readonly SiteSystemJourneyId[];\n home: string;\n money: readonly string[];\n contact: string | undefined;\n /** One page per dynamic route family, for W9's URL variants. */\n families: readonly string[];\n /** Home, the money pages and the contact page, without repeats: K5, K6 and C4 run on each. */\n sampled: readonly string[];\n desktopWidths: readonly number[];\n actionOrder: SiteSystemActionOrder;\n quoteName: RegExp;\n navName: string;\n menuName: string | RegExp;\n navigationTimeoutMs: number;\n settleTimeoutMs: number;\n maxTabStops: number;\n /** Gaps that fail a journey when it runs (fail closed), rather than stopping the whole spec file. */\n problems: Partial<Record<SiteSystemJourneyId, string>>;\n}\n\nexport function siteSystemJourneyTitle(id: SiteSystemJourneyId, text: string): string {\n return `[${id}] ${text}`;\n}\n\n/**\n * The journey checks a tier registers on a release: its own and the tiers below it, each from the release that brings\n * it (default: this conformance release).\n */\nexport function siteSystemJourneysFor(tier: SiteSystemTier, release: string = BRANDLAB_CURRENT_RUNTIME_RELEASE.releaseVersion): SiteSystemJourneyId[] {\n const rank = SITE_SYSTEM_TIERS.indexOf(tier);\n const releaseRank = siteSystemReleaseRank(release);\n const journeys = new Map<string, { tier: string; from: string }>(SITE_SYSTEM_CHECKS.filter((check) => check.kind === 'journey').map((check) => [check.id, check]));\n return SITE_SYSTEM_JOURNEY_IDS.filter((id) => {\n const check = journeys.get(id);\n return check !== undefined\n && SITE_SYSTEM_TIERS.indexOf(check.tier as SiteSystemTier) <= rank\n && siteSystemReleaseRank(check.from) <= releaseRank;\n });\n}\n\n/**\n * The release the journeys register for on a site: its installed Brandlab release as `brandlab-ready` reads it (all\n * four packages at one exact version, or one canary, from package.json), else this conformance release. A canary's\n * own release manifest still names the last published release, so without this its new journeys (W2 and W9 on\n * 3.15.0-canary.2) would not register while the gate blocks on them.\n */\nexport function siteSystemJourneyRelease(packageJson: Parameters<typeof detectInstalledBrandlabRelease>[0]): string {\n return detectInstalledBrandlabRelease(packageJson) ?? detectBrandlabCanaryRelease(packageJson)?.version ?? BRANDLAB_CURRENT_RUNTIME_RELEASE.releaseVersion;\n}\n\nfunction pathProblem(label: string, path: unknown): string | null {\n if (typeof path !== 'string' || !path.startsWith('/') || path.startsWith('//') || /\\s/.test(path)) {\n return `${label} must be a site path starting with \"/\" (got ${JSON.stringify(path)})`;\n }\n return null;\n}\n\n/** Validate the options and fill the defaults. Malformed options throw, listing every problem. */\nexport function planBrandlabSiteSystemJourneys(options: BrandlabSiteSystemTestOptions): SiteSystemJourneyPlan {\n const errors: string[] = [];\n const input = (options ?? {}) as Partial<BrandlabSiteSystemTestOptions>;\n if (!SITE_SYSTEM_TIERS.includes(input.tier as SiteSystemTier)) {\n errors.push(`tier must be one of ${SITE_SYSTEM_TIERS.join(', ')} (got ${JSON.stringify(input.tier)})`);\n }\n const pages = input.pages ?? ({} as BrandlabSiteSystemTestOptions['pages']);\n const homeProblem = pathProblem('pages.home', pages.home);\n if (homeProblem) errors.push(homeProblem);\n const money = pages.money === undefined ? [] : typeof pages.money === 'string' ? [pages.money] : [...pages.money];\n money.forEach((path, index) => {\n const problem = pathProblem(`pages.money[${index}]`, path);\n if (problem) errors.push(problem);\n });\n if (pages.contact !== undefined) {\n const problem = pathProblem('pages.contact', pages.contact);\n if (problem) errors.push(problem);\n }\n const families = pages.families === undefined ? [] : typeof pages.families === 'string' ? [pages.families] : [...pages.families];\n families.forEach((path, index) => {\n const problem = pathProblem(`pages.families[${index}]`, path) ?? (/[?#]/.test(path) || path === '/' ? `pages.families[${index}] must be a page path below home with no query (got ${JSON.stringify(path)})` : null);\n if (problem) errors.push(problem);\n });\n const desktopWidths = input.desktopWidths?.length ? [...input.desktopWidths] : [...SITE_SYSTEM_DESKTOP_WIDTHS];\n for (const width of desktopWidths) {\n if (!Number.isInteger(width) || width < 1024 || width > 3840) errors.push(`desktopWidths must be whole pixel widths from 1024 to 3840 (got ${width})`);\n }\n if (input.actionOrder !== undefined && input.actionOrder !== 'phone-first' && input.actionOrder !== 'quote-first') {\n errors.push(`actionOrder must be phone-first or quote-first (got ${JSON.stringify(input.actionOrder)})`);\n }\n for (const key of ['navigationTimeoutMs', 'settleTimeoutMs', 'maxTabStops'] as const) {\n const value = input[key];\n if (value !== undefined && (!Number.isInteger(value) || value <= 0)) errors.push(`${key} must be a positive whole number (got ${value})`);\n }\n if (input.release !== undefined && (typeof input.release !== 'string' || !/^\\d+\\.\\d+\\.\\d+(?:-canary\\.\\d+)?$/.test(input.release))) {\n errors.push(`release must be a version such as 3.9.0 or 3.15.0-canary.2 (got ${JSON.stringify(input.release)})`);\n }\n if (input.navName !== undefined && (typeof input.navName !== 'string' || !input.navName.trim())) errors.push('navName must be a non-empty string');\n if (errors.length > 0) throw new TypeError(`defineBrandlabSiteSystemTests: ${errors.join('; ')}`);\n\n const tier = input.tier as SiteSystemTier;\n const release = input.release ?? BRANDLAB_CURRENT_RUNTIME_RELEASE.releaseVersion;\n const journeys = siteSystemJourneysFor(tier, release);\n const problems: SiteSystemJourneyPlan['problems'] = {};\n if (journeys.includes('C1') && money.length === 0) {\n problems.C1 = 'pages.money names no money page: C1 checks the first screen of each service page with the enquiry form';\n }\n if (journeys.includes('W9') && families.length === 0) {\n problems.W9 = 'pages.families names no page: W9 checks the URL variants of one built page per dynamic route family';\n }\n const quoteName = input.quoteName === undefined\n ? SITE_SYSTEM_QUOTE_NAME\n : input.quoteName instanceof RegExp\n ? input.quoteName\n : new RegExp(`^\\\\s*${input.quoteName.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')}\\\\s*$`, 'i');\n return {\n tier,\n release,\n journeys,\n home: pages.home,\n money,\n contact: pages.contact,\n families: [...new Set(families)],\n sampled: [...new Set([pages.home, ...money, ...(pages.contact ? [pages.contact] : [])])],\n desktopWidths,\n actionOrder: input.actionOrder ?? 'phone-first',\n quoteName,\n navName: input.navName ?? 'Main',\n menuName: input.menuName ?? 'Menu',\n navigationTimeoutMs: input.navigationTimeoutMs ?? 45_000,\n settleTimeoutMs: input.settleTimeoutMs ?? 10_000,\n maxTabStops: input.maxTabStops ?? 400,\n problems,\n };\n}\n"]}
1
+ {"version":3,"file":"plan.js","sourceRoot":"","sources":["../../../src/site-system/journeys/plan.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Options and planning for the site-system journeys: which journeys a tier registers, the sampled pages and the\n * defaults. Pure, so it is unit-tested without a browser.\n */\nimport { BRANDLAB_CURRENT_RUNTIME_RELEASE } from '../../current-release.js';\nimport { detectBrandlabCanaryRelease, detectInstalledBrandlabRelease } from '../../project.js';\nimport { SITE_SYSTEM_CHECKS, SITE_SYSTEM_TIERS, siteSystemReleaseRank, type SiteSystemTier } from '../checks.js';\n\ntype JourneyCheck = Extract<(typeof SITE_SYSTEM_CHECKS)[number], { kind: 'journey' }>;\nexport type SiteSystemJourneyId = JourneyCheck['id'];\n\n/** Every journey check, in the order the journeys register. */\nexport const SITE_SYSTEM_JOURNEY_IDS = Object.freeze(['N3', 'N4', 'N5', 'N6', 'N8', 'K3', 'K4', 'K5', 'K6', 'C4', 'W2', 'K14', 'C1', 'W9'] as const satisfies readonly SiteSystemJourneyId[]);\n\n/** Desktop widths for N6: both ends and the middle of the compact band, the desktop breakpoint and two wide screens. */\nexport const SITE_SYSTEM_DESKTOP_WIDTHS = Object.freeze([1024, 1100, 1280, 1440, 1920]);\nexport const SITE_SYSTEM_PHONE_VIEWPORT = Object.freeze({ width: 390, height: 844 });\nexport const SITE_SYSTEM_DESKTOP_VIEWPORT = Object.freeze({ width: 1440, height: 900 });\n\n/** The quote action's accessible name when the site does not record one. */\nexport const SITE_SYSTEM_QUOTE_NAME = /\\bquote\\b/i;\n\nexport type SiteSystemActionOrder = 'phone-first' | 'quote-first';\n\nexport interface BrandlabSiteSystemTestOptions {\n /** The adoption tier from brandlab.config.ts (`siteSystem.tier`). C1 registers from `enquiry`. */\n tier: SiteSystemTier;\n /**\n * The release whose journeys register. `defineBrandlabSiteSystemTests` defaults it to the site's installed Brandlab\n * release, read from its package.json as `brandlab-ready` reads it (one exact version, or one canary such as\n * `3.15.0-canary.2`), else this conformance release. A journey registers only from the release that brings it, so a\n * later journey never turns a site's e2e run red before the gate blocks on it, and a check that blocks has its\n * journey. Name a later release to preview its journeys.\n */\n release?: string;\n /**\n * The sampled pages: home, the money pages (service pages with the enquiry form) and the contact page. `families`\n * (W9, full tier from 3.13.0) names one built page per dynamic route family, such as `/services/blocked-drains` and\n * `/locations/ryde`: its URL variants are checked.\n */\n pages: {\n home: string;\n money?: string | readonly string[];\n contact?: string;\n families?: string | readonly string[];\n /**\n * More pages for K14 (3.19.0), one per page family the other lists don't cover, for example `/about` and a guide.\n * K14 also runs on home, the money pages, the contact page and the family pages.\n */\n grounds?: string | readonly string[];\n };\n /** Desktop widths for N6, each 1024 px or more (default 1024, 1100, 1280, 1440 and 1920). */\n desktopWidths?: readonly number[];\n /** The recorded order of the phone and quote actions (K6), default `phone-first`. */\n actionOrder?: SiteSystemActionOrder;\n /** The quote action's accessible name (default /\\bquote\\b/i), for a recorded label such as \"Book an inspection\". */\n quoteName?: string | RegExp;\n /** The main menu's accessible name (default \"Main\"). */\n navName?: string;\n /** The phone menu button's accessible name (default \"Menu\"). */\n menuName?: string | RegExp;\n /** How long a document may take to load, default 45 seconds; a timeout is retried once. */\n navigationTimeoutMs?: number;\n /** How long to wait for the network to settle after a load, default 10 seconds; a busy network is not a failure. */\n settleTimeoutMs?: number;\n /** The most Tab presses K5 makes on one page (default 400); a longer page is reported, not failed. */\n maxTabStops?: number;\n}\n\nexport interface SiteSystemJourneyPlan {\n tier: SiteSystemTier;\n release: string;\n journeys: readonly SiteSystemJourneyId[];\n home: string;\n money: readonly string[];\n contact: string | undefined;\n /** One page per dynamic route family, for W9's URL variants. */\n families: readonly string[];\n /** Home, the money pages and the contact page, without repeats: K5, K6 and C4 run on each. */\n sampled: readonly string[];\n /** The sampled pages, the family pages and `pages.grounds`, without repeats: K14 runs on each. */\n grounds: readonly string[];\n desktopWidths: readonly number[];\n actionOrder: SiteSystemActionOrder;\n quoteName: RegExp;\n navName: string;\n menuName: string | RegExp;\n navigationTimeoutMs: number;\n settleTimeoutMs: number;\n maxTabStops: number;\n /** Gaps that fail a journey when it runs (fail closed), rather than stopping the whole spec file. */\n problems: Partial<Record<SiteSystemJourneyId, string>>;\n}\n\nexport function siteSystemJourneyTitle(id: SiteSystemJourneyId, text: string): string {\n return `[${id}] ${text}`;\n}\n\n/**\n * The journey checks a tier registers on a release: its own and the tiers below it, each from the release that brings\n * it (default: this conformance release).\n */\nexport function siteSystemJourneysFor(tier: SiteSystemTier, release: string = BRANDLAB_CURRENT_RUNTIME_RELEASE.releaseVersion): SiteSystemJourneyId[] {\n const rank = SITE_SYSTEM_TIERS.indexOf(tier);\n const releaseRank = siteSystemReleaseRank(release);\n const journeys = new Map<string, { tier: string; from: string }>(SITE_SYSTEM_CHECKS.filter((check) => check.kind === 'journey').map((check) => [check.id, check]));\n return SITE_SYSTEM_JOURNEY_IDS.filter((id) => {\n const check = journeys.get(id);\n return check !== undefined\n && SITE_SYSTEM_TIERS.indexOf(check.tier as SiteSystemTier) <= rank\n && siteSystemReleaseRank(check.from) <= releaseRank;\n });\n}\n\n/**\n * The release the journeys register for on a site: its installed Brandlab release as `brandlab-ready` reads it (all\n * four packages at one exact version, or one canary, from package.json), else this conformance release. A canary's\n * own release manifest still names the last published release, so without this its new journeys (W2 and W9 on\n * 3.15.0-canary.2) would not register while the gate blocks on them.\n */\nexport function siteSystemJourneyRelease(packageJson: Parameters<typeof detectInstalledBrandlabRelease>[0]): string {\n return detectInstalledBrandlabRelease(packageJson) ?? detectBrandlabCanaryRelease(packageJson)?.version ?? BRANDLAB_CURRENT_RUNTIME_RELEASE.releaseVersion;\n}\n\nfunction pathProblem(label: string, path: unknown): string | null {\n if (typeof path !== 'string' || !path.startsWith('/') || path.startsWith('//') || /\\s/.test(path)) {\n return `${label} must be a site path starting with \"/\" (got ${JSON.stringify(path)})`;\n }\n return null;\n}\n\n/** Validate the options and fill the defaults. Malformed options throw, listing every problem. */\nexport function planBrandlabSiteSystemJourneys(options: BrandlabSiteSystemTestOptions): SiteSystemJourneyPlan {\n const errors: string[] = [];\n const input = (options ?? {}) as Partial<BrandlabSiteSystemTestOptions>;\n if (!SITE_SYSTEM_TIERS.includes(input.tier as SiteSystemTier)) {\n errors.push(`tier must be one of ${SITE_SYSTEM_TIERS.join(', ')} (got ${JSON.stringify(input.tier)})`);\n }\n const pages = input.pages ?? ({} as BrandlabSiteSystemTestOptions['pages']);\n const homeProblem = pathProblem('pages.home', pages.home);\n if (homeProblem) errors.push(homeProblem);\n const money = pages.money === undefined ? [] : typeof pages.money === 'string' ? [pages.money] : [...pages.money];\n money.forEach((path, index) => {\n const problem = pathProblem(`pages.money[${index}]`, path);\n if (problem) errors.push(problem);\n });\n if (pages.contact !== undefined) {\n const problem = pathProblem('pages.contact', pages.contact);\n if (problem) errors.push(problem);\n }\n const families = pages.families === undefined ? [] : typeof pages.families === 'string' ? [pages.families] : [...pages.families];\n families.forEach((path, index) => {\n const problem = pathProblem(`pages.families[${index}]`, path) ?? (/[?#]/.test(path) || path === '/' ? `pages.families[${index}] must be a page path below home with no query (got ${JSON.stringify(path)})` : null);\n if (problem) errors.push(problem);\n });\n const grounds = pages.grounds === undefined ? [] : typeof pages.grounds === 'string' ? [pages.grounds] : [...pages.grounds];\n grounds.forEach((path, index) => {\n const problem = pathProblem(`pages.grounds[${index}]`, path);\n if (problem) errors.push(problem);\n });\n const desktopWidths = input.desktopWidths?.length ? [...input.desktopWidths] : [...SITE_SYSTEM_DESKTOP_WIDTHS];\n for (const width of desktopWidths) {\n if (!Number.isInteger(width) || width < 1024 || width > 3840) errors.push(`desktopWidths must be whole pixel widths from 1024 to 3840 (got ${width})`);\n }\n if (input.actionOrder !== undefined && input.actionOrder !== 'phone-first' && input.actionOrder !== 'quote-first') {\n errors.push(`actionOrder must be phone-first or quote-first (got ${JSON.stringify(input.actionOrder)})`);\n }\n for (const key of ['navigationTimeoutMs', 'settleTimeoutMs', 'maxTabStops'] as const) {\n const value = input[key];\n if (value !== undefined && (!Number.isInteger(value) || value <= 0)) errors.push(`${key} must be a positive whole number (got ${value})`);\n }\n if (input.release !== undefined && (typeof input.release !== 'string' || !/^\\d+\\.\\d+\\.\\d+(?:-canary\\.\\d+)?$/.test(input.release))) {\n errors.push(`release must be a version such as 3.9.0 or 3.15.0-canary.2 (got ${JSON.stringify(input.release)})`);\n }\n if (input.navName !== undefined && (typeof input.navName !== 'string' || !input.navName.trim())) errors.push('navName must be a non-empty string');\n if (errors.length > 0) throw new TypeError(`defineBrandlabSiteSystemTests: ${errors.join('; ')}`);\n\n const tier = input.tier as SiteSystemTier;\n const release = input.release ?? BRANDLAB_CURRENT_RUNTIME_RELEASE.releaseVersion;\n const journeys = siteSystemJourneysFor(tier, release);\n const problems: SiteSystemJourneyPlan['problems'] = {};\n if (journeys.includes('C1') && money.length === 0) {\n problems.C1 = 'pages.money names no money page: C1 checks the first screen of each service page with the enquiry form';\n }\n if (journeys.includes('W9') && families.length === 0) {\n problems.W9 = 'pages.families names no page: W9 checks the URL variants of one built page per dynamic route family';\n }\n const quoteName = input.quoteName === undefined\n ? SITE_SYSTEM_QUOTE_NAME\n : input.quoteName instanceof RegExp\n ? input.quoteName\n : new RegExp(`^\\\\s*${input.quoteName.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')}\\\\s*$`, 'i');\n return {\n tier,\n release,\n journeys,\n home: pages.home,\n money,\n contact: pages.contact,\n families: [...new Set(families)],\n sampled: [...new Set([pages.home, ...money, ...(pages.contact ? [pages.contact] : [])])],\n grounds: [...new Set([pages.home, ...money, ...(pages.contact ? [pages.contact] : []), ...families, ...grounds])],\n desktopWidths,\n actionOrder: input.actionOrder ?? 'phone-first',\n quoteName,\n navName: input.navName ?? 'Main',\n menuName: input.menuName ?? 'Menu',\n navigationTimeoutMs: input.navigationTimeoutMs ?? 45_000,\n settleTimeoutMs: input.settleTimeoutMs ?? 10_000,\n maxTabStops: input.maxTabStops ?? 400,\n problems,\n };\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"site-system-e2e.d.ts","sourceRoot":"","sources":["../src/site-system-e2e.ts"],"names":[],"mappings":"AAgCA,OAAO,EAA4D,KAAK,6BAA6B,EAAE,MAAM,gCAAgC,CAAC;AAK9I,OAAO,EACL,4BAA4B,EAC5B,0BAA0B,EAC1B,uBAAuB,EACvB,0BAA0B,EAC1B,sBAAsB,EACtB,8BAA8B,EAC9B,wBAAwB,EACxB,sBAAsB,EACtB,qBAAqB,EACrB,KAAK,6BAA6B,EAClC,KAAK,qBAAqB,EAC1B,KAAK,mBAAmB,EACxB,KAAK,qBAAqB,GAC3B,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAE,oBAAoB,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAC;AACjG,OAAO,EAAE,4BAA4B,EAAE,MAAM,wCAAwC,CAAC;AACtF,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,eAAe,EACf,uBAAuB,EACvB,uBAAuB,EACvB,WAAW,EACX,KAAK,UAAU,EACf,KAAK,UAAU,GAChB,MAAM,oCAAoC,CAAC;AAa5C,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,6BAA6B,GAAG,IAAI,CAuB1F"}
1
+ {"version":3,"file":"site-system-e2e.d.ts","sourceRoot":"","sources":["../src/site-system-e2e.ts"],"names":[],"mappings":"AAiCA,OAAO,EAA4D,KAAK,6BAA6B,EAAE,MAAM,gCAAgC,CAAC;AAK9I,OAAO,EACL,4BAA4B,EAC5B,0BAA0B,EAC1B,uBAAuB,EACvB,0BAA0B,EAC1B,sBAAsB,EACtB,8BAA8B,EAC9B,wBAAwB,EACxB,sBAAsB,EACtB,qBAAqB,EACrB,KAAK,6BAA6B,EAClC,KAAK,qBAAqB,EAC1B,KAAK,mBAAmB,EACxB,KAAK,qBAAqB,GAC3B,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAE,oBAAoB,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAC;AACjG,OAAO,EAAE,4BAA4B,EAAE,MAAM,wCAAwC,CAAC;AACtF,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,eAAe,EACf,uBAAuB,EACvB,uBAAuB,EACvB,WAAW,EACX,KAAK,UAAU,EACf,KAAK,UAAU,GAChB,MAAM,oCAAoC,CAAC;AAa5C,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,6BAA6B,GAAG,IAAI,CAwB1F"}
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * The Site System v2 browser journeys for a Brandlab production build (release 3.8.0): the header and its menu,
3
3
  * the phone drawer and call bar, contrast, targets, focus, the action order, the money-page first screen and phone
4
- * placements, from 3.13.0 prefetch by link role (W2) and (full tier) the URL variants of each page family (W9). Every
4
+ * placements, from 3.13.0 prefetch by link role (W2), from 3.19.0 alternating backgrounds (K14) and (full tier) the URL variants of each page family (W9). Every
5
5
  * test title starts with its ready-gate check id in square brackets ("[N3] ...") so `brandlab-ready` can read the
6
6
  * Playwright JSON report.
7
7
  *
@@ -29,6 +29,7 @@ import { isolateFromNetwork } from './e2e-support.js';
29
29
  import { defineAccessJourneys } from './site-system/journeys/access.js';
30
30
  import { installPageHelpers } from './site-system/journeys/browser.js';
31
31
  import { defineConversionJourneys } from './site-system/journeys/conversion.js';
32
+ import { defineGroundJourneys } from './site-system/journeys/grounds.js';
32
33
  import { defineMenuJourneys } from './site-system/journeys/menu.js';
33
34
  import { planBrandlabSiteSystemJourneys, siteSystemJourneyRelease } from './site-system/journeys/plan.js';
34
35
  import { definePrefetchJourneys } from './site-system/journeys/prefetch.js';
@@ -64,6 +65,7 @@ export function defineBrandlabSiteSystemTests(options) {
64
65
  defineMenuJourneys(plan, runs);
65
66
  defineShellJourneys(plan, runs);
66
67
  definePrefetchJourneys(plan, runs);
68
+ defineGroundJourneys(plan, runs);
67
69
  defineConversionJourneys(plan, runs);
68
70
  defineVariantJourneys(plan, runs);
69
71
  });
@@ -1 +1 @@
1
- {"version":3,"file":"site-system-e2e.js","sourceRoot":"","sources":["../src/site-system-e2e.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The Site System v2 browser journeys for a Brandlab production build (release 3.8.0): the header and its menu,\n * the phone drawer and call bar, contrast, targets, focus, the action order, the money-page first screen and phone\n * placements, from 3.13.0 prefetch by link role (W2) and (full tier) the URL variants of each page family (W9). Every\n * test title starts with its ready-gate check id in square brackets (\"[N3] ...\") so `brandlab-ready` can read the\n * Playwright JSON report.\n *\n * // tests/e2e/site-system.spec.ts\n * import { defineBrandlabSiteSystemTests } from '@brandlab-digital/conformance/site-system-e2e'\n *\n * defineBrandlabSiteSystemTests({\n * tier: 'enquiry',\n * pages: { home: '/', money: ['/services/blocked-drains'], contact: '/contact' },\n * })\n *\n * The journeys find the header parts by role, so they test a bespoke header and the shared one alike: the header is\n * the banner landmark, the menu the navigation named \"Main\", each submenu a `button[aria-expanded][aria-controls]`\n * and its panel the element that names. The tier decides which journeys register (C1 from `enquiry`, W9 at `full`\n * with `pages.families`), each from the release that brings it: the site's installed Brandlab release (a canary\n * included), read from its package.json as brandlab-ready reads it, so every journey the gate blocks on runs. They run in\n * Chromium; K5 (focus not hidden) runs in every browser project the site's Playwright config defines, so add a\n * WebKit project to cover Safari. axe comes from `@axe-core/playwright` when the site has it, else `axe-core`.\n * Every request leaves the browser only as a GET or HEAD to the build under test.\n */\nimport { readFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { test } from '@playwright/test';\nimport { isolateFromNetwork } from './e2e-support.js';\nimport { defineAccessJourneys } from './site-system/journeys/access.js';\nimport { installPageHelpers } from './site-system/journeys/browser.js';\nimport { defineConversionJourneys } from './site-system/journeys/conversion.js';\nimport { defineMenuJourneys } from './site-system/journeys/menu.js';\nimport { planBrandlabSiteSystemJourneys, siteSystemJourneyRelease, type BrandlabSiteSystemTestOptions } from './site-system/journeys/plan.js';\nimport { definePrefetchJourneys } from './site-system/journeys/prefetch.js';\nimport { defineShellJourneys } from './site-system/journeys/shell.js';\nimport { defineVariantJourneys } from './site-system/journeys/variants.js';\n\nexport {\n SITE_SYSTEM_DESKTOP_VIEWPORT,\n SITE_SYSTEM_DESKTOP_WIDTHS,\n SITE_SYSTEM_JOURNEY_IDS,\n SITE_SYSTEM_PHONE_VIEWPORT,\n SITE_SYSTEM_QUOTE_NAME,\n planBrandlabSiteSystemJourneys,\n siteSystemJourneyRelease,\n siteSystemJourneyTitle,\n siteSystemJourneysFor,\n type BrandlabSiteSystemTestOptions,\n type SiteSystemActionOrder,\n type SiteSystemJourneyId,\n type SiteSystemJourneyPlan,\n} from './site-system/journeys/plan.js';\nexport { HOVER_OPEN_WINDOW_MS, MOUSE_OUT_CLOSE_WINDOW_MS } from './site-system/journeys/menu.js';\nexport { SITE_SYSTEM_PHONE_PLACEMENTS } from './site-system/journeys/page-helpers.js';\nexport {\n W9_CACHE_HEADERS,\n W9_TRACKING_QUERY,\n W9_UNKNOWN_SLUG,\n notFoundVariantProblems,\n trackingVariantProblems,\n urlVariants,\n type W9Response,\n type W9Variants,\n} from './site-system/journeys/variants.js';\n\nconst CHROMIUM_ONLY = 'The site-system journeys run in Chromium; K5 runs in every browser project.';\n\n/** The site's package.json (Playwright runs from the project root), or null when it cannot be read. */\nfunction sitePackageJson(): Parameters<typeof siteSystemJourneyRelease>[0] {\n try {\n return JSON.parse(readFileSync(join(process.cwd(), 'package.json'), 'utf8')) as Parameters<typeof siteSystemJourneyRelease>[0];\n } catch {\n return null;\n }\n}\n\nexport function defineBrandlabSiteSystemTests(options: BrandlabSiteSystemTestOptions): void {\n const plan = planBrandlabSiteSystemJourneys({ ...options, release: options.release ?? siteSystemJourneyRelease(sitePackageJson()) });\n const runs = new Set<string>(plan.journeys);\n const chromiumOnly = () => test.skip(({ browserName }) => browserName !== 'chromium', CHROMIUM_ONLY);\n\n test.describe('Brandlab site system', () => {\n // Service workers could answer requests outside page and context routing.\n test.use({ serviceWorkers: 'block' });\n test.beforeEach(async ({ page, baseURL }) => {\n await isolateFromNetwork(page, { baseURL, googleTag: 'stub' });\n await installPageHelpers(page);\n });\n\n test.describe('Chromium journeys', () => {\n chromiumOnly();\n defineMenuJourneys(plan, runs);\n defineShellJourneys(plan, runs);\n definePrefetchJourneys(plan, runs);\n defineConversionJourneys(plan, runs);\n defineVariantJourneys(plan, runs);\n });\n defineAccessJourneys(plan, runs, { chromiumOnly });\n });\n}\n"]}
1
+ {"version":3,"file":"site-system-e2e.js","sourceRoot":"","sources":["../src/site-system-e2e.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The Site System v2 browser journeys for a Brandlab production build (release 3.8.0): the header and its menu,\n * the phone drawer and call bar, contrast, targets, focus, the action order, the money-page first screen and phone\n * placements, from 3.13.0 prefetch by link role (W2), from 3.19.0 alternating backgrounds (K14) and (full tier) the URL variants of each page family (W9). Every\n * test title starts with its ready-gate check id in square brackets (\"[N3] ...\") so `brandlab-ready` can read the\n * Playwright JSON report.\n *\n * // tests/e2e/site-system.spec.ts\n * import { defineBrandlabSiteSystemTests } from '@brandlab-digital/conformance/site-system-e2e'\n *\n * defineBrandlabSiteSystemTests({\n * tier: 'enquiry',\n * pages: { home: '/', money: ['/services/blocked-drains'], contact: '/contact' },\n * })\n *\n * The journeys find the header parts by role, so they test a bespoke header and the shared one alike: the header is\n * the banner landmark, the menu the navigation named \"Main\", each submenu a `button[aria-expanded][aria-controls]`\n * and its panel the element that names. The tier decides which journeys register (C1 from `enquiry`, W9 at `full`\n * with `pages.families`), each from the release that brings it: the site's installed Brandlab release (a canary\n * included), read from its package.json as brandlab-ready reads it, so every journey the gate blocks on runs. They run in\n * Chromium; K5 (focus not hidden) runs in every browser project the site's Playwright config defines, so add a\n * WebKit project to cover Safari. axe comes from `@axe-core/playwright` when the site has it, else `axe-core`.\n * Every request leaves the browser only as a GET or HEAD to the build under test.\n */\nimport { readFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { test } from '@playwright/test';\nimport { isolateFromNetwork } from './e2e-support.js';\nimport { defineAccessJourneys } from './site-system/journeys/access.js';\nimport { installPageHelpers } from './site-system/journeys/browser.js';\nimport { defineConversionJourneys } from './site-system/journeys/conversion.js';\nimport { defineGroundJourneys } from './site-system/journeys/grounds.js';\nimport { defineMenuJourneys } from './site-system/journeys/menu.js';\nimport { planBrandlabSiteSystemJourneys, siteSystemJourneyRelease, type BrandlabSiteSystemTestOptions } from './site-system/journeys/plan.js';\nimport { definePrefetchJourneys } from './site-system/journeys/prefetch.js';\nimport { defineShellJourneys } from './site-system/journeys/shell.js';\nimport { defineVariantJourneys } from './site-system/journeys/variants.js';\n\nexport {\n SITE_SYSTEM_DESKTOP_VIEWPORT,\n SITE_SYSTEM_DESKTOP_WIDTHS,\n SITE_SYSTEM_JOURNEY_IDS,\n SITE_SYSTEM_PHONE_VIEWPORT,\n SITE_SYSTEM_QUOTE_NAME,\n planBrandlabSiteSystemJourneys,\n siteSystemJourneyRelease,\n siteSystemJourneyTitle,\n siteSystemJourneysFor,\n type BrandlabSiteSystemTestOptions,\n type SiteSystemActionOrder,\n type SiteSystemJourneyId,\n type SiteSystemJourneyPlan,\n} from './site-system/journeys/plan.js';\nexport { HOVER_OPEN_WINDOW_MS, MOUSE_OUT_CLOSE_WINDOW_MS } from './site-system/journeys/menu.js';\nexport { SITE_SYSTEM_PHONE_PLACEMENTS } from './site-system/journeys/page-helpers.js';\nexport {\n W9_CACHE_HEADERS,\n W9_TRACKING_QUERY,\n W9_UNKNOWN_SLUG,\n notFoundVariantProblems,\n trackingVariantProblems,\n urlVariants,\n type W9Response,\n type W9Variants,\n} from './site-system/journeys/variants.js';\n\nconst CHROMIUM_ONLY = 'The site-system journeys run in Chromium; K5 runs in every browser project.';\n\n/** The site's package.json (Playwright runs from the project root), or null when it cannot be read. */\nfunction sitePackageJson(): Parameters<typeof siteSystemJourneyRelease>[0] {\n try {\n return JSON.parse(readFileSync(join(process.cwd(), 'package.json'), 'utf8')) as Parameters<typeof siteSystemJourneyRelease>[0];\n } catch {\n return null;\n }\n}\n\nexport function defineBrandlabSiteSystemTests(options: BrandlabSiteSystemTestOptions): void {\n const plan = planBrandlabSiteSystemJourneys({ ...options, release: options.release ?? siteSystemJourneyRelease(sitePackageJson()) });\n const runs = new Set<string>(plan.journeys);\n const chromiumOnly = () => test.skip(({ browserName }) => browserName !== 'chromium', CHROMIUM_ONLY);\n\n test.describe('Brandlab site system', () => {\n // Service workers could answer requests outside page and context routing.\n test.use({ serviceWorkers: 'block' });\n test.beforeEach(async ({ page, baseURL }) => {\n await isolateFromNetwork(page, { baseURL, googleTag: 'stub' });\n await installPageHelpers(page);\n });\n\n test.describe('Chromium journeys', () => {\n chromiumOnly();\n defineMenuJourneys(plan, runs);\n defineShellJourneys(plan, runs);\n definePrefetchJourneys(plan, runs);\n defineGroundJourneys(plan, runs);\n defineConversionJourneys(plan, runs);\n defineVariantJourneys(plan, runs);\n });\n defineAccessJourneys(plan, runs, { chromiumOnly });\n });\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brandlab-digital/conformance",
3
- "version": "3.18.0",
3
+ "version": "3.19.0",
4
4
  "license": "UNLICENSED",
5
5
  "repository": {
6
6
  "type": "git",
@@ -29,7 +29,7 @@
29
29
  "typecheck": "tsc --noEmit -p tsconfig.json"
30
30
  },
31
31
  "dependencies": {
32
- "@brandlab-digital/site-core": "3.18.0",
32
+ "@brandlab-digital/site-core": "3.19.0",
33
33
  "typescript": "6.0.3"
34
34
  },
35
35
  "peerDependencies": {