@brandlab-digital/conformance 3.13.0 → 3.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/README.md +9 -0
  2. package/dist/analytics-e2e.d.ts +10 -16
  3. package/dist/analytics-e2e.d.ts.map +1 -1
  4. package/dist/analytics-e2e.js +61 -3
  5. package/dist/analytics-e2e.js.map +1 -1
  6. package/dist/cli.d.ts.map +1 -1
  7. package/dist/cli.js +4 -1
  8. package/dist/cli.js.map +1 -1
  9. package/dist/current-release.js +5 -5
  10. package/dist/current-release.js.map +1 -1
  11. package/dist/estate.d.ts +28 -0
  12. package/dist/estate.d.ts.map +1 -1
  13. package/dist/estate.js +35 -12
  14. package/dist/estate.js.map +1 -1
  15. package/dist/project.d.ts +20 -0
  16. package/dist/project.d.ts.map +1 -1
  17. package/dist/project.js +92 -2
  18. package/dist/project.js.map +1 -1
  19. package/dist/ready-cli.d.ts.map +1 -1
  20. package/dist/ready-cli.js +8 -3
  21. package/dist/ready-cli.js.map +1 -1
  22. package/dist/rule-registry.d.ts +5 -0
  23. package/dist/rule-registry.d.ts.map +1 -1
  24. package/dist/rule-registry.js +1 -0
  25. package/dist/rule-registry.js.map +1 -1
  26. package/dist/site-system/checks.d.ts +26 -1
  27. package/dist/site-system/checks.d.ts.map +1 -1
  28. package/dist/site-system/checks.js +3 -0
  29. package/dist/site-system/checks.js.map +1 -1
  30. package/dist/site-system/report.d.ts.map +1 -1
  31. package/dist/site-system/report.js +19 -5
  32. package/dist/site-system/report.js.map +1 -1
  33. package/dist/site-system/rule-modules.d.ts.map +1 -1
  34. package/dist/site-system/rule-modules.js +5 -0
  35. package/dist/site-system/rule-modules.js.map +1 -1
  36. package/dist/site-system/rules/delivery/h13-inbox.d.ts +23 -0
  37. package/dist/site-system/rules/delivery/h13-inbox.d.ts.map +1 -0
  38. package/dist/site-system/rules/delivery/h13-inbox.js +208 -0
  39. package/dist/site-system/rules/delivery/h13-inbox.js.map +1 -0
  40. package/dist/site-system/rules/delivery/index.d.ts +12 -0
  41. package/dist/site-system/rules/delivery/index.d.ts.map +1 -0
  42. package/dist/site-system/rules/delivery/index.js +7 -0
  43. package/dist/site-system/rules/delivery/index.js.map +1 -0
  44. package/dist/site-system/rules/full/e1-pattern.d.ts +4 -0
  45. package/dist/site-system/rules/full/e1-pattern.d.ts.map +1 -0
  46. package/dist/site-system/rules/full/e1-pattern.js +47 -0
  47. package/dist/site-system/rules/full/e1-pattern.js.map +1 -0
  48. package/dist/site-system/rules/full/estate.d.ts +16 -1
  49. package/dist/site-system/rules/full/estate.d.ts.map +1 -1
  50. package/dist/site-system/rules/full/estate.js +23 -4
  51. package/dist/site-system/rules/full/estate.js.map +1 -1
  52. package/dist/site-system/rules/full/geography.d.ts +8 -2
  53. package/dist/site-system/rules/full/geography.d.ts.map +1 -1
  54. package/dist/site-system/rules/full/geography.js +6 -2
  55. package/dist/site-system/rules/full/geography.js.map +1 -1
  56. package/dist/site-system/rules/full/index.d.ts +2 -2
  57. package/dist/site-system/rules/full/index.d.ts.map +1 -1
  58. package/dist/site-system/rules/full/index.js +15 -4
  59. package/dist/site-system/rules/full/index.js.map +1 -1
  60. package/dist/site-system/rules/full/k12-slots.d.ts +4 -0
  61. package/dist/site-system/rules/full/k12-slots.d.ts.map +1 -0
  62. package/dist/site-system/rules/full/k12-slots.js +40 -0
  63. package/dist/site-system/rules/full/k12-slots.js.map +1 -0
  64. package/dist/site-system/rules/full/loc-index.d.ts +5 -0
  65. package/dist/site-system/rules/full/loc-index.d.ts.map +1 -1
  66. package/dist/site-system/rules/full/loc-index.js +43 -2
  67. package/dist/site-system/rules/full/loc-index.js.map +1 -1
  68. package/dist/site-system/rules/full/strategy.d.ts +4 -0
  69. package/dist/site-system/rules/full/strategy.d.ts.map +1 -1
  70. package/dist/site-system/rules/full/strategy.js +19 -3
  71. package/dist/site-system/rules/full/strategy.js.map +1 -1
  72. package/dist/site-system/rules/sections/k2-skin-coverage.d.ts +2 -2
  73. package/dist/site-system/rules/sections/k2-skin-coverage.d.ts.map +1 -1
  74. package/dist/site-system/rules/sections/k2-skin-coverage.js +6 -2
  75. package/dist/site-system/rules/sections/k2-skin-coverage.js.map +1 -1
  76. package/dist/site-system/runners.js +1 -1
  77. package/dist/site-system/runners.js.map +1 -1
  78. package/package.json +2 -2
package/dist/estate.js CHANGED
@@ -51,11 +51,29 @@ import { extractSearchCertificationHtmlSnapshot } from './search-certification.j
51
51
  import { changedFilesFromEnv, estateScopeFromEnv, sampleBySeed, sampleEstateRoutes } from './estate-sample.js';
52
52
  export { changedFilesFromEnv, changedFileStem, defaultRoutesForFile, estateScopeFromEnv, sampleBySeed, sampleEstateRoutes, } from './estate-sample.js';
53
53
  const LOCATION_KINDS = new Set(['location', 'service-location']);
