@brandlab-digital/conformance 3.12.1 → 3.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +41 -1
- package/dist/analytics-e2e.d.ts +10 -16
- package/dist/analytics-e2e.d.ts.map +1 -1
- package/dist/analytics-e2e.js +61 -3
- package/dist/analytics-e2e.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +4 -1
- package/dist/cli.js.map +1 -1
- package/dist/current-release.js +5 -5
- package/dist/current-release.js.map +1 -1
- package/dist/edge-probe-cli.d.ts +57 -0
- package/dist/edge-probe-cli.d.ts.map +1 -0
- package/dist/edge-probe-cli.js +134 -0
- package/dist/edge-probe-cli.js.map +1 -0
- package/dist/engineering.d.ts +4 -1
- package/dist/engineering.d.ts.map +1 -1
- package/dist/engineering.js +19 -14
- package/dist/engineering.js.map +1 -1
- package/dist/estate-sample.d.ts +60 -0
- package/dist/estate-sample.d.ts.map +1 -0
- package/dist/estate-sample.js +144 -0
- package/dist/estate-sample.js.map +1 -0
- package/dist/estate.d.ts +45 -0
- package/dist/estate.d.ts.map +1 -1
- package/dist/estate.js +82 -22
- package/dist/estate.js.map +1 -1
- package/dist/origin-region.d.ts +14 -0
- package/dist/origin-region.d.ts.map +1 -0
- package/dist/origin-region.js +46 -0
- package/dist/origin-region.js.map +1 -0
- package/dist/prefetch-probe-cli.d.ts +36 -0
- package/dist/prefetch-probe-cli.d.ts.map +1 -0
- package/dist/prefetch-probe-cli.js +163 -0
- package/dist/prefetch-probe-cli.js.map +1 -0
- package/dist/project.d.ts +20 -0
- package/dist/project.d.ts.map +1 -1
- package/dist/project.js +92 -2
- package/dist/project.js.map +1 -1
- package/dist/ready-cli.d.ts.map +1 -1
- package/dist/ready-cli.js +8 -3
- package/dist/ready-cli.js.map +1 -1
- package/dist/rule-registry.d.ts +5 -0
- package/dist/rule-registry.d.ts.map +1 -1
- package/dist/rule-registry.js +1 -0
- package/dist/rule-registry.js.map +1 -1
- package/dist/site-system/checks.d.ts +62 -1
- package/dist/site-system/checks.d.ts.map +1 -1
- package/dist/site-system/checks.js +9 -0
- package/dist/site-system/checks.js.map +1 -1
- package/dist/site-system/config.d.ts +27 -0
- package/dist/site-system/config.d.ts.map +1 -1
- package/dist/site-system/config.js +50 -1
- package/dist/site-system/config.js.map +1 -1
- package/dist/site-system/index.d.ts +1 -1
- package/dist/site-system/index.d.ts.map +1 -1
- package/dist/site-system/index.js +1 -1
- package/dist/site-system/index.js.map +1 -1
- package/dist/site-system/journeys/plan.d.ts +9 -2
- package/dist/site-system/journeys/plan.d.ts.map +1 -1
- package/dist/site-system/journeys/plan.js +11 -1
- package/dist/site-system/journeys/plan.js.map +1 -1
- package/dist/site-system/journeys/prefetch-measure.d.ts +28 -0
- package/dist/site-system/journeys/prefetch-measure.d.ts.map +1 -0
- package/dist/site-system/journeys/prefetch-measure.js +94 -0
- package/dist/site-system/journeys/prefetch-measure.js.map +1 -0
- package/dist/site-system/journeys/prefetch-policy.d.ts +85 -0
- package/dist/site-system/journeys/prefetch-policy.d.ts.map +1 -0
- package/dist/site-system/journeys/prefetch-policy.js +152 -0
- package/dist/site-system/journeys/prefetch-policy.js.map +1 -0
- package/dist/site-system/journeys/prefetch.d.ts +3 -0
- package/dist/site-system/journeys/prefetch.d.ts.map +1 -0
- package/dist/site-system/journeys/prefetch.js +37 -0
- package/dist/site-system/journeys/prefetch.js.map +1 -0
- package/dist/site-system/journeys/variants.d.ts +34 -0
- package/dist/site-system/journeys/variants.d.ts.map +1 -0
- package/dist/site-system/journeys/variants.js +139 -0
- package/dist/site-system/journeys/variants.js.map +1 -0
- package/dist/site-system/report.d.ts.map +1 -1
- package/dist/site-system/report.js +19 -5
- package/dist/site-system/report.js.map +1 -1
- package/dist/site-system/rule-modules.d.ts.map +1 -1
- package/dist/site-system/rule-modules.js +10 -0
- package/dist/site-system/rule-modules.js.map +1 -1
- package/dist/site-system/rules/delivery/h13-inbox.d.ts +23 -0
- package/dist/site-system/rules/delivery/h13-inbox.d.ts.map +1 -0
- package/dist/site-system/rules/delivery/h13-inbox.js +208 -0
- package/dist/site-system/rules/delivery/h13-inbox.js.map +1 -0
- package/dist/site-system/rules/delivery/index.d.ts +12 -0
- package/dist/site-system/rules/delivery/index.d.ts.map +1 -0
- package/dist/site-system/rules/delivery/index.js +7 -0
- package/dist/site-system/rules/delivery/index.js.map +1 -0
- package/dist/site-system/rules/full/common.d.ts +3 -0
- package/dist/site-system/rules/full/common.d.ts.map +1 -1
- package/dist/site-system/rules/full/common.js.map +1 -1
- package/dist/site-system/rules/full/e1-pattern.d.ts +4 -0
- package/dist/site-system/rules/full/e1-pattern.d.ts.map +1 -0
- package/dist/site-system/rules/full/e1-pattern.js +47 -0
- package/dist/site-system/rules/full/e1-pattern.js.map +1 -0
- package/dist/site-system/rules/full/estate.d.ts +20 -1
- package/dist/site-system/rules/full/estate.d.ts.map +1 -1
- package/dist/site-system/rules/full/estate.js +33 -5
- package/dist/site-system/rules/full/estate.js.map +1 -1
- package/dist/site-system/rules/full/geography.d.ts +8 -2
- package/dist/site-system/rules/full/geography.d.ts.map +1 -1
- package/dist/site-system/rules/full/geography.js +6 -2
- package/dist/site-system/rules/full/geography.js.map +1 -1
- package/dist/site-system/rules/full/index.d.ts +3 -2
- package/dist/site-system/rules/full/index.d.ts.map +1 -1
- package/dist/site-system/rules/full/index.js +27 -9
- package/dist/site-system/rules/full/index.js.map +1 -1
- package/dist/site-system/rules/full/k12-slots.d.ts +4 -0
- package/dist/site-system/rules/full/k12-slots.d.ts.map +1 -0
- package/dist/site-system/rules/full/k12-slots.js +40 -0
- package/dist/site-system/rules/full/k12-slots.js.map +1 -0
- package/dist/site-system/rules/full/loc-index.d.ts +5 -0
- package/dist/site-system/rules/full/loc-index.d.ts.map +1 -1
- package/dist/site-system/rules/full/loc-index.js +43 -2
- package/dist/site-system/rules/full/loc-index.js.map +1 -1
- package/dist/site-system/rules/full/strategy.d.ts +4 -0
- package/dist/site-system/rules/full/strategy.d.ts.map +1 -1
- package/dist/site-system/rules/full/strategy.js +19 -3
- package/dist/site-system/rules/full/strategy.js.map +1 -1
- package/dist/site-system/rules/full/w3-refresh.d.ts +22 -0
- package/dist/site-system/rules/full/w3-refresh.d.ts.map +1 -0
- package/dist/site-system/rules/full/w3-refresh.js +70 -0
- package/dist/site-system/rules/full/w3-refresh.js.map +1 -0
- package/dist/site-system/rules/guards/index.d.ts +22 -0
- package/dist/site-system/rules/guards/index.d.ts.map +1 -0
- package/dist/site-system/rules/guards/index.js +109 -0
- package/dist/site-system/rules/guards/index.js.map +1 -0
- package/dist/site-system/rules/guards/w10-crawlers.d.ts +55 -0
- package/dist/site-system/rules/guards/w10-crawlers.d.ts.map +1 -0
- package/dist/site-system/rules/guards/w10-crawlers.js +182 -0
- package/dist/site-system/rules/guards/w10-crawlers.js.map +1 -0
- package/dist/site-system/rules/guards/w9-params.d.ts +19 -0
- package/dist/site-system/rules/guards/w9-params.d.ts.map +1 -0
- package/dist/site-system/rules/guards/w9-params.js +84 -0
- package/dist/site-system/rules/guards/w9-params.js.map +1 -0
- package/dist/site-system/rules/sections/k2-skin-coverage.d.ts +2 -2
- package/dist/site-system/rules/sections/k2-skin-coverage.d.ts.map +1 -1
- package/dist/site-system/rules/sections/k2-skin-coverage.js +6 -2
- package/dist/site-system/rules/sections/k2-skin-coverage.js.map +1 -1
- package/dist/site-system/runners.js +1 -1
- package/dist/site-system/runners.js.map +1 -1
- package/dist/site-system-e2e.d.ts +1 -0
- package/dist/site-system-e2e.d.ts.map +1 -1
- package/dist/site-system-e2e.js +8 -2
- package/dist/site-system-e2e.js.map +1 -1
- package/package.json +6 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"geography.d.ts","sourceRoot":"","sources":["../../../../src/site-system/rules/full/geography.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAmB,KAAK,YAAY,EAAE,KAAK,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAExG,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,iDAAiD;IACjD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,wCAAwC;IACxC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,wEAAwE;IACxE,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CACnC;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,QAAQ,EAAE,kBAAkB,CAAC;IACtC,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;IAC3D,wCAAwC;IACxC,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;IAC3D,wGAAwG;IACxG,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IACxC,mEAAmE;IACnE,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5C,sGAAsG;IACtG,QAAQ,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,EAAE,CAAC;CAC/C;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAC,mBAAmB,GAAG;QAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,YAAY,CAAA;KAAE,CAAC,EAAE,CAAC;IACxF,QAAQ,CAAC,OAAO,EAAE,SAAS;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;KAAE,EAAE,CAAC;IAClH,QAAQ,CAAC,QAAQ,CAAC,EAAE,kBAAkB,CAAC;CACxC;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,kBAAkB,GAAG,aAAa,CAoC5E;AAaD,iEAAiE;AACjE,wBAAgB,iBAAiB,IAAI,OAAO,CAAC,aAAa,CAAC,CAmB1D;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,QAAQ,CAAC;IACnC,kGAAkG;IAClG,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED
|
|
1
|
+
{"version":3,"file":"geography.d.ts","sourceRoot":"","sources":["../../../../src/site-system/rules/full/geography.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAmB,KAAK,YAAY,EAAE,KAAK,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAExG,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,iDAAiD;IACjD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,wCAAwC;IACxC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,wEAAwE;IACxE,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CACnC;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,QAAQ,EAAE,kBAAkB,CAAC;IACtC,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;IAC3D,wCAAwC;IACxC,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;IAC3D,wGAAwG;IACxG,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;IACxC,mEAAmE;IACnE,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5C,sGAAsG;IACtG,QAAQ,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,EAAE,CAAC;CAC/C;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAC,mBAAmB,GAAG;QAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,YAAY,CAAA;KAAE,CAAC,EAAE,CAAC;IACxF,QAAQ,CAAC,OAAO,EAAE,SAAS;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;KAAE,EAAE,CAAC;IAClH,QAAQ,CAAC,QAAQ,CAAC,EAAE,kBAAkB,CAAC;CACxC;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,kBAAkB,GAAG,aAAa,CAoC5E;AAaD,iEAAiE;AACjE,wBAAgB,iBAAiB,IAAI,OAAO,CAAC,aAAa,CAAC,CAmB1D;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,QAAQ,CAAC;IACnC,kGAAkG;IAClG,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,sHAAsH;AACtH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9C,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC/C;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,aAAa,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,SAAS,GAAG,SAAS,CAsBhH;AAMD,wEAAwE;AACxE,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,aAAa,GAAG,MAAM,CAExE"}
|
|
@@ -72,14 +72,18 @@ export function loadFullGeography() {
|
|
|
72
72
|
}
|
|
73
73
|
/**
|
|
74
74
|
* The place a path names: its last segment, or the end of its last segment after a hyphen (`/tree-removal-ryde`),
|
|
75
|
-
* when that is a registry suburb or region (`{region}-area` included)
|
|
75
|
+
* when that is a registry suburb or region (`{region}-area` included), or a public slug the estate plan renames
|
|
76
|
+
* (`ryde` for the region `ryde-area`, `ryde-suburb` for the suburb `ryde`), read first. The longest match wins.
|
|
76
77
|
*/
|
|
77
|
-
export function placeOfPath(path, geography) {
|
|
78
|
+
export function placeOfPath(path, geography, aliases) {
|
|
78
79
|
const segments = path.split("/");
|
|
79
80
|
const last = segments.pop() ?? "";
|
|
80
81
|
if (!last)
|
|
81
82
|
return undefined;
|
|
82
83
|
const parent = segments.join("/");
|
|
84
|
+
const alias = aliases?.suburbs.get(last) ?? aliases?.regions.get(last);
|
|
85
|
+
if (alias !== undefined)
|
|
86
|
+
return { slug: alias, kind: aliases?.suburbs.has(last) ? "suburb" : "region", stem: `${parent}/{place}` };
|
|
83
87
|
const kindOf = (slug) => {
|
|
84
88
|
if (geography.suburbs.has(slug))
|
|
85
89
|
return "suburb";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"geography.js","sourceRoot":"","sources":["../../../../src/site-system/rules/full/geography.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The places the full tier reasons about: registry suburbs with their region and centre, and the regions. They come\n * from the site's installed site-core geography at run time (the conformance package compiles without site-core), or\n * from `defineFullGeography` in a test.\n */\nimport { createPlaceMask, type SuburbCentre, type SuburbCentreLookup } from \"../../../page-standard.js\";\n\nexport interface FullGeographySuburb {\n readonly slug: string;\n readonly name: string;\n /** The registry region the suburb belongs to. */\n readonly region: string;\n /** The suburb's council, when known. */\n readonly council?: string;\n}\n\nexport interface FullGeographyRegion {\n readonly slug: string;\n readonly name: string;\n /** Every name the region goes by (name, search name and alternates). */\n readonly names: readonly string[];\n}\n\nexport interface FullGeography {\n readonly centreOf: SuburbCentreLookup;\n readonly suburbs: ReadonlyMap<string, FullGeographySuburb>;\n /** Regions and sub-regions, by slug. */\n readonly regions: ReadonlyMap<string, FullGeographyRegion>;\n /** Masks every place name and postcode in lowercase text, so name-swapped copy compares as the same. */\n readonly mask: (text: string) => string;\n /** Lowercase place names (suburbs and regions) and their slugs. */\n readonly names: ReadonlyMap<string, string>;\n /** The slugs of the places a piece of copy names, longest name first (\"North Ryde\" before \"Ryde\"). */\n readonly placesIn: (text: string) => string[];\n}\n\nexport interface FullGeographyInput {\n readonly suburbs: readonly (FullGeographySuburb & { readonly centre?: SuburbCentre })[];\n readonly regions: readonly { readonly slug: string; readonly name: string; readonly names?: readonly string[] }[];\n readonly centreOf?: SuburbCentreLookup;\n}\n\nexport function defineFullGeography(input: FullGeographyInput): FullGeography {\n const suburbs = new Map<string, FullGeographySuburb>();\n const centres = new Map<string, SuburbCentre>();\n for (const suburb of input.suburbs) {\n if (suburbs.has(suburb.slug)) continue;\n suburbs.set(suburb.slug, { slug: suburb.slug, name: suburb.name, region: suburb.region, ...(suburb.council ? { council: suburb.council } : {}) });\n if (suburb.centre) centres.set(suburb.slug, suburb.centre);\n }\n const regions = new Map<string, FullGeographyRegion>();\n for (const region of input.regions) {\n if (!regions.has(region.slug)) regions.set(region.slug, { slug: region.slug, name: region.name, names: [...new Set([region.name, ...(region.names ?? [])])] });\n }\n const names = new Map<string, string>();\n for (const suburb of suburbs.values()) names.set(suburb.name.toLowerCase(), suburb.slug);\n for (const region of regions.values()) for (const name of region.names) if (!names.has(name.toLowerCase())) names.set(name.toLowerCase(), region.slug);\n const alternation = [...names.keys()]\n .filter((name) => name.length >= 3)\n .sort((left, right) => right.length - left.length)\n .map((name) => name.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\").replace(/\\s+/g, \"\\\\s+\"));\n const pattern = alternation.length > 0 ? new RegExp(`\\\\b(?:${alternation.join(\"|\")})\\\\b`, \"gi\") : null;\n return {\n centreOf: input.centreOf ?? ((slug) => centres.get(slug)),\n suburbs,\n regions,\n mask: createPlaceMask([...names.keys()]),\n names,\n placesIn: (text) => {\n if (!pattern) return [];\n const found = new Set<string>();\n for (const match of text.matchAll(pattern)) {\n const slug = names.get(match[0].toLowerCase().replace(/\\s+/g, \" \"));\n if (slug) found.add(slug);\n }\n return [...found];\n },\n };\n}\n\ninterface RegistryLike {\n suburbs: readonly { slug: string; name: string; regionSlug: string; councilSlug: string }[];\n nonSuburbLocalities: readonly { slug: string; name: string; regionSlug: string; councilSlug: string }[];\n regions: readonly { slug: string; name: string; seoName: string; alternateNames?: readonly string[] }[];\n subRegions: readonly { slug: string; name: string }[];\n}\n\n// A variable specifier, so the package compiles without site-core; it resolves to the site's installed copy.\nconst GEOGRAPHY_MODULE: string = \"@brandlab-digital/site-core/geography\";\nlet loaded: Promise<FullGeography> | undefined;\n\n/** The site's installed site-core geography (3.4.0 or later). */\nexport function loadFullGeography(): Promise<FullGeography> {\n loaded ??= (import(GEOGRAPHY_MODULE) as Promise<{\n getSuburbCentre?: SuburbCentreLookup;\n sydneyGeography: RegistryLike;\n centralCoastGeography: RegistryLike;\n }>).then((module) => {\n if (typeof module.getSuburbCentre !== \"function\") throw new Error(\"the installed site-core geography has no suburb centres (3.4.0 or later is needed)\");\n const registries = [module.sydneyGeography, module.centralCoastGeography];\n return defineFullGeography({\n suburbs: registries.flatMap((registry) =>\n [...registry.suburbs, ...registry.nonSuburbLocalities].map((place) => ({ slug: place.slug, name: place.name, region: place.regionSlug, council: place.councilSlug }))),\n regions: registries.flatMap((registry) => [\n ...registry.regions.map((region) => ({ slug: region.slug, name: region.name, names: [region.seoName, ...(region.alternateNames ?? [])] })),\n ...registry.subRegions.map((region) => ({ slug: region.slug, name: region.name })),\n ]),\n centreOf: module.getSuburbCentre,\n });\n });\n return loaded;\n}\n\nexport interface PathPlace {\n readonly slug: string;\n readonly kind: \"suburb\" | \"region\";\n /** The path with the place replaced by `{place}`: pages sharing a stem are one family's pages. */\n readonly stem: string;\n}\n\n/**\n * The place a path names: its last segment, or the end of its last segment after a hyphen (`/tree-removal-ryde`),\n * when that is a registry suburb or region (`{region}-area` included). The longest match wins.\n */\nexport function placeOfPath(path: string, geography: FullGeography): PathPlace | undefined {\n const segments = path.split(\"/\");\n const last = segments.pop() ?? \"\";\n if (!last) return undefined;\n const parent = segments.join(\"/\");\n const kindOf = (slug: string): PathPlace[\"kind\"] | undefined => {\n if (geography.suburbs.has(slug)) return \"suburb\";\n if (geography.regions.has(slug)) return \"region\";\n if (slug.endsWith(\"-area\") && geography.regions.has(slug.slice(0, -5))) return \"region\";\n return undefined;\n };\n const whole = kindOf(last);\n if (whole) return { slug: regionSlug(last, geography), kind: whole, stem: `${parent}/{place}` };\n const parts = last.split(\"-\");\n for (let start = 1; start < parts.length; start += 1) {\n const slug = parts.slice(start).join(\"-\");\n const kind = kindOf(slug);\n if (kind) return { slug: regionSlug(slug, geography), kind, stem: `${parent}/${parts.slice(0, start).join(\"-\")}-{place}` };\n }\n return undefined;\n}\n\nfunction regionSlug(slug: string, geography: FullGeography): string {\n return !geography.suburbs.has(slug) && !geography.regions.has(slug) && slug.endsWith(\"-area\") ? slug.slice(0, -5) : slug;\n}\n\n/** A place's display name, for messages and for matching it in copy. */\nexport function placeName(slug: string, geography: FullGeography): string {\n return geography.suburbs.get(slug)?.name ?? geography.regions.get(slug)?.name ?? slug;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"geography.js","sourceRoot":"","sources":["../../../../src/site-system/rules/full/geography.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The places the full tier reasons about: registry suburbs with their region and centre, and the regions. They come\n * from the site's installed site-core geography at run time (the conformance package compiles without site-core), or\n * from `defineFullGeography` in a test.\n */\nimport { createPlaceMask, type SuburbCentre, type SuburbCentreLookup } from \"../../../page-standard.js\";\n\nexport interface FullGeographySuburb {\n readonly slug: string;\n readonly name: string;\n /** The registry region the suburb belongs to. */\n readonly region: string;\n /** The suburb's council, when known. */\n readonly council?: string;\n}\n\nexport interface FullGeographyRegion {\n readonly slug: string;\n readonly name: string;\n /** Every name the region goes by (name, search name and alternates). */\n readonly names: readonly string[];\n}\n\nexport interface FullGeography {\n readonly centreOf: SuburbCentreLookup;\n readonly suburbs: ReadonlyMap<string, FullGeographySuburb>;\n /** Regions and sub-regions, by slug. */\n readonly regions: ReadonlyMap<string, FullGeographyRegion>;\n /** Masks every place name and postcode in lowercase text, so name-swapped copy compares as the same. */\n readonly mask: (text: string) => string;\n /** Lowercase place names (suburbs and regions) and their slugs. */\n readonly names: ReadonlyMap<string, string>;\n /** The slugs of the places a piece of copy names, longest name first (\"North Ryde\" before \"Ryde\"). */\n readonly placesIn: (text: string) => string[];\n}\n\nexport interface FullGeographyInput {\n readonly suburbs: readonly (FullGeographySuburb & { readonly centre?: SuburbCentre })[];\n readonly regions: readonly { readonly slug: string; readonly name: string; readonly names?: readonly string[] }[];\n readonly centreOf?: SuburbCentreLookup;\n}\n\nexport function defineFullGeography(input: FullGeographyInput): FullGeography {\n const suburbs = new Map<string, FullGeographySuburb>();\n const centres = new Map<string, SuburbCentre>();\n for (const suburb of input.suburbs) {\n if (suburbs.has(suburb.slug)) continue;\n suburbs.set(suburb.slug, { slug: suburb.slug, name: suburb.name, region: suburb.region, ...(suburb.council ? { council: suburb.council } : {}) });\n if (suburb.centre) centres.set(suburb.slug, suburb.centre);\n }\n const regions = new Map<string, FullGeographyRegion>();\n for (const region of input.regions) {\n if (!regions.has(region.slug)) regions.set(region.slug, { slug: region.slug, name: region.name, names: [...new Set([region.name, ...(region.names ?? [])])] });\n }\n const names = new Map<string, string>();\n for (const suburb of suburbs.values()) names.set(suburb.name.toLowerCase(), suburb.slug);\n for (const region of regions.values()) for (const name of region.names) if (!names.has(name.toLowerCase())) names.set(name.toLowerCase(), region.slug);\n const alternation = [...names.keys()]\n .filter((name) => name.length >= 3)\n .sort((left, right) => right.length - left.length)\n .map((name) => name.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\").replace(/\\s+/g, \"\\\\s+\"));\n const pattern = alternation.length > 0 ? new RegExp(`\\\\b(?:${alternation.join(\"|\")})\\\\b`, \"gi\") : null;\n return {\n centreOf: input.centreOf ?? ((slug) => centres.get(slug)),\n suburbs,\n regions,\n mask: createPlaceMask([...names.keys()]),\n names,\n placesIn: (text) => {\n if (!pattern) return [];\n const found = new Set<string>();\n for (const match of text.matchAll(pattern)) {\n const slug = names.get(match[0].toLowerCase().replace(/\\s+/g, \" \"));\n if (slug) found.add(slug);\n }\n return [...found];\n },\n };\n}\n\ninterface RegistryLike {\n suburbs: readonly { slug: string; name: string; regionSlug: string; councilSlug: string }[];\n nonSuburbLocalities: readonly { slug: string; name: string; regionSlug: string; councilSlug: string }[];\n regions: readonly { slug: string; name: string; seoName: string; alternateNames?: readonly string[] }[];\n subRegions: readonly { slug: string; name: string }[];\n}\n\n// A variable specifier, so the package compiles without site-core; it resolves to the site's installed copy.\nconst GEOGRAPHY_MODULE: string = \"@brandlab-digital/site-core/geography\";\nlet loaded: Promise<FullGeography> | undefined;\n\n/** The site's installed site-core geography (3.4.0 or later). */\nexport function loadFullGeography(): Promise<FullGeography> {\n loaded ??= (import(GEOGRAPHY_MODULE) as Promise<{\n getSuburbCentre?: SuburbCentreLookup;\n sydneyGeography: RegistryLike;\n centralCoastGeography: RegistryLike;\n }>).then((module) => {\n if (typeof module.getSuburbCentre !== \"function\") throw new Error(\"the installed site-core geography has no suburb centres (3.4.0 or later is needed)\");\n const registries = [module.sydneyGeography, module.centralCoastGeography];\n return defineFullGeography({\n suburbs: registries.flatMap((registry) =>\n [...registry.suburbs, ...registry.nonSuburbLocalities].map((place) => ({ slug: place.slug, name: place.name, region: place.regionSlug, council: place.councilSlug }))),\n regions: registries.flatMap((registry) => [\n ...registry.regions.map((region) => ({ slug: region.slug, name: region.name, names: [region.seoName, ...(region.alternateNames ?? [])] })),\n ...registry.subRegions.map((region) => ({ slug: region.slug, name: region.name })),\n ]),\n centreOf: module.getSuburbCentre,\n });\n });\n return loaded;\n}\n\nexport interface PathPlace {\n readonly slug: string;\n readonly kind: \"suburb\" | \"region\";\n /** The path with the place replaced by `{place}`: pages sharing a stem are one family's pages. */\n readonly stem: string;\n}\n\n/** Public slugs that differ from the registry's (strategy.ts `estate.slugs`, 3.14.0): public slug → registry slug. */\nexport interface PathAliases {\n readonly regions: ReadonlyMap<string, string>;\n readonly suburbs: ReadonlyMap<string, string>;\n}\n\n/**\n * The place a path names: its last segment, or the end of its last segment after a hyphen (`/tree-removal-ryde`),\n * when that is a registry suburb or region (`{region}-area` included), or a public slug the estate plan renames\n * (`ryde` for the region `ryde-area`, `ryde-suburb` for the suburb `ryde`), read first. The longest match wins.\n */\nexport function placeOfPath(path: string, geography: FullGeography, aliases?: PathAliases): PathPlace | undefined {\n const segments = path.split(\"/\");\n const last = segments.pop() ?? \"\";\n if (!last) return undefined;\n const parent = segments.join(\"/\");\n const alias = aliases?.suburbs.get(last) ?? aliases?.regions.get(last);\n if (alias !== undefined) return { slug: alias, kind: aliases?.suburbs.has(last) ? \"suburb\" : \"region\", stem: `${parent}/{place}` };\n const kindOf = (slug: string): PathPlace[\"kind\"] | undefined => {\n if (geography.suburbs.has(slug)) return \"suburb\";\n if (geography.regions.has(slug)) return \"region\";\n if (slug.endsWith(\"-area\") && geography.regions.has(slug.slice(0, -5))) return \"region\";\n return undefined;\n };\n const whole = kindOf(last);\n if (whole) return { slug: regionSlug(last, geography), kind: whole, stem: `${parent}/{place}` };\n const parts = last.split(\"-\");\n for (let start = 1; start < parts.length; start += 1) {\n const slug = parts.slice(start).join(\"-\");\n const kind = kindOf(slug);\n if (kind) return { slug: regionSlug(slug, geography), kind, stem: `${parent}/${parts.slice(0, start).join(\"-\")}-{place}` };\n }\n return undefined;\n}\n\nfunction regionSlug(slug: string, geography: FullGeography): string {\n return !geography.suburbs.has(slug) && !geography.regions.has(slug) && slug.endsWith(\"-area\") ? slug.slice(0, -5) : slug;\n}\n\n/** A place's display name, for messages and for matching it in copy. */\nexport function placeName(slug: string, geography: FullGeography): string {\n return geography.suburbs.get(slug)?.name ?? geography.regions.get(slug)?.name ?? slug;\n}\n"]}
|
|
@@ -5,16 +5,17 @@ import { type FullGeography } from "./geography.js";
|
|
|
5
5
|
export { FULL_LISTED_FAILURES, type FullContext } from "./common.js";
|
|
6
6
|
export { headingPhrase } from "./e10-phrases.js";
|
|
7
7
|
export { readEstate, type Estate, type EstatePage, type LocationCopy, type ManifestData } from "./estate.js";
|
|
8
|
-
export { defineFullGeography, loadFullGeography, placeOfPath, type FullGeography, type FullGeographyInput } from "./geography.js";
|
|
8
|
+
export { defineFullGeography, loadFullGeography, placeOfPath, type FullGeography, type FullGeographyInput, type PathAliases } from "./geography.js";
|
|
9
9
|
export { K12_FAMILIES, isDataBackedReason } from "./k12-manifests.js";
|
|
10
10
|
export { L3_MAX_LIST, L3_MIN_LIST, L3_TOLERANCE_KM } from "./l3-neighbours.js";
|
|
11
11
|
export { L4_MIN_ROTATION_PAGES, L4_MIN_TEMPLATE_WORDS } from "./l4-local-copy.js";
|
|
12
12
|
export { yearStamp } from "./l10-location-copy.js";
|
|
13
13
|
export { L6_MIN_INBOUND, L6_MIN_PAGE_TYPES, L7_MAX_DEPTH, clickDepths } from "./links.js";
|
|
14
14
|
export { sydneyDay } from "./loc-index.js";
|
|
15
|
+
export { W3_FAMILIES, W3_MIN_REVALIDATE_SECONDS, refreshInterval } from "./w3-refresh.js";
|
|
15
16
|
export { FULL_STRATEGY_FILE, WAVE_MAX_GAP_DAYS, WAVE_MIN_GAP_DAYS, matchesWaveRoute, normalisePhrase, readFullStrategy, waveScheduleProblems } from "./strategy.js";
|
|
16
17
|
/** The full-tier checks this module runs, in the order their findings are returned. */
|
|
17
|
-
export declare const FULL_TIER_CHECK_IDS: readonly ["K12", "E5", "E10", "L3", "L4", "L6", "L7", "L8", "L9", "L10", "S4", "LOC-INDEX"];
|
|
18
|
+
export declare const FULL_TIER_CHECK_IDS: readonly ["K12", "K12-slots", "E1-pattern", "E5", "E10", "L3", "L4", "L6", "L7", "L8", "L9", "L10", "S4", "LOC-INDEX", "W3"];
|
|
18
19
|
export interface FullTierRuleOptions {
|
|
19
20
|
/** The day waves are judged on. Defaults to now. */
|
|
20
21
|
readonly asOf?: Date;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/site-system/rules/full/index.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAqB,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAC5E,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAChE,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/site-system/rules/full/index.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAqB,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAC5E,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAChE,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,aAAa,CAAC;AAI/C,OAAO,EAAqB,KAAK,aAAa,EAAoB,MAAM,gBAAgB,CAAC;AAazF,OAAO,EAAE,oBAAoB,EAAE,KAAK,WAAW,EAAE,MAAM,aAAa,CAAC;AACrE,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,KAAK,MAAM,EAAE,KAAK,UAAU,EAAE,KAAK,YAAY,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAC7G,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,WAAW,EAAE,KAAK,aAAa,EAAE,KAAK,kBAAkB,EAAE,KAAK,WAAW,EAAE,MAAM,gBAAgB,CAAC;AACpJ,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACtE,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAC/E,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAClF,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC1F,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,WAAW,EAAE,yBAAyB,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAC1F,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,eAAe,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAEpK,uFAAuF;AACvF,eAAO,MAAM,mBAAmB,8HAAsK,CAAC;AAEvM,MAAM,WAAW,mBAAmB;IAClC,oDAAoD;IACpD,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC;IACrB,qFAAqF;IACrF,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,oGAAoG;IACpG,QAAQ,CAAC,SAAS,CAAC,EAAE,aAAa,GAAG,IAAI,CAAC;IAC1C,uGAAuG;IACvG,QAAQ,CAAC,YAAY,CAAC,EAAE,WAAW,CAAC,cAAc,CAAC,CAAC;CACrD;AA8CD;;;GAGG;AACH,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,uBAAuB,EAC9B,OAAO,GAAE,mBAAwB,GAChC,OAAO,CAAC,SAAS,iBAAiB,EAAE,CAAC,CAoDvC"}
|
|
@@ -2,16 +2,18 @@
|
|
|
2
2
|
* The full-tier rules (release 3.12.0): page families, location estates, section manifests, links and the local data
|
|
3
3
|
* contract. They read the production build's route catalogue and pages (`.next`), the project's strategy record and
|
|
4
4
|
* source, and the site's installed site-core (geography and section orders), and return findings for the ready report:
|
|
5
|
-
* K12 (families), E5, E10, L3, L4, L6, L7, L8, L9, L10 (location), S4
|
|
6
|
-
* the signature the ready gate registers.
|
|
5
|
+
* K12 (families), K12-slots and E1-pattern (3.14.0), E5, E10, L3, L4, L6, L7, L8, L9, L10 (location), S4, the location indexing guard and, from 3.13.0,
|
|
6
|
+
* W3 (estate refresh). The runner has the signature the ready gate registers.
|
|
7
7
|
*/
|
|
8
8
|
import { join } from "node:path";
|
|
9
9
|
import { readSiteSystemProjectConfig } from "../../config.js";
|
|
10
10
|
import {} from "./common.js";
|
|
11
|
+
import { checkE1Pattern } from "./e1-pattern.js";
|
|
11
12
|
import { checkE10 } from "./e10-phrases.js";
|
|
12
13
|
import { readEstate } from "./estate.js";
|
|
13
14
|
import { loadFullGeography } from "./geography.js";
|
|
14
15
|
import { checkK12 } from "./k12-manifests.js";
|
|
16
|
+
import { checkK12Slots } from "./k12-slots.js";
|
|
15
17
|
import { checkL3 } from "./l3-neighbours.js";
|
|
16
18
|
import { checkL4 } from "./l4-local-copy.js";
|
|
17
19
|
import { checkL9 } from "./l9-data-contract.js";
|
|
@@ -20,6 +22,7 @@ import { checkE5, checkL6, checkL7, checkL8, inboundLinks } from "./links.js";
|
|
|
20
22
|
import { checkLocationIndexing, sydneyDay } from "./loc-index.js";
|
|
21
23
|
import { checkS4 } from "./s4-route-files.js";
|
|
22
24
|
import { readFullStrategy } from "./strategy.js";
|
|
25
|
+
import { checkW3 } from "./w3-refresh.js";
|
|
23
26
|
export { FULL_LISTED_FAILURES } from "./common.js";
|
|
24
27
|
export { headingPhrase } from "./e10-phrases.js";
|
|
25
28
|
export { readEstate } from "./estate.js";
|
|
@@ -30,9 +33,10 @@ export { L4_MIN_ROTATION_PAGES, L4_MIN_TEMPLATE_WORDS } from "./l4-local-copy.js
|
|
|
30
33
|
export { yearStamp } from "./l10-location-copy.js";
|
|
31
34
|
export { L6_MIN_INBOUND, L6_MIN_PAGE_TYPES, L7_MAX_DEPTH, clickDepths } from "./links.js";
|
|
32
35
|
export { sydneyDay } from "./loc-index.js";
|
|
36
|
+
export { W3_FAMILIES, W3_MIN_REVALIDATE_SECONDS, refreshInterval } from "./w3-refresh.js";
|
|
33
37
|
export { FULL_STRATEGY_FILE, WAVE_MAX_GAP_DAYS, WAVE_MIN_GAP_DAYS, matchesWaveRoute, normalisePhrase, readFullStrategy, waveScheduleProblems } from "./strategy.js";
|
|
34
38
|
/** The full-tier checks this module runs, in the order their findings are returned. */
|
|
35
|
-
export const FULL_TIER_CHECK_IDS = ["K12", "E5", "E10", "L3", "L4", "L6", "L7", "L8", "L9", "L10", "S4", "LOC-INDEX"];
|
|
39
|
+
export const FULL_TIER_CHECK_IDS = ["K12", "K12-slots", "E1-pattern", "E5", "E10", "L3", "L4", "L6", "L7", "L8", "L9", "L10", "S4", "LOC-INDEX", "W3"];
|
|
36
40
|
// Variable specifiers, so the package compiles without site-core; they resolve to the site's installed copy.
|
|
37
41
|
const SECTION_ORDER_MODULE = "@brandlab-digital/site-core/section-order";
|
|
38
42
|
async function loadDefaultOrder() {
|
|
@@ -55,17 +59,26 @@ async function loadDefaultOrder() {
|
|
|
55
59
|
return `site-core's section orders could not be imported: ${error instanceof Error ? error.message : String(error)}`;
|
|
56
60
|
}
|
|
57
61
|
}
|
|
58
|
-
/** The declared tier
|
|
62
|
+
/** The declared tier, the representative service route and the W3 exceptions; null tier when the config cannot be read in full. */
|
|
59
63
|
function projectSettings(projectRoot) {
|
|
60
64
|
try {
|
|
61
65
|
const config = readSiteSystemProjectConfig(projectRoot);
|
|
62
66
|
// A config the gate cannot read in full could hide a full tier, so the checks run.
|
|
63
|
-
return {
|
|
67
|
+
return {
|
|
68
|
+
tier: config.complete ? (config.siteSystem?.tier ?? "none") : null,
|
|
69
|
+
service: config.representativeRoutes["service"],
|
|
70
|
+
refreshExceptions: config.siteSystem?.refreshExceptions ?? [],
|
|
71
|
+
};
|
|
64
72
|
}
|
|
65
73
|
catch {
|
|
66
|
-
return { tier: null, service: undefined };
|
|
74
|
+
return { tier: null, service: undefined, refreshExceptions: [] };
|
|
67
75
|
}
|
|
68
76
|
}
|
|
77
|
+
/** The estate plan's public slugs as path aliases: public slug → registry slug. */
|
|
78
|
+
function pathAliases(plan) {
|
|
79
|
+
const flip = (renames) => new Map(Object.entries(renames ?? {}).map(([registry, slug]) => [slug, registry]));
|
|
80
|
+
return { regions: flip(plan.slugs?.regions), suburbs: flip(plan.slugs?.suburbs) };
|
|
81
|
+
}
|
|
69
82
|
/**
|
|
70
83
|
* Runs the full-tier rules over the built estate at `projectRoot`. A site that declares a lower tier (or none) gets no
|
|
71
84
|
* findings: the report shows the full checks as not run, which never blocks below the full tier.
|
|
@@ -89,25 +102,30 @@ export async function runFullTierRules(input, options = {}) {
|
|
|
89
102
|
geographyProblem = `site-core's geography could not be imported: ${error instanceof Error ? error.message : String(error)}`;
|
|
90
103
|
}
|
|
91
104
|
}
|
|
105
|
+
const today = sydneyDay(options.asOf ?? new Date());
|
|
106
|
+
const strategy = readFullStrategy(input.projectRoot, today);
|
|
92
107
|
const estate = readEstate({
|
|
93
108
|
pages: input.pages,
|
|
94
109
|
buildDir: options.buildDir ?? input.buildDir ?? join(input.projectRoot, ".next"),
|
|
95
110
|
geography,
|
|
96
111
|
...(settings.service ? { seeds: { [settings.service]: "service" } } : {}),
|
|
112
|
+
...(strategy.estate?.slugs ? { aliases: pathAliases(strategy.estate) } : {}),
|
|
97
113
|
});
|
|
98
|
-
const today = sydneyDay(options.asOf ?? new Date());
|
|
99
114
|
const context = {
|
|
100
115
|
estate,
|
|
101
116
|
geography,
|
|
102
117
|
geographyProblem,
|
|
103
|
-
strategy
|
|
118
|
+
strategy,
|
|
104
119
|
projectRoot: input.projectRoot,
|
|
120
|
+
refreshExceptions: settings.refreshExceptions,
|
|
105
121
|
today,
|
|
106
122
|
defaultOrder: options.defaultOrder ?? (await loadDefaultOrder()),
|
|
107
123
|
};
|
|
108
124
|
const inbound = inboundLinks(estate);
|
|
109
125
|
const findings = [
|
|
110
126
|
...checkK12(context),
|
|
127
|
+
...checkK12Slots(context),
|
|
128
|
+
...checkE1Pattern(context),
|
|
111
129
|
...checkE5(context, inbound),
|
|
112
130
|
...checkE10(context),
|
|
113
131
|
...checkL3(context),
|
|
@@ -121,6 +139,6 @@ export async function runFullTierRules(input, options = {}) {
|
|
|
121
139
|
// The copy is only for L3, L4 and L10; let it go before the source scan.
|
|
122
140
|
for (const page of estate.pages)
|
|
123
141
|
page.copy = undefined;
|
|
124
|
-
return [...findings, ...checkS4(context), ...checkLocationIndexing(context)];
|
|
142
|
+
return [...findings, ...checkS4(context), ...checkLocationIndexing(context), ...checkW3(context)];
|
|
125
143
|
}
|
|
126
144
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/site-system/rules/full/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The full-tier rules (release 3.12.0): page families, location estates, section manifests, links and the local data\n * contract. They read the production build's route catalogue and pages (`.next`), the project's strategy record and\n * source, and the site's installed site-core (geography and section orders), and return findings for the ready report:\n * K12 (families), E5, E10, L3, L4, L6, L7, L8, L9, L10 (location), S4 and the location indexing guard. The runner has\n * the signature the ready gate registers.\n */\nimport { join } from \"node:path\";\nimport { readSiteSystemProjectConfig } from \"../../config.js\";\nimport type { SiteSystemCheckId, SiteSystemFinding } from \"../../checks.js\";\nimport type { SiteSystemRenderedInput } from \"../../runners.js\";\nimport { type FullContext } from \"./common.js\";\nimport { checkE10 } from \"./e10-phrases.js\";\nimport { readEstate } from \"./estate.js\";\nimport { loadFullGeography, type FullGeography } from \"./geography.js\";\nimport { checkK12 } from \"./k12-manifests.js\";\nimport { checkL3 } from \"./l3-neighbours.js\";\nimport { checkL4 } from \"./l4-local-copy.js\";\nimport { checkL9 } from \"./l9-data-contract.js\";\nimport { checkL10 } from \"./l10-location-copy.js\";\nimport { checkE5, checkL6, checkL7, checkL8, inboundLinks } from \"./links.js\";\nimport { checkLocationIndexing, sydneyDay } from \"./loc-index.js\";\nimport { checkS4 } from \"./s4-route-files.js\";\nimport { readFullStrategy } from \"./strategy.js\";\n\nexport { FULL_LISTED_FAILURES, type FullContext } from \"./common.js\";\nexport { headingPhrase } from \"./e10-phrases.js\";\nexport { readEstate, type Estate, type EstatePage, type LocationCopy, type ManifestData } from \"./estate.js\";\nexport { defineFullGeography, loadFullGeography, placeOfPath, type FullGeography, type FullGeographyInput } from \"./geography.js\";\nexport { K12_FAMILIES, isDataBackedReason } from \"./k12-manifests.js\";\nexport { L3_MAX_LIST, L3_MIN_LIST, L3_TOLERANCE_KM } from \"./l3-neighbours.js\";\nexport { L4_MIN_ROTATION_PAGES, L4_MIN_TEMPLATE_WORDS } from \"./l4-local-copy.js\";\nexport { yearStamp } from \"./l10-location-copy.js\";\nexport { L6_MIN_INBOUND, L6_MIN_PAGE_TYPES, L7_MAX_DEPTH, clickDepths } from \"./links.js\";\nexport { sydneyDay } from \"./loc-index.js\";\nexport { FULL_STRATEGY_FILE, WAVE_MAX_GAP_DAYS, WAVE_MIN_GAP_DAYS, matchesWaveRoute, normalisePhrase, readFullStrategy, waveScheduleProblems } from \"./strategy.js\";\n\n/** The full-tier checks this module runs, in the order their findings are returned. */\nexport const FULL_TIER_CHECK_IDS = [\"K12\", \"E5\", \"E10\", \"L3\", \"L4\", \"L6\", \"L7\", \"L8\", \"L9\", \"L10\", \"S4\", \"LOC-INDEX\"] as const satisfies readonly SiteSystemCheckId[];\n\nexport interface FullTierRuleOptions {\n /** The day waves are judged on. Defaults to now. */\n readonly asOf?: Date;\n /** The build directory; defaults to `input.buildDir`, then `<projectRoot>/.next`. */\n readonly buildDir?: string;\n /** Places for a test; a real run loads the site's site-core geography. Null runs without places. */\n readonly geography?: FullGeography | null;\n /** Canonical section ids per family for a test; a real run loads site-core's `defaultSectionOrder`. */\n readonly defaultOrder?: FullContext[\"defaultOrder\"];\n}\n\n// Variable specifiers, so the package compiles without site-core; they resolve to the site's installed copy.\nconst SECTION_ORDER_MODULE: string = \"@brandlab-digital/site-core/section-order\";\n\nasync function loadDefaultOrder(): Promise<FullContext[\"defaultOrder\"]> {\n try {\n const module = (await import(SECTION_ORDER_MODULE)) as {\n defaultSectionOrder?: (family: string, trade?: string) => { cap: number; entries: readonly { id: string; requirement: string }[] };\n };\n const defaults = module.defaultSectionOrder;\n if (typeof defaults !== \"function\") return \"the installed site-core has no defaultSectionOrder (3.11.0 or later is needed)\";\n return (family, trade) => {\n try {\n const order = defaults(family, trade);\n return { cap: order.cap, entries: order.entries.map((entry) => ({ id: entry.id, requirement: entry.requirement })) };\n } catch {\n return undefined;\n }\n };\n } catch (error) {\n return `site-core's section orders could not be imported: ${error instanceof Error ? error.message : String(error)}`;\n }\n}\n\n/** The declared tier and the representative service route; null tier when the config cannot be read in full. */\nfunction projectSettings(projectRoot: string): { tier: string | null; service: string | undefined } {\n try {\n const config = readSiteSystemProjectConfig(projectRoot);\n // A config the gate cannot read in full could hide a full tier, so the checks run.\n return { tier: config.complete ? (config.siteSystem?.tier ?? \"none\") : null, service: config.representativeRoutes[\"service\"] };\n } catch {\n return { tier: null, service: undefined };\n }\n}\n\n/**\n * Runs the full-tier rules over the built estate at `projectRoot`. A site that declares a lower tier (or none) gets no\n * findings: the report shows the full checks as not run, which never blocks below the full tier.\n */\nexport async function runFullTierRules(\n input: SiteSystemRenderedInput,\n options: FullTierRuleOptions = {},\n): Promise<readonly SiteSystemFinding[]> {\n const settings = projectSettings(input.projectRoot);\n if (settings.tier !== null && settings.tier !== \"full\") return [];\n let geography: FullGeography | null = null;\n let geographyProblem = \"\";\n if (options.geography !== undefined) {\n geography = options.geography;\n if (!geography) geographyProblem = \"no geography was supplied\";\n } else {\n try {\n geography = await loadFullGeography();\n } catch (error) {\n geographyProblem = `site-core's geography could not be imported: ${error instanceof Error ? error.message : String(error)}`;\n }\n }\n const estate = readEstate({\n pages: input.pages,\n buildDir: options.buildDir ?? input.buildDir ?? join(input.projectRoot, \".next\"),\n geography,\n ...(settings.service ? { seeds: { [settings.service]: \"service\" } } : {}),\n });\n const today = sydneyDay(options.asOf ?? new Date());\n const context: FullContext = {\n estate,\n geography,\n geographyProblem,\n strategy: readFullStrategy(input.projectRoot, today),\n projectRoot: input.projectRoot,\n today,\n defaultOrder: options.defaultOrder ?? (await loadDefaultOrder()),\n };\n const inbound = inboundLinks(estate);\n const findings = [\n ...checkK12(context),\n ...checkE5(context, inbound),\n ...checkE10(context),\n ...checkL3(context),\n ...checkL4(context),\n ...checkL6(context, inbound),\n ...checkL7(context),\n ...checkL8(context),\n ...checkL9(context),\n ...checkL10(context),\n ];\n // The copy is only for L3, L4 and L10; let it go before the source scan.\n for (const page of estate.pages) page.copy = undefined;\n return [...findings, ...checkS4(context), ...checkLocationIndexing(context)];\n}\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/site-system/rules/full/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The full-tier rules (release 3.12.0): page families, location estates, section manifests, links and the local data\n * contract. They read the production build's route catalogue and pages (`.next`), the project's strategy record and\n * source, and the site's installed site-core (geography and section orders), and return findings for the ready report:\n * K12 (families), K12-slots and E1-pattern (3.14.0), E5, E10, L3, L4, L6, L7, L8, L9, L10 (location), S4, the location indexing guard and, from 3.13.0,\n * W3 (estate refresh). The runner has the signature the ready gate registers.\n */\nimport { join } from \"node:path\";\nimport { readSiteSystemProjectConfig, type SiteSystemRefreshException } from \"../../config.js\";\nimport type { SiteSystemCheckId, SiteSystemFinding } from \"../../checks.js\";\nimport type { SiteSystemRenderedInput } from \"../../runners.js\";\nimport { type FullContext } from \"./common.js\";\nimport { checkE1Pattern } from \"./e1-pattern.js\";\nimport { checkE10 } from \"./e10-phrases.js\";\nimport { readEstate } from \"./estate.js\";\nimport { loadFullGeography, type FullGeography, type PathAliases } from \"./geography.js\";\nimport { checkK12 } from \"./k12-manifests.js\";\nimport { checkK12Slots } from \"./k12-slots.js\";\nimport { checkL3 } from \"./l3-neighbours.js\";\nimport { checkL4 } from \"./l4-local-copy.js\";\nimport { checkL9 } from \"./l9-data-contract.js\";\nimport { checkL10 } from \"./l10-location-copy.js\";\nimport { checkE5, checkL6, checkL7, checkL8, inboundLinks } from \"./links.js\";\nimport { checkLocationIndexing, sydneyDay } from \"./loc-index.js\";\nimport { checkS4 } from \"./s4-route-files.js\";\nimport { readFullStrategy } from \"./strategy.js\";\nimport { checkW3 } from \"./w3-refresh.js\";\n\nexport { FULL_LISTED_FAILURES, type FullContext } from \"./common.js\";\nexport { headingPhrase } from \"./e10-phrases.js\";\nexport { readEstate, type Estate, type EstatePage, type LocationCopy, type ManifestData } from \"./estate.js\";\nexport { defineFullGeography, loadFullGeography, placeOfPath, type FullGeography, type FullGeographyInput, type PathAliases } from \"./geography.js\";\nexport { K12_FAMILIES, isDataBackedReason } from \"./k12-manifests.js\";\nexport { L3_MAX_LIST, L3_MIN_LIST, L3_TOLERANCE_KM } from \"./l3-neighbours.js\";\nexport { L4_MIN_ROTATION_PAGES, L4_MIN_TEMPLATE_WORDS } from \"./l4-local-copy.js\";\nexport { yearStamp } from \"./l10-location-copy.js\";\nexport { L6_MIN_INBOUND, L6_MIN_PAGE_TYPES, L7_MAX_DEPTH, clickDepths } from \"./links.js\";\nexport { sydneyDay } from \"./loc-index.js\";\nexport { W3_FAMILIES, W3_MIN_REVALIDATE_SECONDS, refreshInterval } from \"./w3-refresh.js\";\nexport { FULL_STRATEGY_FILE, WAVE_MAX_GAP_DAYS, WAVE_MIN_GAP_DAYS, matchesWaveRoute, normalisePhrase, readFullStrategy, waveScheduleProblems } from \"./strategy.js\";\n\n/** The full-tier checks this module runs, in the order their findings are returned. */\nexport const FULL_TIER_CHECK_IDS = [\"K12\", \"K12-slots\", \"E1-pattern\", \"E5\", \"E10\", \"L3\", \"L4\", \"L6\", \"L7\", \"L8\", \"L9\", \"L10\", \"S4\", \"LOC-INDEX\", \"W3\"] as const satisfies readonly SiteSystemCheckId[];\n\nexport interface FullTierRuleOptions {\n /** The day waves are judged on. Defaults to now. */\n readonly asOf?: Date;\n /** The build directory; defaults to `input.buildDir`, then `<projectRoot>/.next`. */\n readonly buildDir?: string;\n /** Places for a test; a real run loads the site's site-core geography. Null runs without places. */\n readonly geography?: FullGeography | null;\n /** Canonical section ids per family for a test; a real run loads site-core's `defaultSectionOrder`. */\n readonly defaultOrder?: FullContext[\"defaultOrder\"];\n}\n\n// Variable specifiers, so the package compiles without site-core; they resolve to the site's installed copy.\nconst SECTION_ORDER_MODULE: string = \"@brandlab-digital/site-core/section-order\";\n\nasync function loadDefaultOrder(): Promise<FullContext[\"defaultOrder\"]> {\n try {\n const module = (await import(SECTION_ORDER_MODULE)) as {\n defaultSectionOrder?: (family: string, trade?: string) => { cap: number; entries: readonly { id: string; requirement: string }[] };\n };\n const defaults = module.defaultSectionOrder;\n if (typeof defaults !== \"function\") return \"the installed site-core has no defaultSectionOrder (3.11.0 or later is needed)\";\n return (family, trade) => {\n try {\n const order = defaults(family, trade);\n return { cap: order.cap, entries: order.entries.map((entry) => ({ id: entry.id, requirement: entry.requirement })) };\n } catch {\n return undefined;\n }\n };\n } catch (error) {\n return `site-core's section orders could not be imported: ${error instanceof Error ? error.message : String(error)}`;\n }\n}\n\n/** The declared tier, the representative service route and the W3 exceptions; null tier when the config cannot be read in full. */\nfunction projectSettings(projectRoot: string): { tier: string | null; service: string | undefined; refreshExceptions: readonly SiteSystemRefreshException[] } {\n try {\n const config = readSiteSystemProjectConfig(projectRoot);\n // A config the gate cannot read in full could hide a full tier, so the checks run.\n return {\n tier: config.complete ? (config.siteSystem?.tier ?? \"none\") : null,\n service: config.representativeRoutes[\"service\"],\n refreshExceptions: config.siteSystem?.refreshExceptions ?? [],\n };\n } catch {\n return { tier: null, service: undefined, refreshExceptions: [] };\n }\n}\n\n/** The estate plan's public slugs as path aliases: public slug → registry slug. */\nfunction pathAliases(plan: NonNullable<FullContext[\"strategy\"][\"estate\"]>): PathAliases {\n const flip = (renames: Readonly<Record<string, string>> | undefined) => new Map(Object.entries(renames ?? {}).map(([registry, slug]) => [slug, registry]));\n return { regions: flip(plan.slugs?.regions), suburbs: flip(plan.slugs?.suburbs) };\n}\n\n/**\n * Runs the full-tier rules over the built estate at `projectRoot`. A site that declares a lower tier (or none) gets no\n * findings: the report shows the full checks as not run, which never blocks below the full tier.\n */\nexport async function runFullTierRules(\n input: SiteSystemRenderedInput,\n options: FullTierRuleOptions = {},\n): Promise<readonly SiteSystemFinding[]> {\n const settings = projectSettings(input.projectRoot);\n if (settings.tier !== null && settings.tier !== \"full\") return [];\n let geography: FullGeography | null = null;\n let geographyProblem = \"\";\n if (options.geography !== undefined) {\n geography = options.geography;\n if (!geography) geographyProblem = \"no geography was supplied\";\n } else {\n try {\n geography = await loadFullGeography();\n } catch (error) {\n geographyProblem = `site-core's geography could not be imported: ${error instanceof Error ? error.message : String(error)}`;\n }\n }\n const today = sydneyDay(options.asOf ?? new Date());\n const strategy = readFullStrategy(input.projectRoot, today);\n const estate = readEstate({\n pages: input.pages,\n buildDir: options.buildDir ?? input.buildDir ?? join(input.projectRoot, \".next\"),\n geography,\n ...(settings.service ? { seeds: { [settings.service]: \"service\" } } : {}),\n ...(strategy.estate?.slugs ? { aliases: pathAliases(strategy.estate) } : {}),\n });\n const context: FullContext = {\n estate,\n geography,\n geographyProblem,\n strategy,\n projectRoot: input.projectRoot,\n refreshExceptions: settings.refreshExceptions,\n today,\n defaultOrder: options.defaultOrder ?? (await loadDefaultOrder()),\n };\n const inbound = inboundLinks(estate);\n const findings = [\n ...checkK12(context),\n ...checkK12Slots(context),\n ...checkE1Pattern(context),\n ...checkE5(context, inbound),\n ...checkE10(context),\n ...checkL3(context),\n ...checkL4(context),\n ...checkL6(context, inbound),\n ...checkL7(context),\n ...checkL8(context),\n ...checkL9(context),\n ...checkL10(context),\n ];\n // The copy is only for L3, L4 and L10; let it go before the source scan.\n for (const page of estate.pages) page.copy = undefined;\n return [...findings, ...checkS4(context), ...checkLocationIndexing(context), ...checkW3(context)];\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"k12-slots.d.ts","sourceRoot":"","sources":["../../../../src/site-system/rules/full/k12-slots.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAAiD,KAAK,WAAW,EAAE,MAAM,aAAa,CAAC;AAG9F,wBAAgB,aAAa,CAAC,OAAO,EAAE,WAAW,GAAG,iBAAiB,EAAE,CA8BvE"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* K12-slots (3.14.0): the lines a trade requires inside a section (site-core `SECTION_TRADE_PROFILES`, matrix part
|
|
3
|
+
* B), such as the tree trade's council permit note in `LocalFacts` on suburb and service-in-suburb pages. The trade is
|
|
4
|
+
* the one `defineSectionOrder` names in strategy.ts. Wherever the section renders, it carries the slot
|
|
5
|
+
* (`data-bl-slot="permit"`), or lists it in `data-bl-slot-skip` with a reason drawn from its data
|
|
6
|
+
* (`permit:no-current-council-record`), which passes with a warning so the record gets re-checked. A section that
|
|
7
|
+
* does not render is K12's business.
|
|
8
|
+
*/
|
|
9
|
+
import { requiredSectionSlots } from "@brandlab-digital/site-core/section-order";
|
|
10
|
+
import { fail, notApplicable, outcome, plural, warning } from "./common.js";
|
|
11
|
+
import { isDataBackedReason } from "./k12-manifests.js";
|
|
12
|
+
export function checkK12Slots(context) {
|
|
13
|
+
const trade = context.strategy.trade;
|
|
14
|
+
const failures = [];
|
|
15
|
+
let checked = 0;
|
|
16
|
+
for (const page of context.estate.pages) {
|
|
17
|
+
if (!page.family)
|
|
18
|
+
continue;
|
|
19
|
+
const required = requiredSectionSlots(page.family, trade);
|
|
20
|
+
for (const { section, slot } of required) {
|
|
21
|
+
if (!page.sections.includes(section))
|
|
22
|
+
continue;
|
|
23
|
+
checked += 1;
|
|
24
|
+
const found = page.slots?.get(section);
|
|
25
|
+
if (found?.present.includes(slot))
|
|
26
|
+
continue;
|
|
27
|
+
const skip = found?.skipped.find((entry) => entry.slot === slot);
|
|
28
|
+
if (skip && isDataBackedReason(skip.reason)) {
|
|
29
|
+
failures.push(warning("K12-slots", page.path, `${page.path} leaves the ${slot} line out of its "${section}" section (${skip.reason}); bring the record up to date so it shows again.`));
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
failures.push(fail("K12-slots", page.path, `${page.path} renders "${section}" without the ${slot} line the ${trade} trade requires on ${page.family} pages, and gives no data-backed reason (${skip ? skip.reason : "none"}).`));
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
if (checked === 0) {
|
|
36
|
+
return [notApplicable("K12-slots", trade ? `The ${trade} trade requires no line inside the sections these pages render.` : "strategy.ts names no trade in defineSectionOrder, so no trade lines are required.")];
|
|
37
|
+
}
|
|
38
|
+
return outcome("K12-slots", failures, `${plural(checked, "required trade line")} ${checked === 1 ? "is" : "are"} in place.`);
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=k12-slots.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"k12-slots.js","sourceRoot":"","sources":["../../../../src/site-system/rules/full/k12-slots.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,oBAAoB,EAA+C,MAAM,2CAA2C,CAAC;AAE9H,OAAO,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAoB,MAAM,aAAa,CAAC;AAC9F,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAExD,MAAM,UAAU,aAAa,CAAC,OAAoB;IAChD,MAAM,KAAK,GAAG,OAAO,CAAC,QAAQ,CAAC,KAAkC,CAAC;IAClE,MAAM,QAAQ,GAAwB,EAAE,CAAC;IACzC,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QACxC,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE,SAAS;QAC3B,MAAM,QAAQ,GAAG,oBAAoB,CAAC,IAAI,CAAC,MAA4B,EAAE,KAAK,CAAC,CAAC;QAChF,KAAK,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,QAAQ,EAAE,CAAC;YACzC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC;gBAAE,SAAS;YAC/C,OAAO,IAAI,CAAC,CAAC;YACb,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;YACvC,IAAI,KAAK,EAAE,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC;gBAAE,SAAS;YAC5C,MAAM,IAAI,GAAG,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;YACjE,IAAI,IAAI,IAAI,kBAAkB,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;gBAC5C,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,IAAI,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,IAAI,eAAe,IAAI,qBAAqB,OAAO,cAAc,IAAI,CAAC,MAAM,mDAAmD,CAAC,CAAC,CAAC;gBACxL,SAAS;YACX,CAAC;YACD,QAAQ,CAAC,IAAI,CACX,IAAI,CACF,WAAW,EACX,IAAI,CAAC,IAAI,EACT,GAAG,IAAI,CAAC,IAAI,aAAa,OAAO,iBAAiB,IAAI,aAAa,KAAK,sBAAsB,IAAI,CAAC,MAAM,4CAA4C,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,IAAI,CACpL,CACF,CAAC;QACJ,CAAC;IACH,CAAC;IACD,IAAI,OAAO,KAAK,CAAC,EAAE,CAAC;QAClB,OAAO,CAAC,aAAa,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC,CAAC,OAAO,KAAK,iEAAiE,CAAC,CAAC,CAAC,mFAAmF,CAAC,CAAC,CAAC;IACnN,CAAC;IACD,OAAO,OAAO,CAAC,WAAW,EAAE,QAAQ,EAAE,GAAG,MAAM,CAAC,OAAO,EAAE,qBAAqB,CAAC,IAAI,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,YAAY,CAAC,CAAC;AAC/H,CAAC","sourcesContent":["/**\n * K12-slots (3.14.0): the lines a trade requires inside a section (site-core `SECTION_TRADE_PROFILES`, matrix part\n * B), such as the tree trade's council permit note in `LocalFacts` on suburb and service-in-suburb pages. The trade is\n * the one `defineSectionOrder` names in strategy.ts. Wherever the section renders, it carries the slot\n * (`data-bl-slot=\"permit\"`), or lists it in `data-bl-slot-skip` with a reason drawn from its data\n * (`permit:no-current-council-record`), which passes with a warning so the record gets re-checked. A section that\n * does not render is K12's business.\n */\nimport { requiredSectionSlots, type BrandlabPageFamily, type BrandlabTrade } from \"@brandlab-digital/site-core/section-order\";\nimport type { SiteSystemFinding } from \"../../checks.js\";\nimport { fail, notApplicable, outcome, plural, warning, type FullContext } from \"./common.js\";\nimport { isDataBackedReason } from \"./k12-manifests.js\";\n\nexport function checkK12Slots(context: FullContext): SiteSystemFinding[] {\n const trade = context.strategy.trade as BrandlabTrade | undefined;\n const failures: SiteSystemFinding[] = [];\n let checked = 0;\n for (const page of context.estate.pages) {\n if (!page.family) continue;\n const required = requiredSectionSlots(page.family as BrandlabPageFamily, trade);\n for (const { section, slot } of required) {\n if (!page.sections.includes(section)) continue;\n checked += 1;\n const found = page.slots?.get(section);\n if (found?.present.includes(slot)) continue;\n const skip = found?.skipped.find((entry) => entry.slot === slot);\n if (skip && isDataBackedReason(skip.reason)) {\n failures.push(warning(\"K12-slots\", page.path, `${page.path} leaves the ${slot} line out of its \"${section}\" section (${skip.reason}); bring the record up to date so it shows again.`));\n continue;\n }\n failures.push(\n fail(\n \"K12-slots\",\n page.path,\n `${page.path} renders \"${section}\" without the ${slot} line the ${trade} trade requires on ${page.family} pages, and gives no data-backed reason (${skip ? skip.reason : \"none\"}).`,\n ),\n );\n }\n }\n if (checked === 0) {\n return [notApplicable(\"K12-slots\", trade ? `The ${trade} trade requires no line inside the sections these pages render.` : \"strategy.ts names no trade in defineSectionOrder, so no trade lines are required.\")];\n }\n return outcome(\"K12-slots\", failures, `${plural(checked, \"required trade line\")} ${checked === 1 ? \"is\" : \"are\"} in place.`);\n}\n"]}
|
|
@@ -5,6 +5,11 @@
|
|
|
5
5
|
* sitemap. A wave that is due has every page it lists built, indexable and in the sitemap. The schedule itself goes
|
|
6
6
|
* out in date order, 14 to 28 days apart, so it never holds a page open-ended. Nothing here suggests cutting, capping
|
|
7
7
|
* or noindexing a page.
|
|
8
|
+
*
|
|
9
|
+
* From 3.14.0 an existing estate can record its plan (`estate` in strategy.ts): the pages indexed now, return waves
|
|
10
|
+
* (`kind: 'return'`: pages live now that come back to the index on the day, so they must be in the build already), the
|
|
11
|
+
* family paths and the public slugs. With a plan, every location page is indexed now or in a wave, a page indexed now
|
|
12
|
+
* never waits for a wave, and a region page that takes a suburb's slug sends that suburb to "{slug}-suburb".
|
|
8
13
|
*/
|
|
9
14
|
import type { SiteSystemFinding } from "../../checks.js";
|
|
10
15
|
import { type FullContext } from "./common.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loc-index.d.ts","sourceRoot":"","sources":["../../../../src/site-system/rules/full/loc-index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"loc-index.d.ts","sourceRoot":"","sources":["../../../../src/site-system/rules/full/loc-index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAAoD,KAAK,WAAW,EAAE,MAAM,aAAa,CAAC;AAEjG,wHAAwH;AACxH,eAAO,MAAM,kBAAkB,gFAAiF,CAAC;AAGjH,0CAA0C;AAC1C,wBAAgB,SAAS,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAE5C;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,WAAW,GAAG,iBAAiB,EAAE,CAgF/E"}
|
|
@@ -23,15 +23,27 @@ export function checkLocationIndexing(context) {
|
|
|
23
23
|
if (estate.sitemap === null && pages.length > 0) {
|
|
24
24
|
failures.push(fail("LOC-INDEX", undefined, "The build holds no sitemap file, so whether every location page is listed could not be checked."));
|
|
25
25
|
}
|
|
26
|
+
const plan = strategy.estate;
|
|
27
|
+
const indexedNow = (path) => plan?.indexedNow.some((route) => matchesWaveRoute(path, route)) ?? false;
|
|
26
28
|
let live = 0;
|
|
27
29
|
let scheduled = 0;
|
|
28
30
|
for (const page of pages) {
|
|
29
31
|
const wave = waveOf(page.path);
|
|
30
|
-
if (wave &&
|
|
32
|
+
if (plan && !wave && !indexedNow(page.path) && page.family !== "locations-hub" && page.family !== "council") {
|
|
33
|
+
failures.push(fail("LOC-INDEX", page.path, `${page.path} is a location page the estate plan does not account for; list it in estate.indexedNow or in a wave.`));
|
|
34
|
+
}
|
|
35
|
+
const now = indexedNow(page.path);
|
|
36
|
+
const waiting = wave !== undefined && wave.releaseOn > today;
|
|
37
|
+
if (waiting && !now) {
|
|
31
38
|
scheduled += 1;
|
|
32
39
|
continue;
|
|
33
40
|
}
|
|
34
|
-
|
|
41
|
+
// A page indexed now is judged now, even when a wave's pattern also covers it; a wave naming a page the plan's
|
|
42
|
+
// pattern indexes now is a contradiction (the same path in both lists is reported with the plan's problems).
|
|
43
|
+
if (waiting && wave.routes.includes(page.path) && !plan?.indexedNow.includes(page.path)) {
|
|
44
|
+
failures.push(fail("LOC-INDEX", page.path, `${page.path} is indexed now in the estate plan, but the wave of ${wave.releaseOn} still lists it; a page indexed now never waits for a wave.`));
|
|
45
|
+
}
|
|
46
|
+
const dated = waiting ? "it is indexed now in the estate plan" : wave ? `its wave was due on ${wave.releaseOn}` : "no upcoming wave in the recorded schedule dates it";
|
|
35
47
|
const problems = [];
|
|
36
48
|
if (!page.indexable)
|
|
37
49
|
problems.push("is marked noindex");
|
|
@@ -53,6 +65,35 @@ export function checkLocationIndexing(context) {
|
|
|
53
65
|
}
|
|
54
66
|
}
|
|
55
67
|
}
|
|
68
|
+
// A return wave brings back pages that are live now, so its pages are in the build before the day.
|
|
69
|
+
for (const wave of waves) {
|
|
70
|
+
if (wave.kind !== "return" || wave.releaseOn <= today)
|
|
71
|
+
continue;
|
|
72
|
+
for (const route of wave.routes) {
|
|
73
|
+
const matched = route.includes("*") ? estate.pages.some((page) => matchesWaveRoute(page.path, route)) : estate.byPath.has(route);
|
|
74
|
+
if (!matched)
|
|
75
|
+
failures.push(fail("LOC-INDEX", route, `The return wave of ${wave.releaseOn} brings back ${route}, but the build has no ${route.includes("*") ? "page matching it" : "such page"}; a return wave lists pages that are live now.`));
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if (plan) {
|
|
79
|
+
for (const route of plan.indexedNow) {
|
|
80
|
+
const matched = route.includes("*") ? estate.pages.some((page) => matchesWaveRoute(page.path, route)) : estate.byPath.has(route);
|
|
81
|
+
if (!matched)
|
|
82
|
+
failures.push(fail("LOC-INDEX", route, `estate.indexedNow lists ${route}, but the build has no ${route.includes("*") ? "page matching it" : "such page"}.`));
|
|
83
|
+
}
|
|
84
|
+
const parent = (template) => template?.slice(0, template.lastIndexOf("/"));
|
|
85
|
+
const shared = plan.paths?.region !== undefined && parent(plan.paths.region) === parent(plan.paths.suburb);
|
|
86
|
+
for (const page of shared ? pages : []) {
|
|
87
|
+
if (page.place?.kind !== "region")
|
|
88
|
+
continue;
|
|
89
|
+
const slug = page.path.slice(page.path.lastIndexOf("/") + 1);
|
|
90
|
+
if (!context.geography?.suburbs.has(slug) || page.path !== `${parent(plan.paths?.region)}/${slug}`)
|
|
91
|
+
continue;
|
|
92
|
+
if (plan.slugs?.suburbs?.[slug] !== `${slug}-suburb`) {
|
|
93
|
+
failures.push(fail("LOC-INDEX", page.path, `The region page ${page.path} takes the slug of the suburb ${slug}; that suburb's pages take "${slug}-suburb" (estate.slugs.suburbs).`));
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
56
97
|
const upcoming = scheduled > 0 ? `; ${plural(scheduled, "more page")} ${scheduled === 1 ? "is" : "are"} dated by upcoming waves` : "";
|
|
57
98
|
return outcome("LOC-INDEX", failures, `All ${plural(live, "released location page")} are indexable and in the sitemap${upcoming}.`);
|
|
58
99
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loc-index.js","sourceRoot":"","sources":["../../../../src/site-system/rules/full/loc-index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The location indexing guard (Site System v2 §4.3 item 3, §4.4 step 8; D4, 20 Oct 2026): every location page stays\n * indexed. A suburb, service-in-suburb or region page is indexable and listed in the sitemap, unless a wave in the\n * recorded schedule (`waves` in strategy.ts) dates it in the future: until that day it may be noindex or out of the\n * sitemap. A wave that is due has every page it lists built, indexable and in the sitemap. The schedule itself goes\n * out in date order, 14 to 28 days apart, so it never holds a page open-ended. Nothing here suggests cutting, capping\n * or noindexing a page.\n */\nimport type { SiteSystemFinding } from \"../../checks.js\";\nimport { fail, needsBuild, notApplicable, outcome, plural, type FullContext } from \"./common.js\";\n\n/** The families the guard covers: the location families, council pages and the locations hub (v2 §4.1, §4.3 item 3). */\nexport const LOC_INDEX_FAMILIES = [\"suburb\", \"service-in-suburb\", \"region\", \"council\", \"locations-hub\"] as const;\nimport { FULL_STRATEGY_FILE, matchesWaveRoute } from \"./strategy.js\";\n\n/** Today's date in Sydney, YYYY-MM-DD. */\nexport function sydneyDay(date: Date): string {\n return new Intl.DateTimeFormat(\"en-CA\", { timeZone: \"Australia/Sydney\", year: \"numeric\", month: \"2-digit\", day: \"2-digit\" }).format(date);\n}\n\nexport function checkLocationIndexing(context: FullContext): SiteSystemFinding[] {\n const skip = needsBuild(\"LOC-INDEX\", context.estate);\n if (skip) return [skip];\n const { estate, strategy, today } = context;\n const failures: SiteSystemFinding[] = strategy.problems.waves.map((problem) => fail(\"LOC-INDEX\", FULL_STRATEGY_FILE, problem));\n const waves = strategy.waves;\n const waveOf = (path: string) => waves.find((wave) => wave.routes.some((route) => matchesWaveRoute(path, route)));\n const pages = estate.pages.filter((page) => (LOC_INDEX_FAMILIES as readonly string[]).includes(page.family ?? \"\"));\n for (const missing of estate.missingHtml.filter((item) => item.location)) {\n failures.push(fail(\"LOC-INDEX\", missing.path, `${missing.path} is a prerendered location route with no HTML in the build, so it cannot be served or indexed.`));\n }\n if (pages.length === 0 && waves.length === 0) return [...failures, notApplicable(\"LOC-INDEX\", \"The build has no location pages.\")];\n if (estate.sitemap === null && pages.length > 0) {\n failures.push(fail(\"LOC-INDEX\", undefined, \"The build holds no sitemap file, so whether every location page is listed could not be checked.\"));\n }\n\n let live = 0;\n let scheduled = 0;\n for (const page of pages) {\n const wave = waveOf(page.path);\n if (wave && wave.releaseOn > today) {\n scheduled += 1;\n continue;\n }\n const dated = wave ? `its wave was due on ${wave.releaseOn}` : \"no upcoming wave in the recorded schedule dates it\";\n const problems: string[] = [];\n if (!page.indexable) problems.push(\"is marked noindex\");\n if (estate.sitemap && !estate.sitemap.has(page.path)) problems.push(\"is not in the sitemap\");\n if (problems.length > 0) {\n failures.push(fail(\"LOC-INDEX\", page.path, `${page.path} is a location page that ${problems.join(\" and \")}, and ${dated}; every location page is indexable and listed.`));\n } else live += 1;\n }\n\n for (const wave of waves) {\n if (wave.releaseOn > today) continue;\n for (const route of wave.routes) {\n const matched = route.includes(\"*\") ? estate.pages.some((page) => matchesWaveRoute(page.path, route)) : estate.byPath.has(route);\n if (!matched) {\n failures.push(fail(\"LOC-INDEX\", route, `The wave of ${wave.releaseOn} released ${route}, but the build has no ${route.includes(\"*\") ? \"page matching it\" : \"such page\"}.`));\n }\n }\n }\n const upcoming = scheduled > 0 ? `; ${plural(scheduled, \"more page\")} ${scheduled === 1 ? \"is\" : \"are\"} dated by upcoming waves` : \"\";\n return outcome(\"LOC-INDEX\", failures, `All ${plural(live, \"released location page\")} are indexable and in the sitemap${upcoming}.`);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"loc-index.js","sourceRoot":"","sources":["../../../../src/site-system/rules/full/loc-index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The location indexing guard (Site System v2 §4.3 item 3, §4.4 step 8; D4, 20 Oct 2026): every location page stays\n * indexed. A suburb, service-in-suburb or region page is indexable and listed in the sitemap, unless a wave in the\n * recorded schedule (`waves` in strategy.ts) dates it in the future: until that day it may be noindex or out of the\n * sitemap. A wave that is due has every page it lists built, indexable and in the sitemap. The schedule itself goes\n * out in date order, 14 to 28 days apart, so it never holds a page open-ended. Nothing here suggests cutting, capping\n * or noindexing a page.\n *\n * From 3.14.0 an existing estate can record its plan (`estate` in strategy.ts): the pages indexed now, return waves\n * (`kind: 'return'`: pages live now that come back to the index on the day, so they must be in the build already), the\n * family paths and the public slugs. With a plan, every location page is indexed now or in a wave, a page indexed now\n * never waits for a wave, and a region page that takes a suburb's slug sends that suburb to \"{slug}-suburb\".\n */\nimport type { SiteSystemFinding } from \"../../checks.js\";\nimport { fail, needsBuild, notApplicable, outcome, plural, type FullContext } from \"./common.js\";\n\n/** The families the guard covers: the location families, council pages and the locations hub (v2 §4.1, §4.3 item 3). */\nexport const LOC_INDEX_FAMILIES = [\"suburb\", \"service-in-suburb\", \"region\", \"council\", \"locations-hub\"] as const;\nimport { FULL_STRATEGY_FILE, matchesWaveRoute } from \"./strategy.js\";\n\n/** Today's date in Sydney, YYYY-MM-DD. */\nexport function sydneyDay(date: Date): string {\n return new Intl.DateTimeFormat(\"en-CA\", { timeZone: \"Australia/Sydney\", year: \"numeric\", month: \"2-digit\", day: \"2-digit\" }).format(date);\n}\n\nexport function checkLocationIndexing(context: FullContext): SiteSystemFinding[] {\n const skip = needsBuild(\"LOC-INDEX\", context.estate);\n if (skip) return [skip];\n const { estate, strategy, today } = context;\n const failures: SiteSystemFinding[] = strategy.problems.waves.map((problem) => fail(\"LOC-INDEX\", FULL_STRATEGY_FILE, problem));\n const waves = strategy.waves;\n const waveOf = (path: string) => waves.find((wave) => wave.routes.some((route) => matchesWaveRoute(path, route)));\n const pages = estate.pages.filter((page) => (LOC_INDEX_FAMILIES as readonly string[]).includes(page.family ?? \"\"));\n for (const missing of estate.missingHtml.filter((item) => item.location)) {\n failures.push(fail(\"LOC-INDEX\", missing.path, `${missing.path} is a prerendered location route with no HTML in the build, so it cannot be served or indexed.`));\n }\n if (pages.length === 0 && waves.length === 0) return [...failures, notApplicable(\"LOC-INDEX\", \"The build has no location pages.\")];\n if (estate.sitemap === null && pages.length > 0) {\n failures.push(fail(\"LOC-INDEX\", undefined, \"The build holds no sitemap file, so whether every location page is listed could not be checked.\"));\n }\n\n const plan = strategy.estate;\n const indexedNow = (path: string) => plan?.indexedNow.some((route) => matchesWaveRoute(path, route)) ?? false;\n let live = 0;\n let scheduled = 0;\n for (const page of pages) {\n const wave = waveOf(page.path);\n if (plan && !wave && !indexedNow(page.path) && page.family !== \"locations-hub\" && page.family !== \"council\") {\n failures.push(fail(\"LOC-INDEX\", page.path, `${page.path} is a location page the estate plan does not account for; list it in estate.indexedNow or in a wave.`));\n }\n const now = indexedNow(page.path);\n const waiting = wave !== undefined && wave.releaseOn > today;\n if (waiting && !now) {\n scheduled += 1;\n continue;\n }\n // A page indexed now is judged now, even when a wave's pattern also covers it; a wave naming a page the plan's\n // pattern indexes now is a contradiction (the same path in both lists is reported with the plan's problems).\n if (waiting && wave.routes.includes(page.path) && !plan?.indexedNow.includes(page.path)) {\n failures.push(fail(\"LOC-INDEX\", page.path, `${page.path} is indexed now in the estate plan, but the wave of ${wave.releaseOn} still lists it; a page indexed now never waits for a wave.`));\n }\n const dated = waiting ? \"it is indexed now in the estate plan\" : wave ? `its wave was due on ${wave.releaseOn}` : \"no upcoming wave in the recorded schedule dates it\";\n const problems: string[] = [];\n if (!page.indexable) problems.push(\"is marked noindex\");\n if (estate.sitemap && !estate.sitemap.has(page.path)) problems.push(\"is not in the sitemap\");\n if (problems.length > 0) {\n failures.push(fail(\"LOC-INDEX\", page.path, `${page.path} is a location page that ${problems.join(\" and \")}, and ${dated}; every location page is indexable and listed.`));\n } else live += 1;\n }\n\n for (const wave of waves) {\n if (wave.releaseOn > today) continue;\n for (const route of wave.routes) {\n const matched = route.includes(\"*\") ? estate.pages.some((page) => matchesWaveRoute(page.path, route)) : estate.byPath.has(route);\n if (!matched) {\n failures.push(fail(\"LOC-INDEX\", route, `The wave of ${wave.releaseOn} released ${route}, but the build has no ${route.includes(\"*\") ? \"page matching it\" : \"such page\"}.`));\n }\n }\n }\n // A return wave brings back pages that are live now, so its pages are in the build before the day.\n for (const wave of waves) {\n if (wave.kind !== \"return\" || wave.releaseOn <= today) continue;\n for (const route of wave.routes) {\n const matched = route.includes(\"*\") ? estate.pages.some((page) => matchesWaveRoute(page.path, route)) : estate.byPath.has(route);\n if (!matched) failures.push(fail(\"LOC-INDEX\", route, `The return wave of ${wave.releaseOn} brings back ${route}, but the build has no ${route.includes(\"*\") ? \"page matching it\" : \"such page\"}; a return wave lists pages that are live now.`));\n }\n }\n if (plan) {\n for (const route of plan.indexedNow) {\n const matched = route.includes(\"*\") ? estate.pages.some((page) => matchesWaveRoute(page.path, route)) : estate.byPath.has(route);\n if (!matched) failures.push(fail(\"LOC-INDEX\", route, `estate.indexedNow lists ${route}, but the build has no ${route.includes(\"*\") ? \"page matching it\" : \"such page\"}.`));\n }\n const parent = (template: string | undefined) => template?.slice(0, template.lastIndexOf(\"/\"));\n const shared = plan.paths?.region !== undefined && parent(plan.paths.region) === parent(plan.paths.suburb);\n for (const page of shared ? pages : []) {\n if (page.place?.kind !== \"region\") continue;\n const slug = page.path.slice(page.path.lastIndexOf(\"/\") + 1);\n if (!context.geography?.suburbs.has(slug) || page.path !== `${parent(plan.paths?.region)}/${slug}`) continue;\n if (plan.slugs?.suburbs?.[slug] !== `${slug}-suburb`) {\n failures.push(fail(\"LOC-INDEX\", page.path, `The region page ${page.path} takes the slug of the suburb ${slug}; that suburb's pages take \"${slug}-suburb\" (estate.slugs.suburbs).`));\n }\n }\n }\n const upcoming = scheduled > 0 ? `; ${plural(scheduled, \"more page\")} ${scheduled === 1 ? \"is\" : \"are\"} dated by upcoming waves` : \"\";\n return outcome(\"LOC-INDEX\", failures, `All ${plural(live, \"released location page\")} are indexable and in the sitemap${upcoming}.`);\n}\n"]}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type SiteEstatePlan } from "@brandlab-digital/site-core/strategy";
|
|
1
2
|
export declare const FULL_STRATEGY_FILE = "src/data/strategy.ts";
|
|
2
3
|
/** As site-core's SITE_WAVE_MIN_GAP_DAYS and SITE_WAVE_MAX_GAP_DAYS. */
|
|
3
4
|
export declare const WAVE_MIN_GAP_DAYS = 14;
|
|
@@ -13,7 +14,10 @@ export interface FullStrategy {
|
|
|
13
14
|
readonly waves: readonly {
|
|
14
15
|
readonly releaseOn: string;
|
|
15
16
|
readonly routes: readonly string[];
|
|
17
|
+
readonly kind?: "release" | "return";
|
|
16
18
|
}[];
|
|
19
|
+
/** An existing estate's plan (site-core 3.14.0 `estate`): pages indexed now, family paths and public slugs; null when none. */
|
|
20
|
+
readonly estate: SiteEstatePlan | null;
|
|
17
21
|
/** The trade `defineSectionOrder` names, if any. */
|
|
18
22
|
readonly trade: string | undefined;
|
|
19
23
|
/** Recorded alternative orders by family. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"strategy.d.ts","sourceRoot":"","sources":["../../../../src/site-system/rules/full/strategy.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"strategy.d.ts","sourceRoot":"","sources":["../../../../src/site-system/rules/full/strategy.ts"],"names":[],"mappings":"AAQA,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,sCAAsC,CAAC;AAG/F,eAAO,MAAM,kBAAkB,yBAAyB,CAAC;AAEzD,wEAAwE;AACxE,eAAO,MAAM,iBAAiB,KAAK,CAAC;AACpC,eAAO,MAAM,iBAAiB,KAAK,CAAC;AAEpC,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,OAAO,EAAE;QAAE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,MAAM,EAAE,SAAS;YAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;YAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;SAAE,EAAE,CAAA;KAAE,GAAG,IAAI,CAAC;IACzI,QAAQ,CAAC,KAAK,EAAE,SAAS;QAAE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;QAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAA;KAAE,EAAE,CAAC;IACpI,+HAA+H;IAC/H,QAAQ,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI,CAAC;IACvC,oDAAoD;IACpD,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,6CAA6C;IAC7C,QAAQ,CAAC,cAAc,EAAE,WAAW,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAAC;IAChE,sGAAsG;IACtG,QAAQ,CAAC,QAAQ,EAAE;QAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC;QAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC;QAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IACvG,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;CACzB;AAED,yHAAyH;AACzH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAStD;AAED,gHAAgH;AAChH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAOrE;AAKD,kGAAkG;AAClG,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,YAAY,CAkFjF;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,SAAS;IAAE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAiClJ"}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { existsSync, readFileSync } from "node:fs";
|
|
8
8
|
import { join } from "node:path";
|
|
9
|
+
import { estatePlanProblems } from "@brandlab-digital/site-core/strategy";
|
|
9
10
|
import { LiteralReader, isFullyResolved, isLiteralObject } from "../enquiry/literal.js";
|
|
10
11
|
export const FULL_STRATEGY_FILE = "src/data/strategy.ts";
|
|
11
12
|
/** As site-core's SITE_WAVE_MIN_GAP_DAYS and SITE_WAVE_MAX_GAP_DAYS. */
|
|
@@ -39,7 +40,7 @@ export function readFullStrategy(projectRoot, today) {
|
|
|
39
40
|
const empty = { phrases: [], waves: [], orders: [] };
|
|
40
41
|
const file = join(projectRoot, FULL_STRATEGY_FILE);
|
|
41
42
|
if (!existsSync(file)) {
|
|
42
|
-
return { phrases: null, waves: [], trade: undefined, recordedOrders: new Map(), problems: empty, found: false };
|
|
43
|
+
return { phrases: null, waves: [], estate: null, trade: undefined, recordedOrders: new Map(), problems: empty, found: false };
|
|
43
44
|
}
|
|
44
45
|
const reader = new LiteralReader(readFileSync(file, "utf8"), FULL_STRATEGY_FILE);
|
|
45
46
|
const problems = { phrases: [], waves: [], orders: [] };
|
|
@@ -71,11 +72,26 @@ export function readFullStrategy(projectRoot, today) {
|
|
|
71
72
|
problems.waves.push(`Wave ${index + 1} in ${FULL_STRATEGY_FILE} needs a releaseOn date and a list of routes.`);
|
|
72
73
|
continue;
|
|
73
74
|
}
|
|
74
|
-
|
|
75
|
+
const kind = wave["kind"];
|
|
76
|
+
if (kind !== undefined && kind !== "release" && kind !== "return") {
|
|
77
|
+
problems.waves.push(`Wave ${index + 1} in ${FULL_STRATEGY_FILE} has the kind "${String(kind)}"; a wave is a release or a return.`);
|
|
78
|
+
}
|
|
79
|
+
waves.push({ releaseOn: wave["releaseOn"], routes: wave["routes"], ...(kind === "return" || kind === "release" ? { kind } : {}) });
|
|
75
80
|
}
|
|
76
81
|
problems.waves.push(...waveScheduleProblems(waves, today));
|
|
77
82
|
}
|
|
78
83
|
}
|
|
84
|
+
let estate = null;
|
|
85
|
+
const rawEstate = record?.["estate"];
|
|
86
|
+
if (rawEstate !== undefined) {
|
|
87
|
+
if (!isLiteralObject(rawEstate) || !isFullyResolved(rawEstate) || !Array.isArray(rawEstate["indexedNow"])) {
|
|
88
|
+
problems.waves.push(`The estate plan in ${FULL_STRATEGY_FILE} is not plain data with an indexedNow list, so the pages indexed now cannot be read.`);
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
estate = rawEstate;
|
|
92
|
+
problems.waves.push(...estatePlanProblems(estate, waves, today).map((problem) => `${FULL_STRATEGY_FILE}: ${problem}`));
|
|
93
|
+
}
|
|
94
|
+
}
|
|
79
95
|
if (argument && !record) {
|
|
80
96
|
problems.phrases.push(`${FULL_STRATEGY_FILE} is not plain data, so phrase ownership cannot be read.`);
|
|
81
97
|
problems.waves.push(`${FULL_STRATEGY_FILE} is not plain data, so the release schedule cannot be read.`);
|
|
@@ -101,7 +117,7 @@ export function readFullStrategy(projectRoot, today) {
|
|
|
101
117
|
}
|
|
102
118
|
}
|
|
103
119
|
}
|
|
104
|
-
return { phrases, waves, trade, recordedOrders, problems, found: true };
|
|
120
|
+
return { phrases, waves, estate, trade, recordedOrders, problems, found: true };
|
|
105
121
|
}
|
|
106
122
|
/**
|
|
107
123
|
* The schedule's rules, as site-core's: valid dates, in order, 14 to 28 days apart, each route in one wave, and the next
|