54
- /** A location route's place slug: the last segment of its path. */
55
- function routePlace(route) {
56
- if (!route || !LOCATION_KINDS.has(route.kind ?? ''))
57
- return undefined;
58
- return normalisePath(route.path).split('/').filter(Boolean).at(-1);
54
+ /**
55
+ * A location route's place: the registry suburb slug its last path segment names (through the estate plan's public
56
+ * slugs, as site-core's `estatePublicSlug`), and whether it is a region page. A region page has no centre, so its
57
+ * nearby lists are not ranked by distance: it is a region when other location pages sit under it, when its slug is a
58
+ * region's public slug, or when its slug is a suburb's registry slug that the plan moved elsewhere (AA: `ryde`).
59
+ */
60
+ export function estatePlaceResolver(routes, estate) {
61
+ const parents = new Set(routes.filter((route) => LOCATION_KINDS.has(route.kind ?? '')).map((route) => route.parentId));
62
+ const regionSlugs = new Set(Object.values(estate?.slugs?.regions ?? {}));
63
+ const suburbRenames = estate?.slugs?.suburbs ?? {};
64
+ const suburbByPublic = new Map(Object.entries(suburbRenames).map(([registry, publicSlug]) => [publicSlug, registry]));
65
+ return (route) => {
66
+ if (!route || !LOCATION_KINDS.has(route.kind ?? ''))
67
+ return undefined;
68
+ const publicSlug = normalisePath(route.path).split('/').filter(Boolean).at(-1);
69
+ if (!publicSlug)
70
+ return undefined;
71
+ const suburb = suburbByPublic.get(publicSlug);
72
+ const region = (route.id !== undefined && parents.has(route.id)) ||
73
+ regionSlugs.has(publicSlug) ||
74
+ (suburb === undefined && Object.hasOwn(suburbRenames, publicSlug));
75
+ return { slug: suburb ?? publicSlug, region };
76
+ };
59
77
  }
60
78
  const BUSINESS_TYPES = new Set([
61
79
  'LocalBusiness', 'HomeAndConstructionBusiness', 'GeneralContractor', 'Plumber', 'Electrician',
@@ -81,6 +99,7 @@ export function defineBrandlabEstateTests(options) {
81
99
  const families = options.families ?? [];
82
100
  const familyPaths = new Set(families.flatMap((family) => family.paths.map(normalisePath)));
83
101
  const routeByPath = new Map(routes.map((route) => [normalisePath(route.path), route]));
102
+ const placeOfRoute = estatePlaceResolver(routes, options.estate);
84
103
  const homePath = normalisePath(routes.find((route) => route.kind === 'home')?.path ?? '/');
85
104
  // A draft pull request crawls a sample (BRANDLAB_ESTATE_SCOPE=sample); ready for review crawls every route.
86
105
  const scope = estateScopeFromEnv();
@@ -174,13 +193,16 @@ export function defineBrandlabEstateTests(options) {
174
193
  business: options.business,
175
194
  }))
176
195
  failures.push(`${route.path}: ${problem}`);
177
- const place = routePlace(route);
178
- if (place && centreOf(place)) {
196
+ const place = placeOfRoute(route);
197
+ if (place && !place.region && centreOf(place.slug)) {
179
198
  for (const problem of neighbourOrderProblems({
180
199
  html,
181
- origin: place,
200
+ origin: place.slug,
182
201
  pageUrl: expectedCanonical,
183
- placeOf: (path) => routePlace(routeByPath.get(path)),
202
+ placeOf: (path) => {
203
+ const linked = placeOfRoute(routeByPath.get(path));
204
+ return linked && !linked.region ? linked.slug : undefined;
205
+ },
184
206
  centreOf,
185
207
  }))
186
208
  failures.push(`${route.path}: ${problem}`);
@@ -575,6 +597,7 @@ export function defineBrandlabPageQualityTests(options) {
575
597
  const timeout = options.timeout ?? 240_000;
576
598
  const routes = options.routes.filter((route) => normalisePath(route.path) !== NOT_FOUND_ROUTE);
577
599
  const routeByPath = new Map(routes.map((route) => [normalisePath(route.path), route]));
600
+ const placeOfRoute = estatePlaceResolver(routes, options.estate);
578
601
  const run = pageQualityRunFromEnv();
579
602
  test.describe('Brandlab page quality (advisory)', () => {
580
603
  test('reports page and structured-data quality without failing', async ({ request }, testInfo) => {
@@ -608,9 +631,9 @@ export function defineBrandlabPageQualityTests(options) {
608
631
  if (formatProblems.length > 0)
609
632
  add('title-format', `"${snapshot.title}": ${formatProblems.join('; ')}`);
610
633
  titles.set(snapshot.title, [...(titles.get(snapshot.title) ?? []), route.path]);
611
- const place = routePlace(route);
634
+ const place = placeOfRoute(route);
612
635
  if (h1s.length === 1) {
613
- const problems = titleHeadingProblems(snapshot.title, h1s[0], place ? geography.placeNames.get(place) : undefined);
636
+ const problems = titleHeadingProblems(snapshot.title, h1s[0], place && !place.region ? geography.placeNames.get(place.slug) : undefined);
614
637
  if (problems.length > 0)
615
638
  add('title-h1', problems.join('; '));
616
639
  }
@@ -633,7 +656,7 @@ export function defineBrandlabPageQualityTests(options) {
633
656
  for (const failure of inspectStructuredData(route, snapshot.jsonLd, businessId)) {
634
657
  add('structured-data', failure.slice(route.path.length + 2));
635
658
  }
636
- const place = routePlace(route);
659
+ const place = placeOfRoute(route)?.slug;
637
660
  if (indexable && place)
638
661
  similarityPages.push({ path: route.path, place, blocks: textBlocks(html).map(geography.maskPlaces) });
639
662
  const targets = new Set();
@@ -1 +1 @@
1
- {"version":3,"file":"estate.js","sourceRoot":"","sources":["../src/estate.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The shared technical-estate journey for a Brandlab production build:\n *\n * // tests/e2e/estate.spec.ts\n * import { defineBrandlabEstateTests } from '@brandlab-digital/conformance/estate'\n * import { site } from '../../src/config/site'\n * import { routes } from '../../src/data/routes'\n * import { redirects } from '../../src/data/redirects'\n *\n * defineBrandlabEstateTests({ site, routes, redirects })\n *\n * It asserts the estate and its routes: the sitemap, each route's status,\n * canonical, indexing and breadcrumb JSON-LD (every item but the last names its\n * page, from 3.3.3), internal links, redirects, unknown slugs, the page families\n * rendered on request and robots.txt. Page and structured-data\n * quality is reported, never enforced,\n * by defineBrandlabPageQualityTests: a backend release keeps production's pages\n * as they are, which the comparison harness proves. Counts are always derived\n * from the current catalogue, never a historic constant.\n *\n * From 3.4.0 (the Brandlab on-page and location standard) the estate journey\n * also fails on: a sitemap that is not split by page family once the catalogue\n * spans families, or whose lastmod is not the route's content date; an\n * indexable page without a title, meta description or JSON-LD in its server HTML; FAQPage or\n * HowTo markup, or a rating on the business itself; a homepage without a\n * WebSite node (name and url); an unknown slug that does not answer 404 to GET\n * and HEAD; a nearby list on a location page that is not ranked by distance\n * between suburb centres; and a catalogue that adds indexable location pages\n * beyond the recorded capacity (`locationCapacity`). From 3.6.0 it also fails\n * on a service-area map (`<figure data-area-map>`) without a caption carrying\n * the CC BY boundary credit, with a link that is a tab stop or does not go to an\n * indexable catalogue page, with more than `areaMapMaxBytes` inline, or whose\n * base-layer file does not answer as SVG with the groups it uses. The page-quality journey\n * reports title, heading, description and HTML-size warnings, orphan pages,\n * links from indexable pages to noindex location pages, centerpiece\n * similarity (warn at 0.5, review list at 0.7) and the location review list\n * (location-review.json); pages on that list stay indexed.\n *\n * From 3.13.0 (client CI v2) a draft pull request sets BRANDLAB_ESTATE_SCOPE=sample:\n * the catalogue and redirect journeys check the hubs, the first and last route of\n * each kind, the routes changed files feed and a fixed-seed sample (estate-sample.ts),\n * and say so in an `estate-scope` annotation. Every other run crawls the full estate.\n * BRANDLAB_PAGE_QUALITY=skip skips the advisory crawl, which CI v2 runs on its own.\n */\nimport { mkdirSync, writeFileSync } from 'node:fs';\nimport { request as httpRequest } from 'node:http';\nimport { join } from 'node:path';\nimport { expect, test, type APIRequestContext } from '@playwright/test';\nimport {\n areaMapProblems,\n buildLocationReviewList,\n centerpieceSimilarity,\n checkLocationCapacity,\n descriptionLengthProblem,\n HTML_MAX_BYTES,\n headingTexts,\n loadBrandlabGeography,\n neighbourOrderProblems,\n parseSitemapEntries,\n SIMILARITY_REVIEW,\n SIMILARITY_WARN,\n sitemapFamilyProblems,\n sitemapLastmodProblems,\n structuredDataProblems,\n textBlocks,\n titleFormatProblems,\n titleHeadingProblems,\n type LocationCapacityRecord,\n type SimilarityPage,\n type SitemapFile,\n type SuburbCentreLookup,\n} from './page-standard.js';\nimport { extractSearchCertificationHtmlSnapshot } from './search-certification.js';\nimport { changedFilesFromEnv, estateScopeFromEnv, sampleBySeed, sampleEstateRoutes, type BrandlabEstateSampleOptions } from './estate-sample.js';\n\nexport {\n changedFilesFromEnv,\n changedFileStem,\n defaultRoutesForFile,\n estateScopeFromEnv,\n sampleBySeed,\n sampleEstateRoutes,\n type BrandlabEstateSample,\n type BrandlabEstateSampleOptions,\n type BrandlabEstateSampleRoute,\n type BrandlabEstateScope,\n} from './estate-sample.js';\n\nexport interface BrandlabEstateRoute {\n path: string;\n canonicalPath?: string;\n indexing: 'index' | 'noindex-follow' | 'noindex-nofollow';\n /** The route kind (a catalogue route has it): sitemap families and location checks read it. */\n kind?: string;\n id?: string;\n /** The content date the sitemap's lastmod must carry. */\n lastSignificantChange?: string;\n locationDecision?: { indexing?: string; evidence?: readonly string[]; review?: string };\n}\n\nexport interface BrandlabEstateRedirect {\n source: string;\n destination: string;\n status: 301 | 302 | 303 | 307 | 308;\n /** Host-scoped alias rules are proved by the comparison harness host probes. */\n host?: string;\n}\n\n/**\n * A page family the site renders on request outside the catalogue: a dynamic\n * route that prerenders nothing and renders every page of its space (every\n * combination of its values) when it is first requested.\n */\nexport interface BrandlabEstateFamily {\n /** The app route, e.g. `/services/[service]/[location]`. */\n pattern: string;\n /** Every page of the family's space; catalogued pages of the route may be among them. */\n paths: readonly string[];\n /** The indexing its pages outside the catalogue render. */\n indexing: BrandlabEstateRoute['indexing'];\n}\n\nexport interface BrandlabEstateTestOptions {\n site: { canonicalUrl: string | URL };\n /** The route catalogue. A `/404` route is Next's not-found page and must answer 404. */\n routes: readonly BrandlabEstateRoute[];\n /**\n * The redirect table. A source with parameters (`/old/:slug`, `:path*`) is\n * requested with one sample segment per parameter and checked against its\n * destination filled in the same way; a parameter with its own regular\n * expression cannot be sampled and is listed as skipped.\n */\n redirects?: readonly BrandlabEstateRedirect[];\n /** Paths that must answer 404; defaults to one unknown slug under every catalogue parent. */\n notFoundPaths?: readonly string[];\n /** Also prove `<source>/` redirects straight to the destination (the shared helper emits these). */\n trailingSlashRedirects?: boolean;\n /**\n * Page families rendered on request outside the catalogue. Links into a\n * family's space count as live pages; one page of each family (outside the\n * catalogue where it has one) is checked for its status, canonical and\n * indexing; and a path of its pattern outside the space, one unknown value\n * per dynamic segment, must answer 404 with the site's not-found page rendered\n * on the server: the text an unknown top-level path's 404 page shows.\n */\n families?: readonly BrandlabEstateFamily[];\n /**\n * The site's recorded location capacity (site-core/location-policy). The\n * journey fails only when the catalogue holds more indexable location pages\n * than the capacity and the pages at record allow; without one it notes that\n * none is recorded.\n */\n locationCapacity?: LocationCapacityRecord | null;\n /** The business node's @id (default `<origin>/#business`); `false` when the site has none. */\n businessId?: string | false;\n /** The business facts record: the business node's name and telephone must match it. */\n business?: { name?: string; telephone?: string };\n /** A place's centre for neighbour order (default: the registry's suburb centres). */\n centreOf?: SuburbCentreLookup;\n /** The most inline markup one service-area map may carry (default 60 000 bytes, 3.6.0). */\n areaMapMaxBytes?: number;\n /**\n * The sampled crawl a draft pull request runs (`BRANDLAB_ESTATE_SCOPE=sample`, 3.13.0): how many routes and\n * redirects the fixed seed adds, the seed, and which routes a changed file feeds.\n */\n sample?: BrandlabEstateSampleOptions;\n concurrency?: number;\n timeout?: number;\n}\n\nexport interface BrandlabPageQualityTestOptions {\n site: { canonicalUrl: string | URL };\n routes: readonly BrandlabEstateRoute[];\n /** The @id every indexable page's graph references; defaults to `<origin>/#business`, `false` skips it. */\n businessId?: string | false;\n /** A place's centre for similarity siblings (default: the registry's suburb centres). */\n centreOf?: SuburbCentreLookup;\n concurrency?: number;\n timeout?: number;\n}\n\nexport interface BrandlabPageQualityFinding {\n path: string;\n kind:\n | 'headings'\n | 'title'\n | 'title-format'\n | 'title-duplicate'\n | 'title-h1'\n | 'description'\n | 'description-length'\n | 'description-duplicate'\n | 'html-size'\n | 'similarity'\n | 'location-review'\n | 'orphan'\n | 'noindex-link'\n | 'og-url'\n | 'implementation-wording'\n | 'structured-data';\n detail: string;\n}\n\nconst LOCATION_KINDS = new Set(['location', 'service-location']);\n\n/** A location route's place slug: the last segment of its path. */\nfunction routePlace(route: BrandlabEstateRoute | undefined): string | undefined {\n if (!route || !LOCATION_KINDS.has(route.kind ?? '')) return undefined;\n return normalisePath(route.path).split('/').filter(Boolean).at(-1);\n}\n\n\nconst BUSINESS_TYPES = new Set([\n 'LocalBusiness', 'HomeAndConstructionBusiness', 'GeneralContractor', 'Plumber', 'Electrician',\n 'RoofingContractor', 'HousePainter', 'Locksmith', 'MovingCompany', 'HVACBusiness', 'ProfessionalService',\n]);\nconst UNKNOWN_SLUG = 'brandlab-estate-unknown-page';\nconst NOT_FOUND_ROUTE = '/404';\nconst PATTERN_SAMPLE = 'brandlab-estate-sample';\nconst ASSET_PATH = /\\.[a-z0-9]{2,5}$/i;\n\nexport function defineBrandlabEstateTests(options: BrandlabEstateTestOptions) {\n const origin = new URL(String(options.site.canonicalUrl)).origin;\n const canonical = (path: string) => new URL(path, `${origin}/`).href;\n const routes = options.routes;\n const knownPaths = new Set(routes.map((route) => normalisePath(route.path)));\n const redirects = (options.redirects ?? []).filter((rule) => !rule.host);\n const redirectBySource = new Map(\n redirects.filter((rule) => !isRedirectPattern(rule.source)).map((rule) => [normalisePath(rule.source), rule]),\n );\n const patternRedirects = redirects.flatMap((rule) => {\n const matcher = isRedirectPattern(rule.source) ? redirectSourceMatcher(rule.source) : undefined;\n return matcher ? [{ rule, matcher }] : [];\n });\n const findRedirect = (path: string) =>\n redirectBySource.get(path) ?? patternRedirects.find(({ matcher }) => matcher.test(path))?.rule;\n const timeout = options.timeout ?? 240_000;\n const families = options.families ?? [];\n const familyPaths = new Set(families.flatMap((family) => family.paths.map(normalisePath)));\n const routeByPath = new Map(routes.map((route) => [normalisePath(route.path), route]));\n const homePath = normalisePath(routes.find((route) => route.kind === 'home')?.path ?? '/');\n // A draft pull request crawls a sample (BRANDLAB_ESTATE_SCOPE=sample); ready for review crawls every route.\n const scope = estateScopeFromEnv();\n const sample = sampleEstateRoutes(routes, { scope: scope.scope, changedFiles: changedFilesFromEnv(), ...options.sample });\n const scopeNote = [sample.summary, scope.note].filter(Boolean).join('; ');\n const crawledRedirects = scope.scope === 'sample' ? sampleBySeed(redirects, (rule) => rule.source, options.sample) : redirects;\n\n test.describe('Brandlab technical estate', () => {\n test('the sitemap lists exactly the indexable catalogue routes', async ({ request }) => {\n test.setTimeout(timeout);\n const listed = await readSitemap(request, '/sitemap.xml', origin);\n const expected = routes\n .filter((route) => route.indexing === 'index')\n .map((route) => canonical(route.canonicalPath ?? route.path));\n expect(listed.length, 'the sitemap repeats a URL').toBe(new Set(listed).size);\n expect([...listed].sort()).toEqual([...new Set(expected)].sort());\n });\n\n test('the sitemap is split by page family and dated by content, never by the build', async ({ request }, testInfo) => {\n test.setTimeout(timeout);\n const { index, files } = await readSitemapFiles(request, '/sitemap.xml', origin);\n const problems = [\n ...sitemapFamilyProblems({ index, files, routes, origin }),\n ...sitemapLastmodProblems(files.flatMap((file) => file.entries), routes, origin),\n ];\n await testInfo.attach('sitemap-files.json', {\n body: JSON.stringify(files.map((file) => ({ path: file.path, urls: file.entries.length })), null, 2),\n contentType: 'application/json',\n });\n expect(problems).toEqual([]);\n });\n\n test('location pages stay within the recorded capacity', async ({}, testInfo) => {\n const result = checkLocationCapacity(routes, options.locationCapacity);\n testInfo.annotations.push({ type: result.limit === null ? 'warning' : 'location-capacity', description: result.detail });\n expect(result.blocked, result.detail).toBe(false);\n });\n\n test('every catalogue route serves its canonical identity and links only to live pages', async ({ request }, testInfo) => {\n test.setTimeout(timeout);\n const centreOf = options.centreOf ?? (await loadBrandlabGeography()).centreOf;\n const failures: string[] = [];\n const internalLinks = new Map<string, string>();\n const checked: { path: string; canonical: string | null; jsonLd: number }[] = [];\n const areaMapLayers = new Map<string, { ids: Set<string>; from: string }>();\n testInfo.annotations.push({ type: 'estate-scope', description: scopeNote });\n await eachWithConcurrency(sample.routes, options.concurrency ?? 8, async (route) => {\n const [get, head] = await Promise.all([\n request.get(route.path, { maxRedirects: 0 }),\n request.head(route.path, { maxRedirects: 0 }),\n ]);\n // Next serves its not-found page at /404 with a 404 status.\n const notFound = normalisePath(route.path) === NOT_FOUND_ROUTE;\n const expectedStatus = notFound ? 404 : 200;\n if (get.status() !== expectedStatus || head.status() !== expectedStatus) {\n failures.push(`${route.path}: GET ${get.status()}, HEAD ${head.status()} (expected ${expectedStatus})`);\n return;\n }\n const html = await get.text();\n const snapshot = extractSearchCertificationHtmlSnapshot(html);\n const robots = `${snapshot.robots ?? ''} ${get.headers()['x-robots-tag'] ?? ''}`;\n if (notFound) {\n if (!/noindex/i.test(robots)) failures.push(`${route.path}: the not-found page is indexable`);\n checked.push({ path: route.path, canonical: snapshot.canonical, jsonLd: snapshot.jsonLd.length });\n return;\n }\n const expectedCanonical = canonical(route.canonicalPath ?? route.path);\n const canonicalTags = html.match(/<link\\b[^>]*\\brel=[\"']canonical[\"'][^>]*>/gi) ?? [];\n if (canonicalTags.length !== 1) failures.push(`${route.path}: ${canonicalTags.length} canonical links`);\n if (!snapshot.canonical || new URL(snapshot.canonical, origin).href !== expectedCanonical) {\n failures.push(`${route.path}: canonical ${snapshot.canonical ?? 'missing'} (expected ${expectedCanonical})`);\n }\n if (route.indexing === 'index' && /noindex/i.test(robots)) failures.push(`${route.path}: indexable route is noindex`);\n if (route.indexing !== 'index' && !/noindex/i.test(robots)) failures.push(`${route.path}: ${route.indexing} route is indexable`);\n for (const problem of breadcrumbListProblems(snapshot.jsonLd)) failures.push(`${route.path}: ${problem}`);\n if (route.indexing === 'index') {\n if (!snapshot.title) failures.push(`${route.path}: no <title> in the server HTML`);\n if (!snapshot.description) failures.push(`${route.path}: no meta description in the server HTML`);\n if (snapshot.jsonLd.length === 0) failures.push(`${route.path}: no JSON-LD in the server HTML`);\n }\n if (snapshot.jsonLd.some((payload) => (payload as { parseError?: boolean }).parseError)) failures.push(`${route.path}: invalid JSON-LD`);\n for (const problem of structuredDataProblems(snapshot.jsonLd, {\n origin,\n home: normalisePath(route.path) === homePath,\n businessId: options.businessId,\n business: options.business,\n })) failures.push(`${route.path}: ${problem}`);\n const place = routePlace(route);\n if (place && centreOf(place)) {\n for (const problem of neighbourOrderProblems({\n html,\n origin: place,\n pageUrl: expectedCanonical,\n placeOf: (path) => routePlace(routeByPath.get(path)),\n centreOf,\n })) failures.push(`${route.path}: ${problem}`);\n }\n const maps = areaMapProblems({\n html,\n pageUrl: expectedCanonical,\n isIndexable: (path) => routeByPath.get(path)?.indexing === 'index',\n maxBytes: options.areaMapMaxBytes,\n });\n for (const problem of maps.problems) failures.push(`${route.path}: ${problem}`);\n for (const [path, ids] of maps.baseLayers) {\n const entry = areaMapLayers.get(path) ?? { ids: new Set<string>(), from: route.path };\n for (const id of ids) entry.ids.add(id);\n areaMapLayers.set(path, entry);\n }\n for (const href of extractLinks(html)) {\n const path = internalPath(href, expectedCanonical, origin);\n if (path && !internalLinks.has(path)) internalLinks.set(path, route.path);\n }\n checked.push({ path: route.path, canonical: snapshot.canonical, jsonLd: snapshot.jsonLd.length });\n });\n // Each service-area map's base layer is a cached SVG file holding the groups the page draws.\n for (const [path, { ids, from }] of [...areaMapLayers].sort(([a], [b]) => a.localeCompare(b))) {\n const response = await request.get(path, { maxRedirects: 0 });\n const type = response.headers()['content-type'] ?? '';\n if (response.status() !== 200 || !type.includes('image/svg+xml')) {\n failures.push(`${from}: the map's base layer ${path} answers ${response.status()} ${type || 'without a content type'}`);\n continue;\n }\n const svg = await response.text();\n for (const id of ids) if (!svg.includes(`id=\"${id}\"`)) failures.push(`${from}: the map's base layer ${path} has no group #${id}`);\n }\n for (const [path, from] of [...internalLinks].sort()) {\n if (knownPaths.has(path) || familyPaths.has(path)) continue;\n const redirect = findRedirect(path);\n if (redirect && isExternalDestination(redirect.destination)) continue;\n failures.push(redirect\n ? `${from}: links to ${path}, which redirects; link to ${redirect.destination} directly`\n : `${from}: links to ${path}, which is not a catalogue route`);\n }\n await testInfo.attach('technical-estate.json', {\n body: JSON.stringify({ routes: checked.sort((a, b) => a.path.localeCompare(b.path)), internalLinks: [...internalLinks.keys()].sort() }, null, 2),\n contentType: 'application/json',\n });\n expect(failures).toEqual([]);\n });\n\n test('every redirect resolves in one hop and keeps the query string', async ({ request }, testInfo) => {\n test.setTimeout(timeout);\n const failures: string[] = [];\n const skipped: string[] = [];\n if (scope.scope === 'sample') {\n testInfo.annotations.push({ type: 'estate-scope', description: `sample: ${crawledRedirects.length} of ${redirects.length} redirects (fixed seed); every redirect is checked when the pull request is ready for review` });\n }\n for (const rule of crawledRedirects) {\n const probe = redirectProbe(rule);\n if (!probe) {\n skipped.push(`${rule.source} → ${rule.destination}`);\n continue;\n }\n const sources = [probe.source, ...(options.trailingSlashRedirects === false || probe.source === '/' || probe.source.endsWith('/') ? [] : [`${probe.source}/`])];\n for (const source of sources) {\n for (const method of ['GET', 'HEAD'] as const) {\n const response = await request.fetch(`${source}?brandlab_estate=1`, { method, maxRedirects: 0 });\n const location = response.headers().location;\n const expectedStatus: number[] = source === probe.source ? [rule.status] : [301, 308];\n if (!expectedStatus.includes(response.status()) || !location) {\n failures.push(`${method} ${source}: ${response.status()} (expected ${expectedStatus.join(' or ')} to ${probe.destination})`);\n continue;\n }\n const target = new URL(location, origin);\n if (isExternalDestination(probe.destination)) {\n if (!target.href.startsWith(new URL(probe.destination, origin).href.replace(/\\/$/, ''))) failures.push(`${method} ${source}: Location ${location} (expected ${probe.destination})`);\n continue;\n }\n if (target.pathname !== new URL(probe.destination, origin).pathname) failures.push(`${method} ${source}: Location ${location} (expected ${probe.destination})`);\n if (target.searchParams.get('brandlab_estate') !== '1') failures.push(`${method} ${source}: the query string was dropped`);\n }\n }\n // A destination with parameters names many pages; the sample fills it with a page that does not exist.\n if (!isExternalDestination(probe.destination) && !probe.sampled) {\n const destination = await request.get(probe.destination, { maxRedirects: 0 });\n if (destination.status() !== 200) failures.push(`${rule.source}: destination ${probe.destination} answers ${destination.status()} (a chain or a missing page)`);\n }\n }\n if (skipped.length > 0) {\n testInfo.annotations.push({ type: 'skipped-redirects', description: `${skipped.length} redirect(s) with a parameter pattern that cannot be sampled: ${skipped.join('; ')}` });\n }\n expect(failures).toEqual([]);\n });\n\n if (families.length > 0) {\n test('every page family rendered on request serves its pages and answers 404 outside its space', async ({ request }) => {\n test.setTimeout(timeout);\n const failures: string[] = [];\n const reference = await request.get(`/${UNKNOWN_SLUG}`, { maxRedirects: 0 });\n const notFoundText = reference.status() === 404 ? bodyText(await reference.text()) : '';\n if (!notFoundText) failures.push(`/${UNKNOWN_SLUG}: ${reference.status()} with no server-rendered not-found page to compare with`);\n for (const family of families) {\n const sample = familySamplePath(family, knownPaths);\n if (!sample) {\n failures.push(`${family.pattern}: the family lists no page`);\n continue;\n }\n const response = await request.get(sample, { maxRedirects: 0 });\n if (response.status() !== 200) {\n failures.push(`${sample}: ${response.status()} (expected 200 for a page of ${family.pattern})`);\n } else {\n const html = await response.text();\n const snapshot = extractSearchCertificationHtmlSnapshot(html);\n const robots = `${snapshot.robots ?? ''} ${response.headers()['x-robots-tag'] ?? ''}`;\n const expectedCanonical = canonical(sample);\n if (!snapshot.canonical || new URL(snapshot.canonical, origin).href !== expectedCanonical) {\n failures.push(`${sample}: canonical ${snapshot.canonical ?? 'missing'} (expected ${expectedCanonical})`);\n }\n if (family.indexing === 'index' && /noindex/i.test(robots)) failures.push(`${sample}: the family's pages are indexable, this one is noindex`);\n if (family.indexing !== 'index' && !/noindex/i.test(robots)) failures.push(`${sample}: the family's pages are ${family.indexing}, this one is indexable`);\n }\n for (const outside of familyNotFoundPaths(family)) {\n const miss = await request.get(outside, { maxRedirects: 0 });\n if (miss.status() !== 404) {\n failures.push(`${outside}: ${miss.status()} (expected 404 outside ${family.pattern}'s space)`);\n continue;\n }\n const text = bodyText(await miss.text());\n if (notFoundText && text !== notFoundText) {\n failures.push(`${outside}: the 404 is not the site's not-found page rendered on the server (${text ? `it shows \"${text.slice(0, 80)}\"` : 'its body is empty until the browser renders it'})`);\n }\n }\n }\n expect(failures).toEqual([]);\n });\n }\n\n test('unknown slugs answer 404 instead of inventing pages', async ({ request }, testInfo) => {\n // A default probe a redirect rule covers (a wildcard such as /blog/:slug) is the redirect's to answer; the\n // redirect journey proves it. Paths the site lists itself are always probed.\n const { probes: paths, redirected } = options.notFoundPaths\n ? { probes: options.notFoundPaths, redirected: [] }\n : unknownSlugProbes(defaultNotFoundPaths(routes), (path) => Boolean(findRedirect(path)));\n for (const path of redirected) testInfo.annotations.push({ type: 'skipped', description: `${path}: a redirect rule answers it` });\n const failures: string[] = [];\n for (const path of paths) {\n const [get, head] = await Promise.all([\n request.get(path, { maxRedirects: 0 }),\n request.head(path, { maxRedirects: 0 }),\n ]);\n if (get.status() !== 404) failures.push(`${path}: GET ${get.status()} (expected 404)`);\n if (head.status() !== 404) failures.push(`${path}: HEAD ${head.status()} (expected 404)`);\n }\n expect(failures).toEqual([]);\n });\n\n test('robots.txt allows crawling and names the sitemap', async ({ request }) => {\n const response = await request.get('/robots.txt');\n expect(response.status()).toBe(200);\n const body = await response.text();\n expect(body).toMatch(new RegExp(`^sitemap:\\\\s*${escapeRegExp(`${origin}/sitemap.xml`)}\\\\s*$`, 'im'));\n expect(body).not.toMatch(/^disallow:\\s*\\/\\s*$/im);\n });\n\n test('a production deployment tells crawlers not to index any host but its canonical one', async ({ baseURL }) => {\n test.skip(process.env.VERCEL_ENV !== 'production', 'Only a production build (VERCEL_ENV=production, as CI builds it) answers other hosts with noindex.');\n const server = new URL(baseURL ?? 'http://127.0.0.1:3100/');\n const canonicalHost = new URL(origin).hostname;\n // The production *.vercel.app alias serves the full site; its pages must not be indexed.\n const alias = `${canonicalHost.split('.')[0]}-brandlab-e2e.vercel.app`;\n expect(await responseHeader(server, alias, '/', 'x-robots-tag'), `${alias}: X-Robots-Tag`).toBe('noindex');\n expect(await responseHeader(server, canonicalHost, '/', 'x-robots-tag'), `${canonicalHost}: X-Robots-Tag`).not.toMatch(/noindex/);\n });\n });\n}\n\n/**\n * Why a page's BreadcrumbList JSON-LD would fail Google's breadcrumb rich result (3.3.3): every\n * ListItem but the last needs its `item` (the page it names), as the shared builder requires;\n * Google reports \"Missing field item\" otherwise. Lists inside an `@graph` count; an empty list\n * has nothing to judge.\n */\nexport function breadcrumbListProblems(jsonLd: readonly unknown[]): string[] {\n const lists: Record<string, unknown>[] = [];\n const collect = (value: unknown) => {\n if (Array.isArray(value)) {\n value.forEach(collect);\n return;\n }\n if (!value || typeof value !== 'object') return;\n const node = value as Record<string, unknown>;\n const type = node['@type'];\n if (type === 'BreadcrumbList' || (Array.isArray(type) && type.includes('BreadcrumbList'))) lists.push(node);\n if (Array.isArray(node['@graph'])) collect(node['@graph']);\n };\n collect(jsonLd);\n const problems: string[] = [];\n for (const list of lists) {\n const items = Array.isArray(list.itemListElement) ? list.itemListElement : [];\n items.slice(0, -1).forEach((entry, index) => {\n const item = entry && typeof entry === 'object' ? (entry as Record<string, unknown>).item : undefined;\n const named = typeof item === 'string' ? item.trim() : item && typeof item === 'object' ? String((item as Record<string, unknown>)['@id'] ?? (item as Record<string, unknown>).url ?? '').trim() : '';\n if (named) return;\n const name = entry && typeof entry === 'object' ? (entry as Record<string, unknown>).name : undefined;\n problems.push(`BreadcrumbList item ${index + 1}${typeof name === 'string' && name.trim() ? ` (\"${name.trim()}\")` : ''} has no URL (item); only the last breadcrumb may omit it, or Google rejects the list`);\n });\n }\n return problems;\n}\n\n/** One response header for a request that names its own Host (the Playwright request context always sends the server's). */\nexport function responseHeader(server: URL, host: string, path: string, name: string): Promise<string | undefined> {\n return new Promise((resolve, reject) => {\n const request = httpRequest({ hostname: server.hostname, port: server.port || 80, path, method: 'GET', headers: { host, 'x-forwarded-proto': 'https' } }, (response) => {\n response.resume();\n const value = response.headers[name.toLowerCase()];\n resolve(Array.isArray(value) ? value.join(', ') : value);\n });\n request.on('error', reject);\n request.end();\n });\n}\n\n/**\n * Every `<loc>` in a sitemap, following one level of sitemap index. Nothing is\n * followed through a redirect, and with `origin` every nested sitemap must be\n * on that origin (it is then fetched from the build under test).\n */\nexport async function readSitemap(request: APIRequestContext, path: string, origin?: string): Promise<string[]> {\n const read = async (target: string) => {\n const response = await request.get(target, { maxRedirects: 0 });\n if (response.status() !== 200) throw new Error(`${target} answers ${response.status()}; a sitemap must answer 200 without redirecting.`);\n return response.text();\n };\n const xml = await read(path);\n const locations = extractSitemapLocations(xml);\n if (!/<sitemapindex\\b/i.test(xml)) return locations;\n const nested: string[] = [];\n for (const location of locations) {\n const url = new URL(location);\n if (origin && url.origin !== new URL(origin).origin) {\n throw new Error(`The sitemap index lists ${location}, which is not on ${new URL(origin).origin}.`);\n }\n nested.push(...extractSitemapLocations(await read(`${url.pathname}${url.search}`)));\n }\n return nested;\n}\n\n/**\n * The sitemap's URL files with their entries: the sitemap itself, or every\n * file a sitemap index lists (one level, same origin, no redirects).\n */\nexport async function readSitemapFiles(\n request: APIRequestContext,\n path: string,\n origin: string,\n): Promise<{ index: boolean; files: SitemapFile[] }> {\n const read = async (target: string) => {\n const response = await request.get(target, { maxRedirects: 0 });\n if (response.status() !== 200) throw new Error(`${target} answers ${response.status()}; a sitemap must answer 200 without redirecting.`);\n return response.text();\n };\n const xml = await read(path);\n if (!/<sitemapindex\\b/i.test(xml)) return { index: false, files: [{ path, entries: parseSitemapEntries(xml) }] };\n const files: SitemapFile[] = [];\n for (const location of extractSitemapLocations(xml)) {\n const url = new URL(location);\n if (url.origin !== new URL(origin).origin) throw new Error(`The sitemap index lists ${location}, which is not on ${new URL(origin).origin}.`);\n files.push({ path: url.pathname, entries: parseSitemapEntries(await read(`${url.pathname}${url.search}`)) });\n }\n return { index: true, files };\n}\n\nexport function extractSitemapLocations(xml: string): string[] {\n return [...xml.matchAll(/<loc>\\s*([^<\\s]+)\\s*<\\/loc>/gi)].map((match) =>\n new URL(decodeXml(match[1]!)).href);\n}\n\nexport function defaultNotFoundPaths(routes: readonly BrandlabEstateRoute[]): string[] {\n const parents = new Set(['']);\n for (const route of routes) {\n const segments = normalisePath(route.path).split('/').filter(Boolean);\n if (segments.length > 1) parents.add(`/${segments.slice(0, -1).join('/')}`);\n if (segments.length === 1) parents.add(`/${segments[0]}`);\n }\n return [...parents].sort().map((parent) => `${parent}/${UNKNOWN_SLUG}`);\n}\n\n/** The unknown-slug probes to request, apart from those a redirect rule answers. */\nexport function unknownSlugProbes(paths: readonly string[], redirects: (path: string) => boolean): { probes: string[]; redirected: string[] } {\n const probes: string[] = [];\n const redirected: string[] = [];\n for (const path of paths) (redirects(normalisePath(path)) ? redirected : probes).push(path);\n return { probes, redirected };\n}\n\n/** The page of a family its estate check opens: the first one outside the catalogue, else its first page. */\nexport function familySamplePath(family: Pick<BrandlabEstateFamily, 'paths'>, catalogue: ReadonlySet<string>): string | undefined {\n return family.paths.find((path) => !catalogue.has(normalisePath(path))) ?? family.paths[0];\n}\n\n/**\n * Paths of a family's pattern outside its space: a page of the family with one\n * dynamic segment at a time replaced by an unknown slug.\n */\nexport function familyNotFoundPaths(family: Pick<BrandlabEstateFamily, 'pattern' | 'paths'>): string[] {\n const first = family.paths[0];\n if (!first) return [];\n const parts = normalisePath(first).split('/');\n const segments = family.pattern.split('/');\n const paths = segments.flatMap((segment, index) => (/^\\[[^.\\]]/.test(segment) ? [parts.map((part, position) => (position === index ? UNKNOWN_SLUG : part)).join('/')] : []));\n const space = new Set(family.paths.map(normalisePath));\n return [...new Set(paths)].filter((path) => !space.has(path));\n}\n\n/**\n * The text a page shows before any script runs: its body without scripts,\n * styles and templates, tags removed and whitespace collapsed. Empty for the\n * document Next sends when it leaves a page to the browser to render.\n */\nexport function bodyText(html: string): string {\n const body = /<body\\b[^>]*>([\\s\\S]*?)(?:<\\/body>|$)/i.exec(html)?.[1] ?? (/<html\\b/i.test(html) ? '' : html);\n return decodeXml(body\n .replace(/<(script|style|template|noscript)\\b[\\s\\S]*?<\\/\\1>/gi, ' ')\n .replace(/<[^>]+>/g, ' '))\n .replace(/&nbsp;/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\n/** A Next redirect path with parameters: `:name`, `:name*`, `:name+`, `:name?` or a regular-expression group. */\nexport function isRedirectPattern(path: string): boolean {\n return /:[A-Za-z_]\\w*|\\(/.test(path.replace(/^[a-z][a-z0-9+.-]*:\\/\\//i, ''));\n}\n\n/**\n * The request that proves a redirect rule: a pattern gets one sample segment\n * per parameter, in its source and destination alike. Undefined when a\n * parameter carries its own regular expression, which no sample can satisfy\n * with certainty.\n */\nexport function redirectProbe(rule: Pick<BrandlabEstateRedirect, 'source' | 'destination'>): { source: string; destination: string; sampled: boolean } | undefined {\n const sample = (path: string) => {\n if (!isRedirectPattern(path)) return path;\n // Keep an external destination's scheme and host; parameters live in the path.\n const origin = /^[a-z][a-z0-9+.-]*:\\/\\/[^/]*/i.exec(path)?.[0] ?? '';\n const rest = path.slice(origin.length);\n if (rest.includes('(')) return undefined;\n return `${origin}${rest.replace(/:[A-Za-z_]\\w*[*+?]?/g, PATTERN_SAMPLE)}`;\n };\n const source = sample(rule.source);\n const destination = sample(rule.destination);\n if (source === undefined || destination === undefined) return undefined;\n return { source, destination, sampled: isRedirectPattern(rule.destination) };\n}\n\n/** A matcher for paths a pattern source redirects, for internal-link checks. */\nfunction redirectSourceMatcher(source: string): RegExp | undefined {\n if (source.includes('(')) return undefined;\n const pattern = normalisePath(source).split(/(:[A-Za-z_]\\w*[*+?]?)/).map((part) => {\n const parameter = /^:[A-Za-z_]\\w*([*+?]?)$/.exec(part);\n if (!parameter) return escapeRegExp(part);\n return parameter[1] === '*' || parameter[1] === '+' ? '.+' : parameter[1] === '?' ? '[^/]*' : '[^/]+';\n }).join('');\n return new RegExp(`^${pattern}$`);\n}\n\n/**\n * The advisory page-quality journey: headings, title, description, og:url,\n * implementation wording and structured-data identity for every catalogue\n * page. It reports what it finds (page-quality.json and an annotation) and\n * never fails, because a backend release keeps production's pages as they\n * are; the comparison harness proves them unchanged.\n *\n * // tests/e2e/page-quality.spec.ts\n * defineBrandlabPageQualityTests({ site, routes })\n */\nexport function defineBrandlabPageQualityTests(options: BrandlabPageQualityTestOptions) {\n const origin = new URL(String(options.site.canonicalUrl)).origin;\n const canonical = (path: string) => new URL(path, `${origin}/`).href;\n const businessId = options.businessId ?? `${origin}/#business`;\n const timeout = options.timeout ?? 240_000;\n const routes = options.routes.filter((route) => normalisePath(route.path) !== NOT_FOUND_ROUTE);\n const routeByPath = new Map(routes.map((route) => [normalisePath(route.path), route]));\n const run = pageQualityRunFromEnv();\n\n test.describe('Brandlab page quality (advisory)', () => {\n test('reports page and structured-data quality without failing', async ({ request }, testInfo) => {\n test.skip(!run.run, run.reason);\n test.setTimeout(timeout);\n const findings: BrandlabPageQualityFinding[] = [];\n const geography = await loadBrandlabGeography();\n const centreOf = options.centreOf ?? geography.centreOf;\n const titles = new Map<string, string[]>();\n const descriptions = new Map<string, string[]>();\n const similarityPages: SimilarityPage[] = [];\n const linksFrom = new Map<string, Set<string>>();\n await eachWithConcurrency(routes, options.concurrency ?? 8, async (route) => {\n const response = await request.get(route.path, { maxRedirects: 0 });\n // The estate journey owns status codes; quality is read from the pages that load.\n if (response.status() !== 200) return;\n const html = await response.text();\n const snapshot = extractSearchCertificationHtmlSnapshot(html);\n const add = (kind: BrandlabPageQualityFinding['kind'], detail: string) => findings.push({ path: route.path, kind, detail });\n const h1s = headingTexts(html);\n if (h1s.length !== 1) add('headings', `${h1s.length} h1 headings`);\n if (!snapshot.title) add('title', 'no title');\n if (!snapshot.description) add('description', 'no meta description');\n const indexable = route.indexing === 'index';\n if (indexable && snapshot.title) {\n const formatProblems = titleFormatProblems(snapshot.title);\n if (formatProblems.length > 0) add('title-format', `\"${snapshot.title}\": ${formatProblems.join('; ')}`);\n titles.set(snapshot.title, [...(titles.get(snapshot.title) ?? []), route.path]);\n const place = routePlace(route);\n if (h1s.length === 1) {\n const problems = titleHeadingProblems(snapshot.title, h1s[0]!, place ? geography.placeNames.get(place) : undefined);\n if (problems.length > 0) add('title-h1', problems.join('; '));\n }\n }\n if (indexable && snapshot.description) {\n const problem = descriptionLengthProblem(snapshot.description);\n if (problem) add('description-length', problem);\n descriptions.set(snapshot.description, [...(descriptions.get(snapshot.description) ?? []), route.path]);\n }\n const bytes = Buffer.byteLength(html);\n if (bytes > HTML_MAX_BYTES) add('html-size', `${(bytes / 1_048_576).toFixed(2)} MB of HTML (investigate anything over 1 MB)`);\n const expectedCanonical = canonical(route.canonicalPath ?? route.path);\n const ogUrl = snapshot.openGraph['og:url'];\n if (ogUrl && new URL(ogUrl, origin).href !== expectedCanonical) add('og-url', `og:url ${ogUrl} is not the canonical URL`);\n if (snapshot.implementationCopyMatches.length > 0) add('implementation-wording', snapshot.implementationCopyMatches.join(', '));\n for (const failure of inspectStructuredData(route, snapshot.jsonLd, businessId)) {\n add('structured-data', failure.slice(route.path.length + 2));\n }\n const place = routePlace(route);\n if (indexable && place) similarityPages.push({ path: route.path, place, blocks: textBlocks(html).map(geography.maskPlaces) });\n const targets = new Set<string>();\n for (const href of extractLinks(html)) {\n const path = internalPath(href, expectedCanonical, origin);\n if (path && path !== normalisePath(route.path)) targets.add(path);\n }\n linksFrom.set(normalisePath(route.path), targets);\n });\n // Internal links: every indexable page needs a link from another page, and indexable pages link no noindex location page.\n const inbound = new Map<string, number>();\n for (const [from, targets] of linksFrom) {\n const source = routeByPath.get(from);\n for (const target of targets) {\n inbound.set(target, (inbound.get(target) ?? 0) + 1);\n const linked = routeByPath.get(target);\n if (source?.indexing === 'index' && linked && linked.indexing !== 'index' && LOCATION_KINDS.has(linked.kind ?? '')) {\n findings.push({ path: from, kind: 'noindex-link', detail: `links to ${target}, a noindex location page (list unindexed places as plain text)` });\n }\n }\n }\n for (const route of routes) {\n const path = normalisePath(route.path);\n if (route.indexing === 'index' && route.kind !== 'home' && path !== '/' && linksFrom.has(path) && !inbound.get(path)) {\n findings.push({ path: route.path, kind: 'orphan', detail: 'no other catalogue page links to it' });\n }\n }\n for (const [title, paths] of titles) {\n if (paths.length > 1) for (const path of paths) findings.push({ path, kind: 'title-duplicate', detail: `\"${title}\" is also the title of ${paths.filter((other) => other !== path).slice(0, 3).join(', ')}${paths.length > 4 ? ' and others' : ''}` });\n }\n for (const [description, paths] of descriptions) {\n if (paths.length > 1) for (const path of paths) findings.push({ path, kind: 'description-duplicate', detail: `the meta description is shared with ${paths.length - 1} other page(s) (\"${description.slice(0, 60)}…\")` });\n }\n // Location pages: centerpiece similarity against near siblings, by route kind.\n const similarity = [...new Set(similarityPages.map((page) => routeByPath.get(normalisePath(page.path))?.kind))]\n .flatMap((kind) => centerpieceSimilarity(similarityPages.filter((page) => routeByPath.get(normalisePath(page.path))?.kind === kind), centreOf));\n for (const score of similarity) {\n if (score.score < SIMILARITY_WARN) continue;\n findings.push({\n path: score.path,\n kind: 'similarity',\n detail: `${score.score.toFixed(2)} of its page-specific phrases also appear on ${score.sibling ?? 'a near sibling'}${score.score >= SIMILARITY_REVIEW ? ' (at or above 0.7: on the review list; the page stays indexed)' : ' (at or above 0.5: add page-specific evidence)'}`,\n });\n }\n const reviewList = buildLocationReviewList(routes);\n const flagged = new Set(similarity.filter((score) => score.score >= SIMILARITY_REVIEW).map((score) => score.path));\n const review = [\n ...reviewList.map((item) => ({ ...item, reasons: [...item.reasons, ...(flagged.has(item.path) ? ['centerpiece similarity at or above 0.7'] : [])] })),\n ...[...flagged].filter((path) => !reviewList.some((item) => item.path === path)).map((path) => ({ path, evidence: routeByPath.get(normalisePath(path))?.locationDecision?.evidence ?? [], reasons: ['centerpiece similarity at or above 0.7'] })),\n ].sort((a, b) => a.path.localeCompare(b.path));\n for (const item of review) findings.push({ path: item.path, kind: 'location-review', detail: item.reasons.join('; ') });\n findings.sort((a, b) => a.path.localeCompare(b.path) || a.kind.localeCompare(b.kind));\n const byKind = Object.fromEntries(\n [...new Set(findings.map((finding) => finding.kind))].sort().map((kind) => [kind, findings.filter((finding) => finding.kind === kind).length]),\n );\n const reports = {\n 'page-quality.json': JSON.stringify({ pages: routes.length, findings: findings.length, byKind, details: findings }, null, 2),\n 'location-review.json': JSON.stringify({\n reviewed: review.length,\n note: 'Pages on the review list stay indexed. Each review ends in adding evidence, merging into the nearest owner or hiding with noindex, follow; merging and hiding are Romie\\'s decision.',\n similarity: similarity.sort((a, b) => b.score - a.score),\n review,\n }, null, 2),\n };\n for (const [name, body] of Object.entries(reports)) await testInfo.attach(name, { body, contentType: 'application/json' });\n // CI's page-quality run keeps the reports beside the ready report (BRANDLAB_PAGE_QUALITY_DIR=docs/ready).\n if (run.directory) {\n mkdirSync(run.directory, { recursive: true });\n for (const [name, body] of Object.entries(reports)) writeFileSync(join(run.directory, name), `${body}\\n`);\n }\n testInfo.annotations.push({\n type: 'advisory',\n description: `${findings.length} page-quality finding(s) across ${routes.length} page(s)${findings.length ? `: ${Object.entries(byKind).map(([kind, count]) => `${kind} ${count}`).join(', ')}` : ''}; see page-quality.json.`,\n });\n });\n });\n}\n\n/**\n * Whether the advisory page-quality crawl runs (3.13.0, client CI v2). CI v2 runs it on its own when a pull request\n * is ready for review, on a manual run and monthly for large estates, and sets `BRANDLAB_PAGE_QUALITY=skip` for the required journeys so\n * drafts and merges do not crawl every page twice. `BRANDLAB_PAGE_QUALITY_DIR` also writes its reports there.\n */\nexport function pageQualityRunFromEnv(env: NodeJS.ProcessEnv = process.env): { run: boolean; reason: string; directory?: string } {\n const directory = env.BRANDLAB_PAGE_QUALITY_DIR?.trim() || undefined;\n if ((env.BRANDLAB_PAGE_QUALITY ?? '').trim() === 'skip') {\n return { run: false, reason: 'The advisory page-quality crawl runs on its own in CI (ready for review, manual runs and monthly for large estates); BRANDLAB_PAGE_QUALITY=skip.' };\n }\n return { run: true, reason: '', ...(directory ? { directory } : {}) };\n}\n\nfunction inspectStructuredData(route: BrandlabEstateRoute, payloads: readonly unknown[], businessId: string | false): string[] {\n const failures: string[] = [];\n if (payloads.some((payload) => (payload as { parseError?: boolean }).parseError)) failures.push(`${route.path}: invalid JSON-LD`);\n if (route.indexing === 'index' && payloads.length === 0) failures.push(`${route.path}: no JSON-LD`);\n const ids: string[] = [];\n let referencesBusiness = false;\n const visit = (value: unknown) => {\n if (!value || typeof value !== 'object') return;\n if (Array.isArray(value)) { value.forEach(visit); return; }\n const node = value as Record<string, unknown>;\n const types = ([] as unknown[]).concat(node['@type'] ?? []).map(String);\n if (typeof node['@id'] === 'string') {\n if (node['@id'] === businessId) referencesBusiness = true;\n if (types.length > 0) ids.push(node['@id']);\n }\n if (businessId && types.some((type) => BUSINESS_TYPES.has(type)) && node['@id'] !== businessId) {\n failures.push(`${route.path}: business node @id ${String(node['@id'] ?? 'missing')} (expected ${businessId})`);\n }\n Object.values(node).forEach(visit);\n };\n payloads.forEach(visit);\n const duplicate = ids.find((id, index) => ids.indexOf(id) !== index);\n if (duplicate) failures.push(`${route.path}: JSON-LD repeats node ${duplicate}`);\n if (businessId && route.indexing === 'index' && payloads.length > 0 && !referencesBusiness) failures.push(`${route.path}: JSON-LD does not reference ${businessId}`);\n return failures;\n}\n\nfunction extractLinks(html: string): string[] {\n return [...html.matchAll(/<a\\b[^>]*\\bhref=[\"']([^\"']+)[\"']/gi)].map((match) => decodeXml(match[1]!));\n}\n\nfunction internalPath(href: string, base: string, origin: string): string | null {\n if (/^(?:mailto|tel|sms|javascript):/i.test(href) || href.startsWith('#')) return null;\n let url: URL;\n try { url = new URL(href, base); } catch { return null; }\n if (url.origin !== origin && !['127.0.0.1', 'localhost'].includes(url.hostname)) return null;\n if (ASSET_PATH.test(url.pathname) || url.pathname.startsWith('/api/') || url.pathname.startsWith('/_next/')) return null;\n return normalisePath(url.pathname);\n}\n\nfunction normalisePath(path: string): string {\n const pathname = path.split(/[?#]/)[0] || '/';\n return pathname.length > 1 ? pathname.replace(/\\/+$/, '') : '/';\n}\n\n/**\n * Absolute (`https://host`) and protocol-relative (`//host`) destinations leave\n * the site; browsers also read `/\\host` and `\\\\host` as protocol-relative.\n */\nexport function isExternalDestination(destination: string): boolean {\n return /^(?:[a-z][a-z0-9+.-]*:)?[\\\\/]{2}/i.test(destination.trim());\n}\n\nfunction decodeXml(value: string): string {\n return value.replace(/&amp;/g, '&').replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&quot;/g, '\"').replace(/&#39;/g, \"'\");\n}\n\nfunction escapeRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\nasync function eachWithConcurrency<T>(items: readonly T[], limit: number, task: (item: T) => Promise<void>) {\n const queue = [...items];\n await Promise.all(Array.from({ length: Math.max(1, Math.min(limit, queue.length)) }, async () => {\n for (let item = queue.shift(); item !== undefined; item = queue.shift()) await task(item);\n }));\n}\n"]}
1
+ {"version":3,"file":"estate.js","sourceRoot":"","sources":["../src/estate.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The shared technical-estate journey for a Brandlab production build:\n *\n * // tests/e2e/estate.spec.ts\n * import { defineBrandlabEstateTests } from '@brandlab-digital/conformance/estate'\n * import { site } from '../../src/config/site'\n * import { routes } from '../../src/data/routes'\n * import { redirects } from '../../src/data/redirects'\n *\n * defineBrandlabEstateTests({ site, routes, redirects })\n *\n * It asserts the estate and its routes: the sitemap, each route's status,\n * canonical, indexing and breadcrumb JSON-LD (every item but the last names its\n * page, from 3.3.3), internal links, redirects, unknown slugs, the page families\n * rendered on request and robots.txt. Page and structured-data\n * quality is reported, never enforced,\n * by defineBrandlabPageQualityTests: a backend release keeps production's pages\n * as they are, which the comparison harness proves. Counts are always derived\n * from the current catalogue, never a historic constant.\n *\n * From 3.4.0 (the Brandlab on-page and location standard) the estate journey\n * also fails on: a sitemap that is not split by page family once the catalogue\n * spans families, or whose lastmod is not the route's content date; an\n * indexable page without a title, meta description or JSON-LD in its server HTML; FAQPage or\n * HowTo markup, or a rating on the business itself; a homepage without a\n * WebSite node (name and url); an unknown slug that does not answer 404 to GET\n * and HEAD; a nearby list on a location page that is not ranked by distance\n * between suburb centres; and a catalogue that adds indexable location pages\n * beyond the recorded capacity (`locationCapacity`). From 3.6.0 it also fails\n * on a service-area map (`<figure data-area-map>`) without a caption carrying\n * the CC BY boundary credit, with a link that is a tab stop or does not go to an\n * indexable catalogue page, with more than `areaMapMaxBytes` inline, or whose\n * base-layer file does not answer as SVG with the groups it uses. The page-quality journey\n * reports title, heading, description and HTML-size warnings, orphan pages,\n * links from indexable pages to noindex location pages, centerpiece\n * similarity (warn at 0.5, review list at 0.7) and the location review list\n * (location-review.json); pages on that list stay indexed.\n *\n * From 3.13.0 (client CI v2) a draft pull request sets BRANDLAB_ESTATE_SCOPE=sample:\n * the catalogue and redirect journeys check the hubs, the first and last route of\n * each kind, the routes changed files feed and a fixed-seed sample (estate-sample.ts),\n * and say so in an `estate-scope` annotation. Every other run crawls the full estate.\n * BRANDLAB_PAGE_QUALITY=skip skips the advisory crawl, which CI v2 runs on its own.\n */\nimport { mkdirSync, writeFileSync } from 'node:fs';\nimport { request as httpRequest } from 'node:http';\nimport { join } from 'node:path';\nimport { expect, test, type APIRequestContext } from '@playwright/test';\nimport {\n areaMapProblems,\n buildLocationReviewList,\n centerpieceSimilarity,\n checkLocationCapacity,\n descriptionLengthProblem,\n HTML_MAX_BYTES,\n headingTexts,\n loadBrandlabGeography,\n neighbourOrderProblems,\n parseSitemapEntries,\n SIMILARITY_REVIEW,\n SIMILARITY_WARN,\n sitemapFamilyProblems,\n sitemapLastmodProblems,\n structuredDataProblems,\n textBlocks,\n titleFormatProblems,\n titleHeadingProblems,\n type LocationCapacityRecord,\n type SimilarityPage,\n type SitemapFile,\n type SuburbCentreLookup,\n} from './page-standard.js';\nimport { extractSearchCertificationHtmlSnapshot } from './search-certification.js';\nimport { changedFilesFromEnv, estateScopeFromEnv, sampleBySeed, sampleEstateRoutes, type BrandlabEstateSampleOptions } from './estate-sample.js';\n\nexport {\n changedFilesFromEnv,\n changedFileStem,\n defaultRoutesForFile,\n estateScopeFromEnv,\n sampleBySeed,\n sampleEstateRoutes,\n type BrandlabEstateSample,\n type BrandlabEstateSampleOptions,\n type BrandlabEstateSampleRoute,\n type BrandlabEstateScope,\n} from './estate-sample.js';\n\nexport interface BrandlabEstateRoute {\n path: string;\n canonicalPath?: string;\n indexing: 'index' | 'noindex-follow' | 'noindex-nofollow';\n /** The route kind (a catalogue route has it): sitemap families and location checks read it. */\n kind?: string;\n id?: string;\n /** The catalogue parent: a location page other location pages sit under is a region page. */\n parentId?: string;\n /** The content date the sitemap's lastmod must carry. */\n lastSignificantChange?: string;\n locationDecision?: { indexing?: string; evidence?: readonly string[]; review?: string };\n}\n\nexport interface BrandlabEstateRedirect {\n source: string;\n destination: string;\n status: 301 | 302 | 303 | 307 | 308;\n /** Host-scoped alias rules are proved by the comparison harness host probes. */\n host?: string;\n}\n\n/**\n * A page family the site renders on request outside the catalogue: a dynamic\n * route that prerenders nothing and renders every page of its space (every\n * combination of its values) when it is first requested.\n */\nexport interface BrandlabEstateFamily {\n /** The app route, e.g. `/services/[service]/[location]`. */\n pattern: string;\n /** Every page of the family's space; catalogued pages of the route may be among them. */\n paths: readonly string[];\n /** The indexing its pages outside the catalogue render. */\n indexing: BrandlabEstateRoute['indexing'];\n}\n\nexport interface BrandlabEstateTestOptions {\n site: { canonicalUrl: string | URL };\n /** The route catalogue. A `/404` route is Next's not-found page and must answer 404. */\n routes: readonly BrandlabEstateRoute[];\n /**\n * The redirect table. A source with parameters (`/old/:slug`, `:path*`) is\n * requested with one sample segment per parameter and checked against its\n * destination filled in the same way; a parameter with its own regular\n * expression cannot be sampled and is listed as skipped.\n */\n redirects?: readonly BrandlabEstateRedirect[];\n /** Paths that must answer 404; defaults to one unknown slug under every catalogue parent. */\n notFoundPaths?: readonly string[];\n /** Also prove `<source>/` redirects straight to the destination (the shared helper emits these). */\n trailingSlashRedirects?: boolean;\n /**\n * Page families rendered on request outside the catalogue. Links into a\n * family's space count as live pages; one page of each family (outside the\n * catalogue where it has one) is checked for its status, canonical and\n * indexing; and a path of its pattern outside the space, one unknown value\n * per dynamic segment, must answer 404 with the site's not-found page rendered\n * on the server: the text an unknown top-level path's 404 page shows.\n */\n families?: readonly BrandlabEstateFamily[];\n /**\n * The site's recorded location capacity (site-core/location-policy). The\n * journey fails only when the catalogue holds more indexable location pages\n * than the capacity and the pages at record allow; without one it notes that\n * none is recorded.\n */\n locationCapacity?: LocationCapacityRecord | null;\n /** The business node's @id (default `<origin>/#business`); `false` when the site has none. */\n businessId?: string | false;\n /** The business facts record: the business node's name and telephone must match it. */\n business?: { name?: string; telephone?: string };\n /** A place's centre for neighbour order (default: the registry's suburb centres). */\n centreOf?: SuburbCentreLookup;\n /**\n * The site's estate plan (site-core `siteStrategy.estate`, 3.14.0): its public slugs map a location page to the\n * registry suburb it names (`ryde-suburb` → `ryde`) and mark region pages (`ryde`), which are not ranked by distance.\n */\n estate?: BrandlabEstateSlugPlan | null;\n /** The most inline markup one service-area map may carry (default 60 000 bytes, 3.6.0). */\n areaMapMaxBytes?: number;\n /**\n * The sampled crawl a draft pull request runs (`BRANDLAB_ESTATE_SCOPE=sample`, 3.13.0): how many routes and\n * redirects the fixed seed adds, the seed, and which routes a changed file feeds.\n */\n sample?: BrandlabEstateSampleOptions;\n concurrency?: number;\n timeout?: number;\n}\n\nexport interface BrandlabPageQualityTestOptions {\n site: { canonicalUrl: string | URL };\n routes: readonly BrandlabEstateRoute[];\n /** The @id every indexable page's graph references; defaults to `<origin>/#business`, `false` skips it. */\n businessId?: string | false;\n /** A place's centre for similarity siblings (default: the registry's suburb centres). */\n centreOf?: SuburbCentreLookup;\n /** The site's estate plan, as for the estate journey: public slugs resolve to the places they name. */\n estate?: BrandlabEstateSlugPlan | null;\n concurrency?: number;\n timeout?: number;\n}\n\nexport interface BrandlabPageQualityFinding {\n path: string;\n kind:\n | 'headings'\n | 'title'\n | 'title-format'\n | 'title-duplicate'\n | 'title-h1'\n | 'description'\n | 'description-length'\n | 'description-duplicate'\n | 'html-size'\n | 'similarity'\n | 'location-review'\n | 'orphan'\n | 'noindex-link'\n | 'og-url'\n | 'implementation-wording'\n | 'structured-data';\n detail: string;\n}\n\nconst LOCATION_KINDS = new Set(['location', 'service-location']);\n\n/** The public-slug part of site-core's SiteEstatePlan (mirrored: conformance compiles without site-core). */\nexport interface BrandlabEstateSlugPlan {\n slugs?: {\n /** Registry region slug → public slug, for example `{ 'ryde-area': 'ryde' }`. */\n regions?: Readonly<Record<string, string>>;\n /** Registry suburb slug → public slug, for example `{ ryde: 'ryde-suburb' }`. */\n suburbs?: Readonly<Record<string, string>>;\n };\n}\n\n/**\n * A location route's place: the registry suburb slug its last path segment names (through the estate plan's public\n * slugs, as site-core's `estatePublicSlug`), and whether it is a region page. A region page has no centre, so its\n * nearby lists are not ranked by distance: it is a region when other location pages sit under it, when its slug is a\n * region's public slug, or when its slug is a suburb's registry slug that the plan moved elsewhere (AA: `ryde`).\n */\nexport function estatePlaceResolver(\n routes: readonly BrandlabEstateRoute[],\n estate?: BrandlabEstateSlugPlan | null,\n): (route: BrandlabEstateRoute | undefined) => { slug: string; region: boolean } | undefined {\n const parents = new Set(routes.filter((route) => LOCATION_KINDS.has(route.kind ?? '')).map((route) => route.parentId));\n const regionSlugs = new Set(Object.values(estate?.slugs?.regions ?? {}));\n const suburbRenames = estate?.slugs?.suburbs ?? {};\n const suburbByPublic = new Map(Object.entries(suburbRenames).map(([registry, publicSlug]) => [publicSlug, registry]));\n return (route) => {\n if (!route || !LOCATION_KINDS.has(route.kind ?? '')) return undefined;\n const publicSlug = normalisePath(route.path).split('/').filter(Boolean).at(-1);\n if (!publicSlug) return undefined;\n const suburb = suburbByPublic.get(publicSlug);\n const region =\n (route.id !== undefined && parents.has(route.id)) ||\n regionSlugs.has(publicSlug) ||\n (suburb === undefined && Object.hasOwn(suburbRenames, publicSlug));\n return { slug: suburb ?? publicSlug, region };\n };\n}\n\n\nconst BUSINESS_TYPES = new Set([\n 'LocalBusiness', 'HomeAndConstructionBusiness', 'GeneralContractor', 'Plumber', 'Electrician',\n 'RoofingContractor', 'HousePainter', 'Locksmith', 'MovingCompany', 'HVACBusiness', 'ProfessionalService',\n]);\nconst UNKNOWN_SLUG = 'brandlab-estate-unknown-page';\nconst NOT_FOUND_ROUTE = '/404';\nconst PATTERN_SAMPLE = 'brandlab-estate-sample';\nconst ASSET_PATH = /\\.[a-z0-9]{2,5}$/i;\n\nexport function defineBrandlabEstateTests(options: BrandlabEstateTestOptions) {\n const origin = new URL(String(options.site.canonicalUrl)).origin;\n const canonical = (path: string) => new URL(path, `${origin}/`).href;\n const routes = options.routes;\n const knownPaths = new Set(routes.map((route) => normalisePath(route.path)));\n const redirects = (options.redirects ?? []).filter((rule) => !rule.host);\n const redirectBySource = new Map(\n redirects.filter((rule) => !isRedirectPattern(rule.source)).map((rule) => [normalisePath(rule.source), rule]),\n );\n const patternRedirects = redirects.flatMap((rule) => {\n const matcher = isRedirectPattern(rule.source) ? redirectSourceMatcher(rule.source) : undefined;\n return matcher ? [{ rule, matcher }] : [];\n });\n const findRedirect = (path: string) =>\n redirectBySource.get(path) ?? patternRedirects.find(({ matcher }) => matcher.test(path))?.rule;\n const timeout = options.timeout ?? 240_000;\n const families = options.families ?? [];\n const familyPaths = new Set(families.flatMap((family) => family.paths.map(normalisePath)));\n const routeByPath = new Map(routes.map((route) => [normalisePath(route.path), route]));\n const placeOfRoute = estatePlaceResolver(routes, options.estate);\n const homePath = normalisePath(routes.find((route) => route.kind === 'home')?.path ?? '/');\n // A draft pull request crawls a sample (BRANDLAB_ESTATE_SCOPE=sample); ready for review crawls every route.\n const scope = estateScopeFromEnv();\n const sample = sampleEstateRoutes(routes, { scope: scope.scope, changedFiles: changedFilesFromEnv(), ...options.sample });\n const scopeNote = [sample.summary, scope.note].filter(Boolean).join('; ');\n const crawledRedirects = scope.scope === 'sample' ? sampleBySeed(redirects, (rule) => rule.source, options.sample) : redirects;\n\n test.describe('Brandlab technical estate', () => {\n test('the sitemap lists exactly the indexable catalogue routes', async ({ request }) => {\n test.setTimeout(timeout);\n const listed = await readSitemap(request, '/sitemap.xml', origin);\n const expected = routes\n .filter((route) => route.indexing === 'index')\n .map((route) => canonical(route.canonicalPath ?? route.path));\n expect(listed.length, 'the sitemap repeats a URL').toBe(new Set(listed).size);\n expect([...listed].sort()).toEqual([...new Set(expected)].sort());\n });\n\n test('the sitemap is split by page family and dated by content, never by the build', async ({ request }, testInfo) => {\n test.setTimeout(timeout);\n const { index, files } = await readSitemapFiles(request, '/sitemap.xml', origin);\n const problems = [\n ...sitemapFamilyProblems({ index, files, routes, origin }),\n ...sitemapLastmodProblems(files.flatMap((file) => file.entries), routes, origin),\n ];\n await testInfo.attach('sitemap-files.json', {\n body: JSON.stringify(files.map((file) => ({ path: file.path, urls: file.entries.length })), null, 2),\n contentType: 'application/json',\n });\n expect(problems).toEqual([]);\n });\n\n test('location pages stay within the recorded capacity', async ({}, testInfo) => {\n const result = checkLocationCapacity(routes, options.locationCapacity);\n testInfo.annotations.push({ type: result.limit === null ? 'warning' : 'location-capacity', description: result.detail });\n expect(result.blocked, result.detail).toBe(false);\n });\n\n test('every catalogue route serves its canonical identity and links only to live pages', async ({ request }, testInfo) => {\n test.setTimeout(timeout);\n const centreOf = options.centreOf ?? (await loadBrandlabGeography()).centreOf;\n const failures: string[] = [];\n const internalLinks = new Map<string, string>();\n const checked: { path: string; canonical: string | null; jsonLd: number }[] = [];\n const areaMapLayers = new Map<string, { ids: Set<string>; from: string }>();\n testInfo.annotations.push({ type: 'estate-scope', description: scopeNote });\n await eachWithConcurrency(sample.routes, options.concurrency ?? 8, async (route) => {\n const [get, head] = await Promise.all([\n request.get(route.path, { maxRedirects: 0 }),\n request.head(route.path, { maxRedirects: 0 }),\n ]);\n // Next serves its not-found page at /404 with a 404 status.\n const notFound = normalisePath(route.path) === NOT_FOUND_ROUTE;\n const expectedStatus = notFound ? 404 : 200;\n if (get.status() !== expectedStatus || head.status() !== expectedStatus) {\n failures.push(`${route.path}: GET ${get.status()}, HEAD ${head.status()} (expected ${expectedStatus})`);\n return;\n }\n const html = await get.text();\n const snapshot = extractSearchCertificationHtmlSnapshot(html);\n const robots = `${snapshot.robots ?? ''} ${get.headers()['x-robots-tag'] ?? ''}`;\n if (notFound) {\n if (!/noindex/i.test(robots)) failures.push(`${route.path}: the not-found page is indexable`);\n checked.push({ path: route.path, canonical: snapshot.canonical, jsonLd: snapshot.jsonLd.length });\n return;\n }\n const expectedCanonical = canonical(route.canonicalPath ?? route.path);\n const canonicalTags = html.match(/<link\\b[^>]*\\brel=[\"']canonical[\"'][^>]*>/gi) ?? [];\n if (canonicalTags.length !== 1) failures.push(`${route.path}: ${canonicalTags.length} canonical links`);\n if (!snapshot.canonical || new URL(snapshot.canonical, origin).href !== expectedCanonical) {\n failures.push(`${route.path}: canonical ${snapshot.canonical ?? 'missing'} (expected ${expectedCanonical})`);\n }\n if (route.indexing === 'index' && /noindex/i.test(robots)) failures.push(`${route.path}: indexable route is noindex`);\n if (route.indexing !== 'index' && !/noindex/i.test(robots)) failures.push(`${route.path}: ${route.indexing} route is indexable`);\n for (const problem of breadcrumbListProblems(snapshot.jsonLd)) failures.push(`${route.path}: ${problem}`);\n if (route.indexing === 'index') {\n if (!snapshot.title) failures.push(`${route.path}: no <title> in the server HTML`);\n if (!snapshot.description) failures.push(`${route.path}: no meta description in the server HTML`);\n if (snapshot.jsonLd.length === 0) failures.push(`${route.path}: no JSON-LD in the server HTML`);\n }\n if (snapshot.jsonLd.some((payload) => (payload as { parseError?: boolean }).parseError)) failures.push(`${route.path}: invalid JSON-LD`);\n for (const problem of structuredDataProblems(snapshot.jsonLd, {\n origin,\n home: normalisePath(route.path) === homePath,\n businessId: options.businessId,\n business: options.business,\n })) failures.push(`${route.path}: ${problem}`);\n const place = placeOfRoute(route);\n if (place && !place.region && centreOf(place.slug)) {\n for (const problem of neighbourOrderProblems({\n html,\n origin: place.slug,\n pageUrl: expectedCanonical,\n placeOf: (path) => {\n const linked = placeOfRoute(routeByPath.get(path));\n return linked && !linked.region ? linked.slug : undefined;\n },\n centreOf,\n })) failures.push(`${route.path}: ${problem}`);\n }\n const maps = areaMapProblems({\n html,\n pageUrl: expectedCanonical,\n isIndexable: (path) => routeByPath.get(path)?.indexing === 'index',\n maxBytes: options.areaMapMaxBytes,\n });\n for (const problem of maps.problems) failures.push(`${route.path}: ${problem}`);\n for (const [path, ids] of maps.baseLayers) {\n const entry = areaMapLayers.get(path) ?? { ids: new Set<string>(), from: route.path };\n for (const id of ids) entry.ids.add(id);\n areaMapLayers.set(path, entry);\n }\n for (const href of extractLinks(html)) {\n const path = internalPath(href, expectedCanonical, origin);\n if (path && !internalLinks.has(path)) internalLinks.set(path, route.path);\n }\n checked.push({ path: route.path, canonical: snapshot.canonical, jsonLd: snapshot.jsonLd.length });\n });\n // Each service-area map's base layer is a cached SVG file holding the groups the page draws.\n for (const [path, { ids, from }] of [...areaMapLayers].sort(([a], [b]) => a.localeCompare(b))) {\n const response = await request.get(path, { maxRedirects: 0 });\n const type = response.headers()['content-type'] ?? '';\n if (response.status() !== 200 || !type.includes('image/svg+xml')) {\n failures.push(`${from}: the map's base layer ${path} answers ${response.status()} ${type || 'without a content type'}`);\n continue;\n }\n const svg = await response.text();\n for (const id of ids) if (!svg.includes(`id=\"${id}\"`)) failures.push(`${from}: the map's base layer ${path} has no group #${id}`);\n }\n for (const [path, from] of [...internalLinks].sort()) {\n if (knownPaths.has(path) || familyPaths.has(path)) continue;\n const redirect = findRedirect(path);\n if (redirect && isExternalDestination(redirect.destination)) continue;\n failures.push(redirect\n ? `${from}: links to ${path}, which redirects; link to ${redirect.destination} directly`\n : `${from}: links to ${path}, which is not a catalogue route`);\n }\n await testInfo.attach('technical-estate.json', {\n body: JSON.stringify({ routes: checked.sort((a, b) => a.path.localeCompare(b.path)), internalLinks: [...internalLinks.keys()].sort() }, null, 2),\n contentType: 'application/json',\n });\n expect(failures).toEqual([]);\n });\n\n test('every redirect resolves in one hop and keeps the query string', async ({ request }, testInfo) => {\n test.setTimeout(timeout);\n const failures: string[] = [];\n const skipped: string[] = [];\n if (scope.scope === 'sample') {\n testInfo.annotations.push({ type: 'estate-scope', description: `sample: ${crawledRedirects.length} of ${redirects.length} redirects (fixed seed); every redirect is checked when the pull request is ready for review` });\n }\n for (const rule of crawledRedirects) {\n const probe = redirectProbe(rule);\n if (!probe) {\n skipped.push(`${rule.source} → ${rule.destination}`);\n continue;\n }\n const sources = [probe.source, ...(options.trailingSlashRedirects === false || probe.source === '/' || probe.source.endsWith('/') ? [] : [`${probe.source}/`])];\n for (const source of sources) {\n for (const method of ['GET', 'HEAD'] as const) {\n const response = await request.fetch(`${source}?brandlab_estate=1`, { method, maxRedirects: 0 });\n const location = response.headers().location;\n const expectedStatus: number[] = source === probe.source ? [rule.status] : [301, 308];\n if (!expectedStatus.includes(response.status()) || !location) {\n failures.push(`${method} ${source}: ${response.status()} (expected ${expectedStatus.join(' or ')} to ${probe.destination})`);\n continue;\n }\n const target = new URL(location, origin);\n if (isExternalDestination(probe.destination)) {\n if (!target.href.startsWith(new URL(probe.destination, origin).href.replace(/\\/$/, ''))) failures.push(`${method} ${source}: Location ${location} (expected ${probe.destination})`);\n continue;\n }\n if (target.pathname !== new URL(probe.destination, origin).pathname) failures.push(`${method} ${source}: Location ${location} (expected ${probe.destination})`);\n if (target.searchParams.get('brandlab_estate') !== '1') failures.push(`${method} ${source}: the query string was dropped`);\n }\n }\n // A destination with parameters names many pages; the sample fills it with a page that does not exist.\n if (!isExternalDestination(probe.destination) && !probe.sampled) {\n const destination = await request.get(probe.destination, { maxRedirects: 0 });\n if (destination.status() !== 200) failures.push(`${rule.source}: destination ${probe.destination} answers ${destination.status()} (a chain or a missing page)`);\n }\n }\n if (skipped.length > 0) {\n testInfo.annotations.push({ type: 'skipped-redirects', description: `${skipped.length} redirect(s) with a parameter pattern that cannot be sampled: ${skipped.join('; ')}` });\n }\n expect(failures).toEqual([]);\n });\n\n if (families.length > 0) {\n test('every page family rendered on request serves its pages and answers 404 outside its space', async ({ request }) => {\n test.setTimeout(timeout);\n const failures: string[] = [];\n const reference = await request.get(`/${UNKNOWN_SLUG}`, { maxRedirects: 0 });\n const notFoundText = reference.status() === 404 ? bodyText(await reference.text()) : '';\n if (!notFoundText) failures.push(`/${UNKNOWN_SLUG}: ${reference.status()} with no server-rendered not-found page to compare with`);\n for (const family of families) {\n const sample = familySamplePath(family, knownPaths);\n if (!sample) {\n failures.push(`${family.pattern}: the family lists no page`);\n continue;\n }\n const response = await request.get(sample, { maxRedirects: 0 });\n if (response.status() !== 200) {\n failures.push(`${sample}: ${response.status()} (expected 200 for a page of ${family.pattern})`);\n } else {\n const html = await response.text();\n const snapshot = extractSearchCertificationHtmlSnapshot(html);\n const robots = `${snapshot.robots ?? ''} ${response.headers()['x-robots-tag'] ?? ''}`;\n const expectedCanonical = canonical(sample);\n if (!snapshot.canonical || new URL(snapshot.canonical, origin).href !== expectedCanonical) {\n failures.push(`${sample}: canonical ${snapshot.canonical ?? 'missing'} (expected ${expectedCanonical})`);\n }\n if (family.indexing === 'index' && /noindex/i.test(robots)) failures.push(`${sample}: the family's pages are indexable, this one is noindex`);\n if (family.indexing !== 'index' && !/noindex/i.test(robots)) failures.push(`${sample}: the family's pages are ${family.indexing}, this one is indexable`);\n }\n for (const outside of familyNotFoundPaths(family)) {\n const miss = await request.get(outside, { maxRedirects: 0 });\n if (miss.status() !== 404) {\n failures.push(`${outside}: ${miss.status()} (expected 404 outside ${family.pattern}'s space)`);\n continue;\n }\n const text = bodyText(await miss.text());\n if (notFoundText && text !== notFoundText) {\n failures.push(`${outside}: the 404 is not the site's not-found page rendered on the server (${text ? `it shows \"${text.slice(0, 80)}\"` : 'its body is empty until the browser renders it'})`);\n }\n }\n }\n expect(failures).toEqual([]);\n });\n }\n\n test('unknown slugs answer 404 instead of inventing pages', async ({ request }, testInfo) => {\n // A default probe a redirect rule covers (a wildcard such as /blog/:slug) is the redirect's to answer; the\n // redirect journey proves it. Paths the site lists itself are always probed.\n const { probes: paths, redirected } = options.notFoundPaths\n ? { probes: options.notFoundPaths, redirected: [] }\n : unknownSlugProbes(defaultNotFoundPaths(routes), (path) => Boolean(findRedirect(path)));\n for (const path of redirected) testInfo.annotations.push({ type: 'skipped', description: `${path}: a redirect rule answers it` });\n const failures: string[] = [];\n for (const path of paths) {\n const [get, head] = await Promise.all([\n request.get(path, { maxRedirects: 0 }),\n request.head(path, { maxRedirects: 0 }),\n ]);\n if (get.status() !== 404) failures.push(`${path}: GET ${get.status()} (expected 404)`);\n if (head.status() !== 404) failures.push(`${path}: HEAD ${head.status()} (expected 404)`);\n }\n expect(failures).toEqual([]);\n });\n\n test('robots.txt allows crawling and names the sitemap', async ({ request }) => {\n const response = await request.get('/robots.txt');\n expect(response.status()).toBe(200);\n const body = await response.text();\n expect(body).toMatch(new RegExp(`^sitemap:\\\\s*${escapeRegExp(`${origin}/sitemap.xml`)}\\\\s*$`, 'im'));\n expect(body).not.toMatch(/^disallow:\\s*\\/\\s*$/im);\n });\n\n test('a production deployment tells crawlers not to index any host but its canonical one', async ({ baseURL }) => {\n test.skip(process.env.VERCEL_ENV !== 'production', 'Only a production build (VERCEL_ENV=production, as CI builds it) answers other hosts with noindex.');\n const server = new URL(baseURL ?? 'http://127.0.0.1:3100/');\n const canonicalHost = new URL(origin).hostname;\n // The production *.vercel.app alias serves the full site; its pages must not be indexed.\n const alias = `${canonicalHost.split('.')[0]}-brandlab-e2e.vercel.app`;\n expect(await responseHeader(server, alias, '/', 'x-robots-tag'), `${alias}: X-Robots-Tag`).toBe('noindex');\n expect(await responseHeader(server, canonicalHost, '/', 'x-robots-tag'), `${canonicalHost}: X-Robots-Tag`).not.toMatch(/noindex/);\n });\n });\n}\n\n/**\n * Why a page's BreadcrumbList JSON-LD would fail Google's breadcrumb rich result (3.3.3): every\n * ListItem but the last needs its `item` (the page it names), as the shared builder requires;\n * Google reports \"Missing field item\" otherwise. Lists inside an `@graph` count; an empty list\n * has nothing to judge.\n */\nexport function breadcrumbListProblems(jsonLd: readonly unknown[]): string[] {\n const lists: Record<string, unknown>[] = [];\n const collect = (value: unknown) => {\n if (Array.isArray(value)) {\n value.forEach(collect);\n return;\n }\n if (!value || typeof value !== 'object') return;\n const node = value as Record<string, unknown>;\n const type = node['@type'];\n if (type === 'BreadcrumbList' || (Array.isArray(type) && type.includes('BreadcrumbList'))) lists.push(node);\n if (Array.isArray(node['@graph'])) collect(node['@graph']);\n };\n collect(jsonLd);\n const problems: string[] = [];\n for (const list of lists) {\n const items = Array.isArray(list.itemListElement) ? list.itemListElement : [];\n items.slice(0, -1).forEach((entry, index) => {\n const item = entry && typeof entry === 'object' ? (entry as Record<string, unknown>).item : undefined;\n const named = typeof item === 'string' ? item.trim() : item && typeof item === 'object' ? String((item as Record<string, unknown>)['@id'] ?? (item as Record<string, unknown>).url ?? '').trim() : '';\n if (named) return;\n const name = entry && typeof entry === 'object' ? (entry as Record<string, unknown>).name : undefined;\n problems.push(`BreadcrumbList item ${index + 1}${typeof name === 'string' && name.trim() ? ` (\"${name.trim()}\")` : ''} has no URL (item); only the last breadcrumb may omit it, or Google rejects the list`);\n });\n }\n return problems;\n}\n\n/** One response header for a request that names its own Host (the Playwright request context always sends the server's). */\nexport function responseHeader(server: URL, host: string, path: string, name: string): Promise<string | undefined> {\n return new Promise((resolve, reject) => {\n const request = httpRequest({ hostname: server.hostname, port: server.port || 80, path, method: 'GET', headers: { host, 'x-forwarded-proto': 'https' } }, (response) => {\n response.resume();\n const value = response.headers[name.toLowerCase()];\n resolve(Array.isArray(value) ? value.join(', ') : value);\n });\n request.on('error', reject);\n request.end();\n });\n}\n\n/**\n * Every `<loc>` in a sitemap, following one level of sitemap index. Nothing is\n * followed through a redirect, and with `origin` every nested sitemap must be\n * on that origin (it is then fetched from the build under test).\n */\nexport async function readSitemap(request: APIRequestContext, path: string, origin?: string): Promise<string[]> {\n const read = async (target: string) => {\n const response = await request.get(target, { maxRedirects: 0 });\n if (response.status() !== 200) throw new Error(`${target} answers ${response.status()}; a sitemap must answer 200 without redirecting.`);\n return response.text();\n };\n const xml = await read(path);\n const locations = extractSitemapLocations(xml);\n if (!/<sitemapindex\\b/i.test(xml)) return locations;\n const nested: string[] = [];\n for (const location of locations) {\n const url = new URL(location);\n if (origin && url.origin !== new URL(origin).origin) {\n throw new Error(`The sitemap index lists ${location}, which is not on ${new URL(origin).origin}.`);\n }\n nested.push(...extractSitemapLocations(await read(`${url.pathname}${url.search}`)));\n }\n return nested;\n}\n\n/**\n * The sitemap's URL files with their entries: the sitemap itself, or every\n * file a sitemap index lists (one level, same origin, no redirects).\n */\nexport async function readSitemapFiles(\n request: APIRequestContext,\n path: string,\n origin: string,\n): Promise<{ index: boolean; files: SitemapFile[] }> {\n const read = async (target: string) => {\n const response = await request.get(target, { maxRedirects: 0 });\n if (response.status() !== 200) throw new Error(`${target} answers ${response.status()}; a sitemap must answer 200 without redirecting.`);\n return response.text();\n };\n const xml = await read(path);\n if (!/<sitemapindex\\b/i.test(xml)) return { index: false, files: [{ path, entries: parseSitemapEntries(xml) }] };\n const files: SitemapFile[] = [];\n for (const location of extractSitemapLocations(xml)) {\n const url = new URL(location);\n if (url.origin !== new URL(origin).origin) throw new Error(`The sitemap index lists ${location}, which is not on ${new URL(origin).origin}.`);\n files.push({ path: url.pathname, entries: parseSitemapEntries(await read(`${url.pathname}${url.search}`)) });\n }\n return { index: true, files };\n}\n\nexport function extractSitemapLocations(xml: string): string[] {\n return [...xml.matchAll(/<loc>\\s*([^<\\s]+)\\s*<\\/loc>/gi)].map((match) =>\n new URL(decodeXml(match[1]!)).href);\n}\n\nexport function defaultNotFoundPaths(routes: readonly BrandlabEstateRoute[]): string[] {\n const parents = new Set(['']);\n for (const route of routes) {\n const segments = normalisePath(route.path).split('/').filter(Boolean);\n if (segments.length > 1) parents.add(`/${segments.slice(0, -1).join('/')}`);\n if (segments.length === 1) parents.add(`/${segments[0]}`);\n }\n return [...parents].sort().map((parent) => `${parent}/${UNKNOWN_SLUG}`);\n}\n\n/** The unknown-slug probes to request, apart from those a redirect rule answers. */\nexport function unknownSlugProbes(paths: readonly string[], redirects: (path: string) => boolean): { probes: string[]; redirected: string[] } {\n const probes: string[] = [];\n const redirected: string[] = [];\n for (const path of paths) (redirects(normalisePath(path)) ? redirected : probes).push(path);\n return { probes, redirected };\n}\n\n/** The page of a family its estate check opens: the first one outside the catalogue, else its first page. */\nexport function familySamplePath(family: Pick<BrandlabEstateFamily, 'paths'>, catalogue: ReadonlySet<string>): string | undefined {\n return family.paths.find((path) => !catalogue.has(normalisePath(path))) ?? family.paths[0];\n}\n\n/**\n * Paths of a family's pattern outside its space: a page of the family with one\n * dynamic segment at a time replaced by an unknown slug.\n */\nexport function familyNotFoundPaths(family: Pick<BrandlabEstateFamily, 'pattern' | 'paths'>): string[] {\n const first = family.paths[0];\n if (!first) return [];\n const parts = normalisePath(first).split('/');\n const segments = family.pattern.split('/');\n const paths = segments.flatMap((segment, index) => (/^\\[[^.\\]]/.test(segment) ? [parts.map((part, position) => (position === index ? UNKNOWN_SLUG : part)).join('/')] : []));\n const space = new Set(family.paths.map(normalisePath));\n return [...new Set(paths)].filter((path) => !space.has(path));\n}\n\n/**\n * The text a page shows before any script runs: its body without scripts,\n * styles and templates, tags removed and whitespace collapsed. Empty for the\n * document Next sends when it leaves a page to the browser to render.\n */\nexport function bodyText(html: string): string {\n const body = /<body\\b[^>]*>([\\s\\S]*?)(?:<\\/body>|$)/i.exec(html)?.[1] ?? (/<html\\b/i.test(html) ? '' : html);\n return decodeXml(body\n .replace(/<(script|style|template|noscript)\\b[\\s\\S]*?<\\/\\1>/gi, ' ')\n .replace(/<[^>]+>/g, ' '))\n .replace(/&nbsp;/g, ' ')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\n/** A Next redirect path with parameters: `:name`, `:name*`, `:name+`, `:name?` or a regular-expression group. */\nexport function isRedirectPattern(path: string): boolean {\n return /:[A-Za-z_]\\w*|\\(/.test(path.replace(/^[a-z][a-z0-9+.-]*:\\/\\//i, ''));\n}\n\n/**\n * The request that proves a redirect rule: a pattern gets one sample segment\n * per parameter, in its source and destination alike. Undefined when a\n * parameter carries its own regular expression, which no sample can satisfy\n * with certainty.\n */\nexport function redirectProbe(rule: Pick<BrandlabEstateRedirect, 'source' | 'destination'>): { source: string; destination: string; sampled: boolean } | undefined {\n const sample = (path: string) => {\n if (!isRedirectPattern(path)) return path;\n // Keep an external destination's scheme and host; parameters live in the path.\n const origin = /^[a-z][a-z0-9+.-]*:\\/\\/[^/]*/i.exec(path)?.[0] ?? '';\n const rest = path.slice(origin.length);\n if (rest.includes('(')) return undefined;\n return `${origin}${rest.replace(/:[A-Za-z_]\\w*[*+?]?/g, PATTERN_SAMPLE)}`;\n };\n const source = sample(rule.source);\n const destination = sample(rule.destination);\n if (source === undefined || destination === undefined) return undefined;\n return { source, destination, sampled: isRedirectPattern(rule.destination) };\n}\n\n/** A matcher for paths a pattern source redirects, for internal-link checks. */\nfunction redirectSourceMatcher(source: string): RegExp | undefined {\n if (source.includes('(')) return undefined;\n const pattern = normalisePath(source).split(/(:[A-Za-z_]\\w*[*+?]?)/).map((part) => {\n const parameter = /^:[A-Za-z_]\\w*([*+?]?)$/.exec(part);\n if (!parameter) return escapeRegExp(part);\n return parameter[1] === '*' || parameter[1] === '+' ? '.+' : parameter[1] === '?' ? '[^/]*' : '[^/]+';\n }).join('');\n return new RegExp(`^${pattern}$`);\n}\n\n/**\n * The advisory page-quality journey: headings, title, description, og:url,\n * implementation wording and structured-data identity for every catalogue\n * page. It reports what it finds (page-quality.json and an annotation) and\n * never fails, because a backend release keeps production's pages as they\n * are; the comparison harness proves them unchanged.\n *\n * // tests/e2e/page-quality.spec.ts\n * defineBrandlabPageQualityTests({ site, routes })\n */\nexport function defineBrandlabPageQualityTests(options: BrandlabPageQualityTestOptions) {\n const origin = new URL(String(options.site.canonicalUrl)).origin;\n const canonical = (path: string) => new URL(path, `${origin}/`).href;\n const businessId = options.businessId ?? `${origin}/#business`;\n const timeout = options.timeout ?? 240_000;\n const routes = options.routes.filter((route) => normalisePath(route.path) !== NOT_FOUND_ROUTE);\n const routeByPath = new Map(routes.map((route) => [normalisePath(route.path), route]));\n const placeOfRoute = estatePlaceResolver(routes, options.estate);\n const run = pageQualityRunFromEnv();\n\n test.describe('Brandlab page quality (advisory)', () => {\n test('reports page and structured-data quality without failing', async ({ request }, testInfo) => {\n test.skip(!run.run, run.reason);\n test.setTimeout(timeout);\n const findings: BrandlabPageQualityFinding[] = [];\n const geography = await loadBrandlabGeography();\n const centreOf = options.centreOf ?? geography.centreOf;\n const titles = new Map<string, string[]>();\n const descriptions = new Map<string, string[]>();\n const similarityPages: SimilarityPage[] = [];\n const linksFrom = new Map<string, Set<string>>();\n await eachWithConcurrency(routes, options.concurrency ?? 8, async (route) => {\n const response = await request.get(route.path, { maxRedirects: 0 });\n // The estate journey owns status codes; quality is read from the pages that load.\n if (response.status() !== 200) return;\n const html = await response.text();\n const snapshot = extractSearchCertificationHtmlSnapshot(html);\n const add = (kind: BrandlabPageQualityFinding['kind'], detail: string) => findings.push({ path: route.path, kind, detail });\n const h1s = headingTexts(html);\n if (h1s.length !== 1) add('headings', `${h1s.length} h1 headings`);\n if (!snapshot.title) add('title', 'no title');\n if (!snapshot.description) add('description', 'no meta description');\n const indexable = route.indexing === 'index';\n if (indexable && snapshot.title) {\n const formatProblems = titleFormatProblems(snapshot.title);\n if (formatProblems.length > 0) add('title-format', `\"${snapshot.title}\": ${formatProblems.join('; ')}`);\n titles.set(snapshot.title, [...(titles.get(snapshot.title) ?? []), route.path]);\n const place = placeOfRoute(route);\n if (h1s.length === 1) {\n const problems = titleHeadingProblems(snapshot.title, h1s[0]!, place && !place.region ? geography.placeNames.get(place.slug) : undefined);\n if (problems.length > 0) add('title-h1', problems.join('; '));\n }\n }\n if (indexable && snapshot.description) {\n const problem = descriptionLengthProblem(snapshot.description);\n if (problem) add('description-length', problem);\n descriptions.set(snapshot.description, [...(descriptions.get(snapshot.description) ?? []), route.path]);\n }\n const bytes = Buffer.byteLength(html);\n if (bytes > HTML_MAX_BYTES) add('html-size', `${(bytes / 1_048_576).toFixed(2)} MB of HTML (investigate anything over 1 MB)`);\n const expectedCanonical = canonical(route.canonicalPath ?? route.path);\n const ogUrl = snapshot.openGraph['og:url'];\n if (ogUrl && new URL(ogUrl, origin).href !== expectedCanonical) add('og-url', `og:url ${ogUrl} is not the canonical URL`);\n if (snapshot.implementationCopyMatches.length > 0) add('implementation-wording', snapshot.implementationCopyMatches.join(', '));\n for (const failure of inspectStructuredData(route, snapshot.jsonLd, businessId)) {\n add('structured-data', failure.slice(route.path.length + 2));\n }\n const place = placeOfRoute(route)?.slug;\n if (indexable && place) similarityPages.push({ path: route.path, place, blocks: textBlocks(html).map(geography.maskPlaces) });\n const targets = new Set<string>();\n for (const href of extractLinks(html)) {\n const path = internalPath(href, expectedCanonical, origin);\n if (path && path !== normalisePath(route.path)) targets.add(path);\n }\n linksFrom.set(normalisePath(route.path), targets);\n });\n // Internal links: every indexable page needs a link from another page, and indexable pages link no noindex location page.\n const inbound = new Map<string, number>();\n for (const [from, targets] of linksFrom) {\n const source = routeByPath.get(from);\n for (const target of targets) {\n inbound.set(target, (inbound.get(target) ?? 0) + 1);\n const linked = routeByPath.get(target);\n if (source?.indexing === 'index' && linked && linked.indexing !== 'index' && LOCATION_KINDS.has(linked.kind ?? '')) {\n findings.push({ path: from, kind: 'noindex-link', detail: `links to ${target}, a noindex location page (list unindexed places as plain text)` });\n }\n }\n }\n for (const route of routes) {\n const path = normalisePath(route.path);\n if (route.indexing === 'index' && route.kind !== 'home' && path !== '/' && linksFrom.has(path) && !inbound.get(path)) {\n findings.push({ path: route.path, kind: 'orphan', detail: 'no other catalogue page links to it' });\n }\n }\n for (const [title, paths] of titles) {\n if (paths.length > 1) for (const path of paths) findings.push({ path, kind: 'title-duplicate', detail: `\"${title}\" is also the title of ${paths.filter((other) => other !== path).slice(0, 3).join(', ')}${paths.length > 4 ? ' and others' : ''}` });\n }\n for (const [description, paths] of descriptions) {\n if (paths.length > 1) for (const path of paths) findings.push({ path, kind: 'description-duplicate', detail: `the meta description is shared with ${paths.length - 1} other page(s) (\"${description.slice(0, 60)}…\")` });\n }\n // Location pages: centerpiece similarity against near siblings, by route kind.\n const similarity = [...new Set(similarityPages.map((page) => routeByPath.get(normalisePath(page.path))?.kind))]\n .flatMap((kind) => centerpieceSimilarity(similarityPages.filter((page) => routeByPath.get(normalisePath(page.path))?.kind === kind), centreOf));\n for (const score of similarity) {\n if (score.score < SIMILARITY_WARN) continue;\n findings.push({\n path: score.path,\n kind: 'similarity',\n detail: `${score.score.toFixed(2)} of its page-specific phrases also appear on ${score.sibling ?? 'a near sibling'}${score.score >= SIMILARITY_REVIEW ? ' (at or above 0.7: on the review list; the page stays indexed)' : ' (at or above 0.5: add page-specific evidence)'}`,\n });\n }\n const reviewList = buildLocationReviewList(routes);\n const flagged = new Set(similarity.filter((score) => score.score >= SIMILARITY_REVIEW).map((score) => score.path));\n const review = [\n ...reviewList.map((item) => ({ ...item, reasons: [...item.reasons, ...(flagged.has(item.path) ? ['centerpiece similarity at or above 0.7'] : [])] })),\n ...[...flagged].filter((path) => !reviewList.some((item) => item.path === path)).map((path) => ({ path, evidence: routeByPath.get(normalisePath(path))?.locationDecision?.evidence ?? [], reasons: ['centerpiece similarity at or above 0.7'] })),\n ].sort((a, b) => a.path.localeCompare(b.path));\n for (const item of review) findings.push({ path: item.path, kind: 'location-review', detail: item.reasons.join('; ') });\n findings.sort((a, b) => a.path.localeCompare(b.path) || a.kind.localeCompare(b.kind));\n const byKind = Object.fromEntries(\n [...new Set(findings.map((finding) => finding.kind))].sort().map((kind) => [kind, findings.filter((finding) => finding.kind === kind).length]),\n );\n const reports = {\n 'page-quality.json': JSON.stringify({ pages: routes.length, findings: findings.length, byKind, details: findings }, null, 2),\n 'location-review.json': JSON.stringify({\n reviewed: review.length,\n note: 'Pages on the review list stay indexed. Each review ends in adding evidence, merging into the nearest owner or hiding with noindex, follow; merging and hiding are Romie\\'s decision.',\n similarity: similarity.sort((a, b) => b.score - a.score),\n review,\n }, null, 2),\n };\n for (const [name, body] of Object.entries(reports)) await testInfo.attach(name, { body, contentType: 'application/json' });\n // CI's page-quality run keeps the reports beside the ready report (BRANDLAB_PAGE_QUALITY_DIR=docs/ready).\n if (run.directory) {\n mkdirSync(run.directory, { recursive: true });\n for (const [name, body] of Object.entries(reports)) writeFileSync(join(run.directory, name), `${body}\\n`);\n }\n testInfo.annotations.push({\n type: 'advisory',\n description: `${findings.length} page-quality finding(s) across ${routes.length} page(s)${findings.length ? `: ${Object.entries(byKind).map(([kind, count]) => `${kind} ${count}`).join(', ')}` : ''}; see page-quality.json.`,\n });\n });\n });\n}\n\n/**\n * Whether the advisory page-quality crawl runs (3.13.0, client CI v2). CI v2 runs it on its own when a pull request\n * is ready for review, on a manual run and monthly for large estates, and sets `BRANDLAB_PAGE_QUALITY=skip` for the required journeys so\n * drafts and merges do not crawl every page twice. `BRANDLAB_PAGE_QUALITY_DIR` also writes its reports there.\n */\nexport function pageQualityRunFromEnv(env: NodeJS.ProcessEnv = process.env): { run: boolean; reason: string; directory?: string } {\n const directory = env.BRANDLAB_PAGE_QUALITY_DIR?.trim() || undefined;\n if ((env.BRANDLAB_PAGE_QUALITY ?? '').trim() === 'skip') {\n return { run: false, reason: 'The advisory page-quality crawl runs on its own in CI (ready for review, manual runs and monthly for large estates); BRANDLAB_PAGE_QUALITY=skip.' };\n }\n return { run: true, reason: '', ...(directory ? { directory } : {}) };\n}\n\nfunction inspectStructuredData(route: BrandlabEstateRoute, payloads: readonly unknown[], businessId: string | false): string[] {\n const failures: string[] = [];\n if (payloads.some((payload) => (payload as { parseError?: boolean }).parseError)) failures.push(`${route.path}: invalid JSON-LD`);\n if (route.indexing === 'index' && payloads.length === 0) failures.push(`${route.path}: no JSON-LD`);\n const ids: string[] = [];\n let referencesBusiness = false;\n const visit = (value: unknown) => {\n if (!value || typeof value !== 'object') return;\n if (Array.isArray(value)) { value.forEach(visit); return; }\n const node = value as Record<string, unknown>;\n const types = ([] as unknown[]).concat(node['@type'] ?? []).map(String);\n if (typeof node['@id'] === 'string') {\n if (node['@id'] === businessId) referencesBusiness = true;\n if (types.length > 0) ids.push(node['@id']);\n }\n if (businessId && types.some((type) => BUSINESS_TYPES.has(type)) && node['@id'] !== businessId) {\n failures.push(`${route.path}: business node @id ${String(node['@id'] ?? 'missing')} (expected ${businessId})`);\n }\n Object.values(node).forEach(visit);\n };\n payloads.forEach(visit);\n const duplicate = ids.find((id, index) => ids.indexOf(id) !== index);\n if (duplicate) failures.push(`${route.path}: JSON-LD repeats node ${duplicate}`);\n if (businessId && route.indexing === 'index' && payloads.length > 0 && !referencesBusiness) failures.push(`${route.path}: JSON-LD does not reference ${businessId}`);\n return failures;\n}\n\nfunction extractLinks(html: string): string[] {\n return [...html.matchAll(/<a\\b[^>]*\\bhref=[\"']([^\"']+)[\"']/gi)].map((match) => decodeXml(match[1]!));\n}\n\nfunction internalPath(href: string, base: string, origin: string): string | null {\n if (/^(?:mailto|tel|sms|javascript):/i.test(href) || href.startsWith('#')) return null;\n let url: URL;\n try { url = new URL(href, base); } catch { return null; }\n if (url.origin !== origin && !['127.0.0.1', 'localhost'].includes(url.hostname)) return null;\n if (ASSET_PATH.test(url.pathname) || url.pathname.startsWith('/api/') || url.pathname.startsWith('/_next/')) return null;\n return normalisePath(url.pathname);\n}\n\nfunction normalisePath(path: string): string {\n const pathname = path.split(/[?#]/)[0] || '/';\n return pathname.length > 1 ? pathname.replace(/\\/+$/, '') : '/';\n}\n\n/**\n * Absolute (`https://host`) and protocol-relative (`//host`) destinations leave\n * the site; browsers also read `/\\host` and `\\\\host` as protocol-relative.\n */\nexport function isExternalDestination(destination: string): boolean {\n return /^(?:[a-z][a-z0-9+.-]*:)?[\\\\/]{2}/i.test(destination.trim());\n}\n\nfunction decodeXml(value: string): string {\n return value.replace(/&amp;/g, '&').replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&quot;/g, '\"').replace(/&#39;/g, \"'\");\n}\n\nfunction escapeRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\nasync function eachWithConcurrency<T>(items: readonly T[], limit: number, task: (item: T) => Promise<void>) {\n const queue = [...items];\n await Promise.all(Array.from({ length: Math.max(1, Math.min(limit, queue.length)) }, async () => {\n for (let item = queue.shift(); item !== undefined; item = queue.shift()) await task(item);\n }));\n}\n"]}
package/dist/project.d.ts CHANGED
@@ -38,11 +38,24 @@ export interface BrandlabConformanceResult {
38
38
  implementationFingerprint: string;
39
39
  mode: BrandlabConformanceMode;
40
40
  installedRelease: string | null;
41
+ /**
42
+ * Set only when all four packages are one canary prerelease (`X.Y.Z-canary.N`). A canary is never an
43
+ * installed release: `installedRelease` stays null, so it cannot stand as release evidence.
44
+ */
45
+ canaryRelease?: BrandlabCanaryRelease;
41
46
  findings: BrandlabFinding[];
42
47
  operationalFindings: BrandlabOperationalFinding[];
43
48
  verification: BrandlabVerificationResult;
44
49
  summary: ConformanceSummary;
45
50
  }
51
+ export interface BrandlabCanaryRelease {
52
+ /** The canary version, e.g. `3.14.0-canary.2`. */
53
+ version: string;
54
+ /** The release line it previews, e.g. `3.14.0`. */
55
+ line: string;
56
+ /** `embedded`: checked against this checker's contract for the line; `not-cut`: the line has no contract yet. */
57
+ contract: "embedded" | "not-cut";
58
+ }
46
59
  export interface EvaluateBrandlabProjectOptions {
47
60
  packedReleaseReceipt?: string;
48
61
  root: string;
@@ -62,6 +75,13 @@ interface ProjectPackageJsonLike {
62
75
  export declare function evaluateBrandlabProject(options: EvaluateBrandlabProjectOptions): BrandlabConformanceResult;
63
76
  export declare function shouldFailBrandlabConformance(result: BrandlabConformanceResult): boolean;
64
77
  export declare function detectInstalledBrandlabRelease(packageJson: ProjectPackageJsonLike | null): string | null;
78
+ /**
79
+ * Reads one canary prerelease across all four packages, pinned exactly (`3.14.0-canary.2`) or installed from
80
+ * the packed tarball (`file:.canary/brandlab-digital-site-core-3.14.0-canary.2.tgz`). A canary previews the line
81
+ * `X.Y.Z`: it is checked against that line's contract when this checker carries it, and is otherwise reported
82
+ * as a canary notice. Lines older than the checker's release are not supported. Never a published release.
83
+ */
84
+ export declare function detectBrandlabCanaryRelease(packageJson: ProjectPackageJsonLike | null, release?: Pick<BrandlabRuntimeReleaseContract, "releaseVersion">): BrandlabCanaryRelease | null;
65
85
  /** Variable names documented in an env example file (`KEY=`, `export KEY=`); values are ignored. */
66
86
  export declare function parseEnvironmentKeys(text: string): Set<string>;
67
87
  export type { BrandlabContractVersions };