@brandlab-digital/conformance 3.10.0 → 3.12.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/dist/current-release.js +5 -5
- package/dist/current-release.js.map +1 -1
- package/dist/ready-cli.d.ts.map +1 -1
- package/dist/ready-cli.js +1 -0
- package/dist/ready-cli.js.map +1 -1
- package/dist/site-system/checks.d.ts +1 -1
- package/dist/site-system/checks.js +1 -1
- package/dist/site-system/checks.js.map +1 -1
- package/dist/site-system/rule-modules.d.ts.map +1 -1
- package/dist/site-system/rule-modules.js +5 -0
- package/dist/site-system/rule-modules.js.map +1 -1
- package/dist/site-system/rules/full/common.d.ts +37 -0
- package/dist/site-system/rules/full/common.d.ts.map +1 -0
- package/dist/site-system/rules/full/common.js +51 -0
- package/dist/site-system/rules/full/common.js.map +1 -0
- package/dist/site-system/rules/full/e10-phrases.d.ts +14 -0
- package/dist/site-system/rules/full/e10-phrases.d.ts.map +1 -0
- package/dist/site-system/rules/full/e10-phrases.js +69 -0
- package/dist/site-system/rules/full/e10-phrases.js.map +1 -0
- package/dist/site-system/rules/full/estate.d.ts +118 -0
- package/dist/site-system/rules/full/estate.d.ts.map +1 -0
- package/dist/site-system/rules/full/estate.js +447 -0
- package/dist/site-system/rules/full/estate.js.map +1 -0
- package/dist/site-system/rules/full/geography.d.ts +60 -0
- package/dist/site-system/rules/full/geography.d.ts.map +1 -0
- package/dist/site-system/rules/full/geography.js +111 -0
- package/dist/site-system/rules/full/geography.js.map +1 -0
- package/dist/site-system/rules/full/index.d.ts +33 -0
- package/dist/site-system/rules/full/index.d.ts.map +1 -0
- package/dist/site-system/rules/full/index.js +126 -0
- package/dist/site-system/rules/full/index.js.map +1 -0
- package/dist/site-system/rules/full/k12-manifests.d.ts +19 -0
- package/dist/site-system/rules/full/k12-manifests.d.ts.map +1 -0
- package/dist/site-system/rules/full/k12-manifests.js +130 -0
- package/dist/site-system/rules/full/k12-manifests.js.map +1 -0
- package/dist/site-system/rules/full/l10-location-copy.d.ts +15 -0
- package/dist/site-system/rules/full/l10-location-copy.d.ts.map +1 -0
- package/dist/site-system/rules/full/l10-location-copy.js +59 -0
- package/dist/site-system/rules/full/l10-location-copy.js.map +1 -0
- package/dist/site-system/rules/full/l3-neighbours.d.ts +16 -0
- package/dist/site-system/rules/full/l3-neighbours.d.ts.map +1 -0
- package/dist/site-system/rules/full/l3-neighbours.js +78 -0
- package/dist/site-system/rules/full/l3-neighbours.js.map +1 -0
- package/dist/site-system/rules/full/l4-local-copy.d.ts +27 -0
- package/dist/site-system/rules/full/l4-local-copy.d.ts.map +1 -0
- package/dist/site-system/rules/full/l4-local-copy.js +150 -0
- package/dist/site-system/rules/full/l4-local-copy.js.map +1 -0
- package/dist/site-system/rules/full/l9-data-contract.d.ts +15 -0
- package/dist/site-system/rules/full/l9-data-contract.d.ts.map +1 -0
- package/dist/site-system/rules/full/l9-data-contract.js +65 -0
- package/dist/site-system/rules/full/l9-data-contract.js.map +1 -0
- package/dist/site-system/rules/full/links.d.ts +23 -0
- package/dist/site-system/rules/full/links.d.ts.map +1 -0
- package/dist/site-system/rules/full/links.js +131 -0
- package/dist/site-system/rules/full/links.js.map +1 -0
- package/dist/site-system/rules/full/loc-index.d.ts +16 -0
- package/dist/site-system/rules/full/loc-index.d.ts.map +1 -0
- package/dist/site-system/rules/full/loc-index.js +59 -0
- package/dist/site-system/rules/full/loc-index.js.map +1 -0
- package/dist/site-system/rules/full/s4-route-files.d.ts +4 -0
- package/dist/site-system/rules/full/s4-route-files.d.ts.map +1 -0
- package/dist/site-system/rules/full/s4-route-files.js +125 -0
- package/dist/site-system/rules/full/s4-route-files.js.map +1 -0
- package/dist/site-system/rules/full/strategy.d.ts +43 -0
- package/dist/site-system/rules/full/strategy.d.ts.map +1 -0
- package/dist/site-system/rules/full/strategy.js +147 -0
- package/dist/site-system/rules/full/strategy.js.map +1 -0
- package/dist/site-system/runners.d.ts +2 -0
- package/dist/site-system/runners.d.ts.map +1 -1
- package/dist/site-system/runners.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"estate.js","sourceRoot":"","sources":["../../../../src/site-system/rules/full/estate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAChE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAE9D,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAClF,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,kBAAkB,EAAE,aAAa,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,EAAmC,MAAM,oBAAoB,CAAC;AAC7J,OAAO,EAAE,WAAW,EAAsC,MAAM,gBAAgB,CAAC;AAsBjF,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,QAAQ,EAAE,mBAAmB,EAAE,QAAQ,CAAU,CAAC;AAGpF,MAAM,UAAU,gBAAgB,CAAC,MAA0B;IACzD,OAAQ,iBAAuC,CAAC,QAAQ,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC;AACzE,CAAC;AAqDD,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,CAAC,aAAa,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;AAEnF,gGAAgG;AAChG,MAAM,UAAU,UAAU,CAAC,KAM1B;IACC,MAAM,OAAO,GAAqD,EAAE,CAAC;IACrE,MAAM,QAAQ,GAAG,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,yBAAyB,CAAC,CAAC,CAAC;IAC3E,MAAM,OAAO,GAAG,IAAI,GAAG,EAAmG,CAAC;IAC3H,MAAM,aAAa,GAAG,IAAI,GAAG,EAAkB,CAAC;IAChD,IAAI,QAAQ,GAAG,KAAK,CAAC;IAErB,IAAI,QAAQ,CAAC,QAAQ,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QACpD,QAAQ,GAAG,IAAI,CAAC;QAChB,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC;QACrD,KAAK,MAAM,KAAK,IAAI,QAAQ,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,EAAE;YAAE,aAAa,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;QAC7H,KAAK,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YAC/D,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC;gBAAE,SAAS;YAC3D,MAAM,QAAQ,GAAG,OAAO,KAAK,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;YACjF,IAAI,QAAQ,IAAI,aAAa,CAAC,GAAG,CAAC,QAAQ,CAAC;gBAAE,aAAa,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,aAAa,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YACjH,IAAI,KAAK,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,KAAK,MAAM,CAAC,CAAC,CAAC,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC;gBAAE,SAAS;YACzG,MAAM,IAAI,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;YACpC,IAAI,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,eAAe,CAAC,GAAG,CAAC,QAAQ,IAAI,EAAE,CAAC;gBAAE,SAAS;YAC/E,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,EAAE,GAAG,IAAI,KAAK,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC;YACpE,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;gBACtB,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;gBACjC,SAAS;YACX,CAAC;YACD,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,IAAI,KAAK,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC;YAC9E,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE;gBAChB,IAAI,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC;gBACtC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACjC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aAC/E,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IACD,uFAAuF;IACvF,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;QAC/B,MAAM,IAAI,GAAG,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACtC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IACjE,CAAC;IAED,MAAM,YAAY,GAAG,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACzF,MAAM,SAAS,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC5C,KAAK,MAAM,IAAI,IAAI,YAAY;QAAE,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK;YAAE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC;gBAAE,SAAS,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;IAEzH,+FAA+F;IAC/F,MAAM,KAAK,GAAG,IAAI,GAAG,CAAS,CAAC,WAAW,CAAC,CAAC,CAAC;IAC7C,MAAM,IAAI,GAA8G,EAAE,CAAC;IAC3H,KAAK,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACrC,MAAM,IAAI,GAAG,mBAAmB,CAAC,OAAO,MAAM,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;QAChG,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,CAAC,6CAA6C,CAAC,EAAE,CAAC;YACjF,MAAM,IAAI,GAAG,0BAA0B,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;YAC5D,IAAI,CAAC;gBACH,IAAI,IAAI,EAAE,CAAC;oBACT,kFAAkF;oBAClF,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC;oBAChC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;oBAChB,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC;gBACrE,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,sCAAsC;YACxC,CAAC;QACH,CAAC;QACD,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,SAAS,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;QACpM,IAAI,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,iBAAiB,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;IACzE,CAAC;IACD,MAAM,KAAK,GAAiB,EAAE,CAAC;IAC/B,KAAK,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,IAAI,EAAE,CAAC;QAC9C,MAAM,KAAK,GAAG,IAAI,GAAG,EAAU,CAAC;QAChC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,IAAI,GAAG,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;YACrD,IAAI,IAAI,IAAI,IAAI,KAAK,OAAO,CAAC,IAAI;gBAAE,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACrD,CAAC;QACD,KAAK,CAAC,IAAI,CAAC;YACT,GAAG,OAAO;YACV,KAAK,EAAE,CAAC,GAAG,KAAK,CAAC;YACjB,UAAU,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;SAC9I,CAAC,CAAC;IACL,CAAC;IACD,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;IAC/D,aAAa,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC;IACxC,oEAAoE;IACpE,KAAK,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC;QAC5C,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,IAAI;YAAE,SAAS;QAC1D,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAE,CAAC,MAAM,CAAC;QACnC,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC,SAAS,CAAC,mBAAmB,CAAC,OAAO,MAAM,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IAC1H,CAAC;IACD,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,gBAAgB,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,QAAQ,IAAI,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAC/J,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,gBAAgB,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IAC/H,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QACvC,MAAM,IAAI,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,SAAS,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;QACzF,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,CAAC,IAAI,CAAC,QAAQ,KAAK,SAAS,IAAI,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,IAAI,KAAK,SAAS,IAAI,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;IAC9J,CAAC,CAAC,CAAC;IACH,OAAO;QACL,KAAK;QACL,MAAM;QACN,QAAQ;QACR,OAAO,EAAE,QAAQ,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI;QAC/E,aAAa;QACb,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,WAAW;KACZ,CAAC;AACJ,CAAC;AAED,SAAS,SAAS,CAChB,IAA0F,EAC1F,SAAsC,EACtC,SAA+B;IAE/B,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAClC,MAAM,QAAQ,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;IACnC,MAAM,MAAM,GAAG,QAAQ;SACpB,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,KAAK,MAAM,IAAI,uBAAuB,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;SACxG,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,IAAI,EAAE,CAAC;SAChD,IAAI,CAAC,GAAG,CAAC,CAAC;IACb,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC,cAAc,CAAC,CAAC;IAC9C,MAAM,SAAS,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAE5F,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;IAC9G,MAAM,SAAS,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,KAAK,IAAI,CAAC,CAAC;IACnE,MAAM,KAAK,GAAG,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACxE,MAAM,IAAI,GAAe;QACvB,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,SAAS;QACT,SAAS,EAAE,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC;QACnC,WAAW,EAAE,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC;QACrC,KAAK,EAAE,EAAE;QACT,UAAU,EAAE,EAAE;QACd,QAAQ,EAAE,YAAY,CAAC,QAAQ,CAAC;QAChC,QAAQ,EAAE,gBAAgB,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC;QAC/C,EAAE,EAAE,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,EAAE;QACtC,KAAK;QACL,MAAM,EAAE,SAAS;QACjB,YAAY,EAAE,SAAS;QACvB,IAAI,EAAE,SAAS;KAChB,CAAC;IACF,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;QACjC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC;QAC5C,IAAI,CAAC,YAAY,GAAG,UAAU,CAAC;QAC/B,IAAI,gBAAgB,CAAC,IAAI,CAAC,MAAM,CAAC;YAAE,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IACpE,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;AAClC,CAAC;AAED,SAAS,YAAY,CAAC,QAAgC;IACpD,MAAM,OAAO,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,KAAK,QAAQ,IAAI,IAAI,CAAC,OAAO,EAAE,kBAAkB,CAAC,KAAK,SAAS,CAAC,CAAC;IAC1H,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAClD,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC;IACvE,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,CAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACrG,IAAI,KAAc,CAAC;IACnB,IAAI,CAAC;QACH,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC3B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,mBAAmB,EAAE,CAAC;IAC1D,CAAC;IACD,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,OAAO,KAAK,CAAC,MAAM,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;QAC7J,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,sDAAsD,EAAE,CAAC;IAC7F,CAAC;IACD,MAAM,OAAO,GAAG,CAAC,IAAe,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,OAAO,KAAK,CAAC,EAAE,KAAK,QAAQ,IAAI,OAAO,KAAK,CAAC,WAAW,KAAK,QAAQ,CAAC,CAAC;IACrJ,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;QACnF,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,2CAA2C,EAAE,CAAC;IAClF,CAAC;IACD,OAAO;QACL,IAAI,EAAE,KAAK;QACX,QAAQ,EAAE;YACR,OAAO,EAAE,OAAO,KAAK,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;YAC9D,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,GAAG,CAAC,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC/D,GAAG,EAAE,OAAO,KAAK,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,iBAAiB;YACzE,KAAK,EAAE,KAAK,CAAC,KAAK,KAAK,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS;YAC1D,OAAO,EAAE,KAAK,CAAC,OAAkC;YACjD,QAAQ,EAAG,KAAK,CAAC,QAAqC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;gBACrE,EAAE,EAAE,KAAK,CAAC,EAAG;gBACb,OAAO,EAAE,OAAO,KAAK,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,EAAG;gBACtE,WAAW,EAAE,KAAK,CAAC,WAAY;aAChC,CAAC,CAAC;YACH,OAAO,EAAG,KAAK,CAAC,OAAoC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;gBACnE,EAAE,EAAE,KAAK,CAAC,EAAG;gBACb,OAAO,EAAE,OAAO,KAAK,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,EAAG;gBACtE,WAAW,EAAE,KAAK,CAAC,WAAY;gBAC/B,MAAM,EAAE,OAAO,KAAK,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE;aAC7D,CAAC,CAAC;YACH,OAAO,EAAE,OAAO,KAAK,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;YAC9D,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAkB,EAAE,CAAC,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE;SAChH;KACF,CAAC;AACJ,CAAC;AAED,MAAM,WAAW,GAAG,gHAAgH,CAAC;AAErI,mFAAmF;AACnF,SAAS,WAAW,CAAC,KAAa;IAChC,OAAO,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;AACpF,CAAC;AAED;;;GAGG;AACH,SAAS,aAAa,CAAC,KAA4B,EAAE,KAAuC;IAC1F,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS,IAAI,CAAC,IAAI,CAAC,WAAW;YAAE,SAAS;QAC7D,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC;QAC9B,IAAI,qBAAqB,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,KAAK,EAAE,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,mBAAmB,CAAC;aAC9G,IAAI,4BAA4B,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,KAAK,EAAE,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC;QACpH,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS;YAAE,IAAI,CAAC,YAAY,GAAG,SAAS,CAAC;IAC/D,CAAC;IACD,+GAA+G;IAC/G,MAAM,QAAQ,GAAG,CAAC,IAAgB,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,KAAK,EAAE,IAAI,IAAI,EAAE,EAAE,CAAC;IACpF,MAAM,MAAM,GAAG,IAAI,GAAG,EAA+B,CAAC;IACtD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,IAAI,CAAC,YAAY,KAAK,UAAU,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC;YAAE,SAAS;QAChG,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,IAAI,GAAG,EAAkB,CAAC;QACzE,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,MAAO,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,MAAO,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAClE,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC,CAAC;IACvC,CAAC;IACD,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,QAAQ,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,GAAG,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3I,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS;YAAE,SAAS;QACxC,MAAM,MAAM,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,aAAa,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACnG,IAAI,MAAM,EAAE,CAAC;YACX,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;YACrB,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC;QAC9B,CAAC;IACH,CAAC;IACD,qEAAqE;IACrE,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC;YAAE,SAAS;QACzF,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,YAAY,KAAK,OAAO,IAAI,KAAK,CAAC,QAAQ,KAAK,IAAI,CAAC,QAAQ,CAAC,CAAC;QACzG,IAAI,MAAM,EAAE,CAAC;YACX,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;YAC5B,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC;QAC9B,CAAC;IACH,CAAC;IACD,MAAM,KAAK,GAAG,IAAI,GAAG,EAAkB,CAAC;IACxC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,MAAM,KAAK,QAAQ,IAAI,IAAI,CAAC,MAAM,KAAK,mBAAmB,CAAC;YAAE,SAAS;QAC/F,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;YAAE,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;IAC3E,CAAC;IACD,iHAAiH;IACjH,mFAAmF;IACnF,MAAM,OAAO,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC1C,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS,IAAI,IAAI,CAAC,KAAK,EAAE,IAAI,KAAK,QAAQ;YAAE,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IACxI,CAAC;IACD,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,OAAO,EAAE,CAAC;QACpC,IAAI,KAAK,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC;IAC/G,CAAC;IACD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS;YAAE,SAAS;QACxC,IAAI,IAAI,CAAC,IAAI,KAAK,GAAG,EAAE,CAAC;YACtB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;YACrB,IAAI,CAAC,YAAY,GAAG,MAAM,CAAC;YAC3B,SAAS;QACX,CAAC;QACD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QACnE,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK;YAAE,SAAS;QACrC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC;QAC/D,IAAI,CAAC,YAAY,GAAG,MAAM,CAAC;IAC7B,CAAC;AACH,CAAC;AAED,mGAAmG;AACnG,SAAS,WAAW,CAAC,IAAiB;IACpC,OAAO,WAAW,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,KAAK,MAAM,CAAC,IAAI,WAAW,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC;AAC5I,CAAC;AAED,MAAM,MAAM,GAAG,CAAC,OAAoB,EAAE,EAAE,CACtC,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC;AAErJ,SAAS,kBAAkB,CAAC,OAAoB;IAC9C,+EAA+E;IAC/E,MAAM,KAAK,GAAG,CAAC,IAAiB,EAAe,EAAE,CAAC,CAAC;QACjD,GAAG,IAAI;QACP,QAAQ,EAAE,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,KAAK,EAAc,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;KACrI,CAAC,CAAC;IACH,OAAO,aAAa,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAChE,CAAC;AAED,SAAS,YAAY,CAAC,IAAiB;IACrC,MAAM,QAAQ,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;IACnC,MAAM,QAAQ,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;IACxC,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,GAAG,CAAC;QAC/B,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,SAAS,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QACzH,CAAC,CAAC,kBAAkB,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IACrF,MAAM,SAAS,GAAG,CAAC,EAAU,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,iBAAiB,CAAC,KAAK,EAAE,CAAC,CAAC;IAEtG,uFAAuF;IACvF,MAAM,UAAU,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;IACrC,MAAM,QAAQ,GAAG,UAAU,IAAI,WAAW,CAAC,IAAI,CAAC,CAAC;IACjD,MAAM,IAAI,GAA2C,EAAE,CAAC;IACxD,KAAK,MAAM,OAAO,IAAI,WAAW,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,KAAK,SAAS,CAAC,EAAE,CAAC;QAC3F,MAAM,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,KAAK,EAAwB,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,SAAS,IAAI,KAAK,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC;QAC5H,IAAI,CAAC,OAAO;YAAE,SAAS;QACvB,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;QACjC,MAAM,MAAM,GAAG,aAAa,CAAC,MAAM,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,KAAK,OAAO,CAAC,CAAC,CAAC;QACtF,IAAI,QAAQ,IAAI,CAAC,UAAU,IAAI,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;YAAE,IAAI,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;IAC1F,CAAC;IACD,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtB,KAAK,MAAM,IAAI,IAAI,WAAW,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,KAAK,IAAI,CAAC,EAAE,CAAC;YACnF,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,KAAK,EAAwB,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,SAAS,CAAC,IAAI,EAAE,CAAC;YAC/G,MAAM,UAAU,GAAG,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;YACxD,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;YAC9B,IAAI,UAAU,EAAE,GAAG,KAAK,IAAI,IAAI,CAAC,UAAU,IAAI,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;gBAAE,IAAI,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,UAAU,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC;QACpI,CAAC;IACH,CAAC;IAED,MAAM,WAAW,GAAG,SAAS,CAAC,aAAa,CAAC,CAAC;IAC7C,IAAI,IAAI,GAAoB,IAAI,CAAC;IACjC,IAAI,WAAW,EAAE,CAAC;QAChB,MAAM,KAAK,GAAG,WAAW,CAAC,WAAW,CAAC,CAAC,MAAM,CAC3C,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,KAAK,IAAI,IAAI,OAAO,CAAC,GAAG,KAAK,SAAS,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,KAAK,IAAI,IAAI,KAAK,CAAC,GAAG,KAAK,SAAS,CAAC,CACzJ,CAAC;QACF,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QACvI,IAAI,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC;YACrB,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC;YACzD,CAAC,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;IAChG,CAAC;IAED,MAAM,aAAa,GAAG,SAAS,CAAC,aAAa,CAAC,CAAC;IAC/C,MAAM,cAAc,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC;IAC5C,MAAM,OAAO,GAAG,cAAc;QAC5B,CAAC,CAAC;YACE,OAAO,EAAE,WAAW,CAAC,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,KAAK,YAAY,CAAC,CAAC,MAAM;YAC7F,UAAU,EAAE,WAAW,CAAC,cAAc,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,KAAK,GAAG,IAAI,mCAAmC,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;YACvJ,MAAM,EACJ,WAAW,CAAC,cAAc,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,KAAK,aAAa,CAAC;gBACzF,yDAAyD,CAAC,IAAI,CAAC,MAAM,CAAC,cAAc,EAAE,IAAI,CAAC,CAAC;SAC/F;QACH,CAAC,CAAC,IAAI,CAAC;IAET,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,KAAK,MAAM,IAAI,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC,CAAC;IACnG,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,KAAK,YAAY,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC;IAC5F,IAAI,SAAS,GAA8B,IAAI,CAAC;IAChD,IAAI,IAAI,EAAE,CAAC;QACT,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC,IAAI,CAClC,CAAC,OAAO,EAAE,EAAE,CACV,OAAO,CAAC,GAAG,KAAK,OAAO;YACvB,CAAC,IAAI,CAAC,OAAO,EAAE,cAAc,CAAC,KAAK,gBAAgB,IAAI,kBAAkB,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,IAAI,EAAE,IAAI,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CACjJ,CAAC;QACF,SAAS,GAAG,EAAE,WAAW,EAAE,KAAK,KAAK,SAAS,EAAE,WAAW,EAAE,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;IAC1H,CAAC;IAED,OAAO;QACL,KAAK;QACL,IAAI;QACJ,IAAI;QACJ,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI;QAC1D,OAAO;QACP,SAAS;QACT,OAAO,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,KAAK,GAAG,IAAI,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;QACtG,OAAO,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,KAAK,SAAS,CAAC;KAC5E,CAAC;AACJ,CAAC;AAED,mHAAmH;AACnH,MAAM,UAAU,YAAY,CAAC,IAAY,EAAE,IAAY,EAAE,KAA0B;IACjF,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;IAC1B,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,uCAAuC,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACxG,IAAI,GAAQ,CAAC;IACb,IAAI,CAAC;QACH,GAAG,GAAG,IAAI,GAAG,CAAC,KAAK,EAAE,mBAAmB,IAAI,EAAE,CAAC,CAAC;IAClD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAC;IACrG,IAAI,IAAY,CAAC;IACjB,IAAI,CAAC;QACH,IAAI,GAAG,kBAAkB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC1C,CAAC;IAAC,MAAM,CAAC;QACP,IAAI,GAAG,GAAG,CAAC,QAAQ,CAAC;IACtB,CAAC;IACD,OAAO,aAAa,CAAC,IAAI,CAAC,CAAC;AAC7B,CAAC;AAOD,yGAAyG;AACzG,SAAS,YAAY,CAAC,MAAc;IAClC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC;QAAE,OAAO,EAAE,CAAC;IACnC,MAAM,KAAK,GAAkB,EAAE,CAAC;IAChC,MAAM,KAAK,GAAG,CAAC,SAAiB,EAAE,EAAE;QAClC,KAAK,MAAM,KAAK,IAAI,WAAW,CAAC,SAAS,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;YACpE,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YACzC,IAAI,KAAK,CAAC,WAAW,EAAE;gBAAE,KAAK,CAAC,IAAI,CAAC,CAAC;iBAChC,IAAI,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;gBAC1C,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;gBACvC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC;oBAAE,SAAS;gBACtC,MAAM,KAAK,GAAa,EAAE,CAAC;gBAC3B,KAAK,MAAM,KAAK,IAAI,GAAG,CAAC,QAAQ,CAAC,+BAA+B,CAAC,EAAE,CAAC;oBAClE,IAAI,CAAC;wBACH,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;oBAChF,CAAC;oBAAC,MAAM,CAAC;wBACP,yCAAyC;oBAC3C,CAAC;gBACH,CAAC;gBACD,MAAM,IAAI,GAAG,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAG,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;gBACvG,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;YAC9B,CAAC;QACH,CAAC;IACH,CAAC,CAAC;IACF,KAAK,CAAC,MAAM,CAAC,CAAC;IACd,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,QAAQ,CAAC,IAAY;IAC5B,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,SAAS,CAAC;IACxC,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;IAChD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,SAAS,QAAQ,CAAC,KAAc;IAC9B,OAAO,OAAO,CAAC,KAAK,CAAC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC9E,CAAC","sourcesContent":["/**\n * The estate the full tier reads: every page of the production build (the route catalogue as Next built it, from\n * `.next/prerender-manifest.json` and each page's prerendered HTML), the sitemap files in the build, and per page its\n * indexing, internal links, section manifest and the copy the location rules compare. Nothing is crawled: a site\n * checked with only a base URL and no build has just its sampled pages, and the estate-wide checks say so.\n *\n * A page's family is the one its section manifest names. A page without a manifest takes its family from the\n * sitemap file that lists it (`service-locations-*.xml`, `locations-*.xml`) or from the other pages of its stem (the\n * path with its place replaced: `/services/decking/{place}`), so a location page missing its manifest is still\n * found; a page built from the same dynamic route as a page with a manifest takes that page's family; the\n * representative service route is a service page; home is `/`. Other pages without a manifest have no known family.\n */\nimport { existsSync, readdirSync, readFileSync } from \"node:fs\";\nimport { join, relative } from \"node:path\";\nimport { extractLinkGroups } from \"../../../page-standard.js\";\nimport type { SiteSystemPage } from \"../../samples.js\";\nimport { resolveStreamedHtml } from \"../../streamed-html.js\";\nimport { normalisePath, sectionId, topLevelSections } from \"../enquiry/common.js\";\nimport { attr, descendants, isHiddenFromPeople, normaliseText, parseHtml, readableLines, textOf, type HtmlElement, type HtmlNode } from \"../enquiry/html.js\";\nimport { placeOfPath, type FullGeography, type PathPlace } from \"./geography.js\";\n\n/** The section manifest as SectionSequence writes it (site-core `BrandlabSectionManifest`). */\nexport interface ManifestData {\n readonly version: number;\n readonly family: string;\n readonly page?: string;\n readonly cap: number;\n readonly order: \"default\" | \"recorded\";\n readonly planned: readonly { readonly id: string; readonly requirement: string }[];\n readonly rendered: readonly { readonly id: string; readonly section: string; readonly requirement: string; readonly tone?: string }[];\n readonly skipped: readonly { readonly id: string; readonly section: string; readonly requirement: string; readonly reason: string }[];\n readonly counted: number;\n readonly flags: readonly string[];\n}\n\nexport type ManifestState =\n | { readonly kind: \"none\" }\n | { readonly kind: \"invalid\"; readonly reason: string }\n | { readonly kind: \"many\"; readonly count: number }\n | { readonly kind: \"one\"; readonly manifest: ManifestData };\n\nexport const LOCATION_FAMILIES = [\"suburb\", \"service-in-suburb\", \"region\"] as const;\nexport type LocationFamily = (typeof LOCATION_FAMILIES)[number];\n\nexport function isLocationFamily(family: string | undefined): family is LocationFamily {\n return (LOCATION_FAMILIES as readonly string[]).includes(family ?? \"\");\n}\n\n/** The copy and parts of a location page the L4, L9 and L10 rules read. */\nexport interface LocationCopy {\n /** Readable lines of the page content, with the section they sit in (\"page\" outside any section). */\n readonly lines: readonly { readonly section: string; readonly text: string }[];\n readonly faqs: readonly { readonly question: string; readonly answer: string }[];\n /** The job cards of the nearby-jobs section; null when the page has none. */\n readonly jobs: readonly string[] | null;\n /** The price-guide section's text; null when the page has none. */\n readonly prices: string | null;\n readonly reviews: { readonly written: number; readonly googleLink: boolean; readonly rating: boolean } | null;\n readonly quoteForm: { readonly suburbField: boolean; readonly suburbValue: string } | null;\n readonly telLink: boolean;\n readonly callBar: boolean;\n}\n\nexport interface EstatePage {\n readonly path: string;\n /** The app route Next built the page from (`/locations/[suburb]`), when known. */\n readonly srcRoute: string | undefined;\n readonly indexable: boolean;\n readonly inSitemap: boolean;\n /** The sitemap file that lists the page, without its number and extension (`service-locations`). */\n readonly sitemapFile: string | undefined;\n /** Internal paths the page links to, once each. */\n readonly links: readonly string[];\n /** The main content's link groups (lists), as internal paths, for the neighbour order. */\n readonly linkGroups: readonly (readonly string[])[];\n readonly manifest: ManifestState;\n /** Top-level `data-bl-section` ids, in document order. */\n readonly sections: readonly string[];\n readonly h1: string;\n readonly place: PathPlace | undefined;\n family: string | undefined;\n familySource: \"manifest\" | \"sitemap\" | \"route\" | \"stem\" | \"path\" | undefined;\n copy: LocationCopy | undefined;\n}\n\nexport interface Estate {\n readonly pages: readonly EstatePage[];\n readonly byPath: ReadonlyMap<string, EstatePage>;\n /** True when the pages are the whole production build, not just a sample. */\n readonly complete: boolean;\n /** Paths listed in the build's sitemap files; null when the build holds no sitemap. */\n readonly sitemap: ReadonlySet<string> | null;\n /** Dynamic app routes in the build and how many pages each produced. */\n readonly dynamicRoutes: ReadonlyMap<string, number>;\n readonly buildDir: string;\n /** Routes the prerender manifest lists with no HTML in the build; `location` when the route is a location page's. */\n readonly missingHtml: readonly { readonly path: string; readonly location: boolean }[];\n}\n\nconst INTERNAL_ROUTES = new Set([\"/_not-found\", \"/_global-error\", \"/404\", \"/500\"]);\n\n/** Reads the estate: the whole build when `buildDir` holds one, otherwise the sampled pages. */\nexport function readEstate(input: {\n readonly pages: readonly SiteSystemPage[];\n readonly buildDir: string;\n readonly geography: FullGeography | null;\n /** Families known from elsewhere, by path (the representative service route). */\n readonly seeds?: Readonly<Record<string, string>>;\n}): Estate {\n const missing: { path: string; srcRoute: string | undefined }[] = [];\n const manifest = readJson(join(input.buildDir, \"prerender-manifest.json\"));\n const sources = new Map<string, { html: string | (() => string); srcRoute?: string; headers?: Record<string, unknown> }>();\n const dynamicRoutes = new Map<string, number>();\n let complete = false;\n\n if (isRecord(manifest) && isRecord(manifest.routes)) {\n complete = true;\n const appDir = join(input.buildDir, \"server\", \"app\");\n for (const route of isRecord(manifest.dynamicRoutes) ? Object.keys(manifest.dynamicRoutes) : []) dynamicRoutes.set(route, 0);\n for (const [rawPath, entry] of Object.entries(manifest.routes)) {\n if (!isRecord(entry) || !rawPath.startsWith(\"/\")) continue;\n const srcRoute = typeof entry.srcRoute === \"string\" ? entry.srcRoute : undefined;\n if (srcRoute && dynamicRoutes.has(srcRoute)) dynamicRoutes.set(srcRoute, (dynamicRoutes.get(srcRoute) ?? 0) + 1);\n if (entry.routeType !== undefined ? entry.routeType !== \"page\" : /\\.[a-z0-9]+$/i.test(rawPath)) continue;\n const path = normalisePath(rawPath);\n if (INTERNAL_ROUTES.has(path) || INTERNAL_ROUTES.has(srcRoute ?? \"\")) continue;\n const file = join(appDir, `${path === \"/\" ? \"/index\" : path}.html`);\n if (!existsSync(file)) {\n missing.push({ path, srcRoute });\n continue;\n }\n const meta = readJson(join(appDir, `${path === \"/\" ? \"/index\" : path}.meta`));\n sources.set(path, {\n html: () => readFileSync(file, \"utf8\"),\n ...(srcRoute ? { srcRoute } : {}),\n ...(isRecord(meta) && isRecord(meta.headers) ? { headers: meta.headers } : {}),\n });\n }\n }\n // Sampled pages fill in what the build does not have (a base-URL run without a build).\n for (const page of input.pages) {\n const path = normalisePath(page.path);\n if (!sources.has(path)) sources.set(path, { html: page.html });\n }\n\n const sitemapFiles = complete ? readSitemaps(join(input.buildDir, \"server\", \"app\")) : [];\n const sitemapOf = new Map<string, string>();\n for (const file of sitemapFiles) for (const path of file.paths) if (!sitemapOf.has(path)) sitemapOf.set(path, file.name);\n\n // First pass: one summary per page, links kept as written until every canonical host is known.\n const hosts = new Set<string>([\"localhost\"]);\n const read: { summary: EstatePage; hrefs: string[]; groups: string[][]; source: { html: string | (() => string) } }[] = [];\n for (const [path, source] of sources) {\n const html = resolveStreamedHtml(typeof source.html === \"string\" ? source.html : source.html());\n for (const match of html.matchAll(/<link\\b[^>]*\\brel=[\"']canonical[\"'][^>]*>/gi)) {\n const href = /\\bhref=[\"']([^\"']+)[\"']/i.exec(match[0])?.[1];\n try {\n if (href) {\n // A self-link may use the www or the bare host, whichever the canonical does not.\n const host = new URL(href).host;\n hosts.add(host);\n hosts.add(host.startsWith(\"www.\") ? host.slice(4) : `www.${host}`);\n }\n } catch {\n // A relative canonical names no host.\n }\n }\n const { summary, hrefs } = summarise({ path, html, ...(source.srcRoute ? { srcRoute: source.srcRoute } : {}), ...(source.headers ? { headers: source.headers } : {}) }, sitemapOf, input.geography);\n read.push({ summary, hrefs, groups: extractLinkGroups(html), source });\n }\n const pages: EstatePage[] = [];\n for (const { summary, hrefs, groups } of read) {\n const links = new Set<string>();\n for (const href of hrefs) {\n const path = internalPath(href, summary.path, hosts);\n if (path && path !== summary.path) links.add(path);\n }\n pages.push({\n ...summary,\n links: [...links],\n linkGroups: groups.map((group) => group.flatMap((href) => internalPath(href, summary.path, hosts) ?? [])).filter((group) => group.length > 0),\n });\n }\n const byPath = new Map(pages.map((page) => [page.path, page]));\n inferFamilies(pages, input.seeds ?? {});\n // Second pass: the copy of location pages found without a manifest.\n for (const [index, page] of pages.entries()) {\n if (!isLocationFamily(page.family) || page.copy) continue;\n const source = read[index]!.source;\n page.copy = locationCopy(parseHtml(resolveStreamedHtml(typeof source.html === \"string\" ? source.html : source.html())));\n }\n const locationRoutes = new Set(pages.flatMap((page) => (isLocationFamily(page.family) && page.srcRoute && isGenerated(page.srcRoute) ? [page.srcRoute] : [])));\n const locationStems = new Set(pages.flatMap((page) => (isLocationFamily(page.family) && page.place ? [page.place.stem] : [])));\n const missingHtml = missing.map((item) => {\n const stem = input.geography ? placeOfPath(item.path, input.geography)?.stem : undefined;\n return { path: item.path, location: (item.srcRoute !== undefined && locationRoutes.has(item.srcRoute)) || (stem !== undefined && locationStems.has(stem)) };\n });\n return {\n pages,\n byPath,\n complete,\n sitemap: complete && sitemapFiles.length > 0 ? new Set(sitemapOf.keys()) : null,\n dynamicRoutes,\n buildDir: input.buildDir,\n missingHtml,\n };\n}\n\nfunction summarise(\n item: { path: string; html: string; srcRoute?: string; headers?: Record<string, unknown> },\n sitemapOf: ReadonlyMap<string, string>,\n geography: FullGeography | null,\n): { summary: EstatePage; hrefs: string[] } {\n const root = parseHtml(item.html);\n const elements = descendants(root);\n const robots = elements\n .filter((element) => element.tag === \"meta\" && /^(robots|googlebot)$/i.test(attr(element, \"name\") ?? \"\"))\n .map((element) => attr(element, \"content\") ?? \"\")\n .join(\" \");\n const header = item.headers?.[\"x-robots-tag\"];\n const indexable = !/noindex/i.test(`${robots} ${typeof header === \"string\" ? header : \"\"}`);\n\n const hrefs = elements.filter((element) => element.tag === \"a\").map((element) => attr(element, \"href\") ?? \"\");\n const h1Element = elements.find((element) => element.tag === \"h1\");\n const place = geography ? placeOfPath(item.path, geography) : undefined;\n const page: EstatePage = {\n path: item.path,\n srcRoute: item.srcRoute,\n indexable,\n inSitemap: sitemapOf.has(item.path),\n sitemapFile: sitemapOf.get(item.path),\n links: [],\n linkGroups: [],\n manifest: readManifest(elements),\n sections: topLevelSections(root).map(sectionId),\n h1: h1Element ? textOf(h1Element) : \"\",\n place,\n family: undefined,\n familySource: undefined,\n copy: undefined,\n };\n if (page.manifest.kind === \"one\") {\n page.family = page.manifest.manifest.family;\n page.familySource = \"manifest\";\n if (isLocationFamily(page.family)) page.copy = locationCopy(root);\n }\n return { summary: page, hrefs };\n}\n\nfunction readManifest(elements: readonly HtmlElement[]): ManifestState {\n const scripts = elements.filter((element) => element.tag === \"script\" && attr(element, \"data-bl-manifest\") !== undefined);\n if (scripts.length === 0) return { kind: \"none\" };\n if (scripts.length > 1) return { kind: \"many\", count: scripts.length };\n const text = scripts[0]!.children.map((child) => (child.type === \"text\" ? child.text : \"\")).join(\"\");\n let value: unknown;\n try {\n value = JSON.parse(text);\n } catch {\n return { kind: \"invalid\", reason: \"is not valid JSON\" };\n }\n if (!isRecord(value) || typeof value.family !== \"string\" || !Array.isArray(value.planned) || !Array.isArray(value.rendered) || !Array.isArray(value.skipped)) {\n return { kind: \"invalid\", reason: \"lacks its family, planned, rendered or skipped lists\" };\n }\n const entries = (list: unknown[]) => list.every((entry) => isRecord(entry) && typeof entry.id === \"string\" && typeof entry.requirement === \"string\");\n if (!entries(value.planned) || !entries(value.rendered) || !entries(value.skipped)) {\n return { kind: \"invalid\", reason: \"has an entry without an id or requirement\" };\n }\n return {\n kind: \"one\",\n manifest: {\n version: typeof value.version === \"number\" ? value.version : 0,\n family: value.family,\n ...(typeof value.page === \"string\" ? { page: value.page } : {}),\n cap: typeof value.cap === \"number\" ? value.cap : Number.POSITIVE_INFINITY,\n order: value.order === \"recorded\" ? \"recorded\" : \"default\",\n planned: value.planned as ManifestData[\"planned\"],\n rendered: (value.rendered as Record<string, string>[]).map((entry) => ({\n id: entry.id!,\n section: typeof entry.section === \"string\" ? entry.section : entry.id!,\n requirement: entry.requirement!,\n })),\n skipped: (value.skipped as Record<string, string>[]).map((entry) => ({\n id: entry.id!,\n section: typeof entry.section === \"string\" ? entry.section : entry.id!,\n requirement: entry.requirement!,\n reason: typeof entry.reason === \"string\" ? entry.reason : \"\",\n })),\n counted: typeof value.counted === \"number\" ? value.counted : 0,\n flags: Array.isArray(value.flags) ? value.flags.filter((flag): flag is string => typeof flag === \"string\") : [],\n },\n };\n}\n\nconst SUBURB_STEM = /^\\/(?:locations?|areas?|suburbs?|service-areas?|areas-we-(?:service|serve)|regions?|where-we-work)\\/\\{place\\}$/;\n\n/** A dynamic route that builds one family (not a catch-all, which serves many). */\nfunction isGenerated(route: string): boolean {\n return route.includes(\"[\") && !route.includes(\"[...\") && !route.includes(\"[[...\");\n}\n\n/**\n * Families for pages without a manifest: the sitemap file, then the family of the other pages of their dynamic route,\n * the seeds (the representative service route), the family of their stem, then `/` as home.\n */\nfunction inferFamilies(pages: readonly EstatePage[], seeds: Readonly<Record<string, string>>): void {\n for (const page of pages) {\n if (page.family !== undefined || !page.sitemapFile) continue;\n const file = page.sitemapFile;\n if (/service-?locations?/.test(file)) page.family = page.place?.kind === \"region\" ? \"region\" : \"service-in-suburb\";\n else if (/locations?|areas?|suburbs?/.test(file)) page.family = page.place?.kind === \"region\" ? \"region\" : \"suburb\";\n if (page.family !== undefined) page.familySource = \"sitemap\";\n }\n // A dynamic route can build hubs and suburb pages alike, so pages match on the route and on the kind of place.\n const routeKey = (page: EstatePage) => `${page.srcRoute}|${page.place?.kind ?? \"\"}`;\n const counts = new Map<string, Map<string, number>>();\n for (const page of pages) {\n if (page.familySource !== \"manifest\" || !page.srcRoute || !isGenerated(page.srcRoute)) continue;\n const families = counts.get(routeKey(page)) ?? new Map<string, number>();\n families.set(page.family!, (families.get(page.family!) ?? 0) + 1);\n counts.set(routeKey(page), families);\n }\n const routeFamilies = new Map([...counts].map(([key, families]) => [key, [...families].sort((left, right) => right[1] - left[1])[0]![0]]));\n for (const page of pages) {\n if (page.family !== undefined) continue;\n const family = (page.srcRoute ? routeFamilies.get(routeKey(page)) : undefined) ?? seeds[page.path];\n if (family) {\n page.family = family;\n page.familySource = \"route\";\n }\n }\n // The other pages of a seeded page's dynamic route share its family.\n for (const page of pages) {\n if (page.family !== undefined || !page.srcRoute || !isGenerated(page.srcRoute)) continue;\n const seeded = pages.find((other) => other.familySource === \"route\" && other.srcRoute === page.srcRoute);\n if (seeded) {\n page.family = seeded.family;\n page.familySource = \"route\";\n }\n }\n const stems = new Map<string, string>();\n for (const page of pages) {\n if (!page.place || (page.family !== \"suburb\" && page.family !== \"service-in-suburb\")) continue;\n if (!stems.has(page.place.stem)) stems.set(page.place.stem, page.family);\n }\n // A stem that three or more suburbs share is a family of location pages even when nothing names it: suburb pages\n // under an areas folder (`/locations/{place}`), service-in-suburb pages otherwise.\n const unnamed = new Map<string, number>();\n for (const page of pages) {\n if (page.family === undefined && page.place?.kind === \"suburb\") unnamed.set(page.place.stem, (unnamed.get(page.place.stem) ?? 0) + 1);\n }\n for (const [stem, count] of unnamed) {\n if (count >= 3 && !stems.has(stem)) stems.set(stem, SUBURB_STEM.test(stem) ? \"suburb\" : \"service-in-suburb\");\n }\n for (const page of pages) {\n if (page.family !== undefined) continue;\n if (page.path === \"/\") {\n page.family = \"home\";\n page.familySource = \"path\";\n continue;\n }\n const family = page.place ? stems.get(page.place.stem) : undefined;\n if (!family || !page.place) continue;\n page.family = page.place.kind === \"region\" ? \"region\" : family;\n page.familySource = \"stem\";\n }\n}\n\n/** The page content a person reads, without the header, footer, navigation, call bar or drawer. */\nfunction contentRoot(root: HtmlElement): HtmlElement {\n return descendants(root).find((element) => element.tag === \"main\") ?? descendants(root).find((element) => element.tag === \"body\") ?? root;\n}\n\nconst CHROME = (element: HtmlElement) =>\n [\"header\", \"footer\", \"nav\"].includes(element.tag) || [\"header\", \"footer\", \"callbar\", \"drawer\", \"utility\"].includes(attr(element, \"data-bl\") ?? \"\");\n\nfunction linesOutsideChrome(element: HtmlElement): string[] {\n // readableLines has no skip, so chrome is cut from a shallow copy of the tree.\n const prune = (node: HtmlElement): HtmlElement => ({\n ...node,\n children: node.children.flatMap((child): HtmlNode[] => (child.type === \"element\" ? (CHROME(child) ? [] : [prune(child)]) : [child])),\n });\n return readableLines(prune(element)).map((line) => line.text);\n}\n\nfunction locationCopy(root: HtmlElement): LocationCopy {\n const elements = descendants(root);\n const sections = topLevelSections(root);\n const lines = sections.length > 0\n ? sections.flatMap((section) => readableLines(section).map((line) => ({ section: sectionId(section), text: line.text })))\n : linesOutsideChrome(contentRoot(root)).map((text) => ({ section: \"page\", text }));\n const sectionOf = (id: string) => elements.find((element) => attr(element, \"data-bl-section\") === id);\n\n // The FAQs section, or on a page without sections the disclosures that ask a question.\n const faqSection = sectionOf(\"faqs\");\n const faqScope = faqSection ?? contentRoot(root);\n const faqs: { question: string; answer: string }[] = [];\n for (const details of descendants(faqScope).filter((element) => element.tag === \"details\")) {\n const summary = details.children.find((child): child is HtmlElement => child.type === \"element\" && child.tag === \"summary\");\n if (!summary) continue;\n const question = textOf(summary);\n const answer = normaliseText(textOf(details, true, (element) => element === summary));\n if (question && (faqSection || question.endsWith(\"?\"))) faqs.push({ question, answer });\n }\n if (faqs.length === 0) {\n for (const term of descendants(faqScope).filter((element) => element.tag === \"dt\")) {\n const siblings = term.parent?.children.filter((child): child is HtmlElement => child.type === \"element\") ?? [];\n const definition = siblings[siblings.indexOf(term) + 1];\n const question = textOf(term);\n if (definition?.tag === \"dd\" && (faqSection || question.endsWith(\"?\"))) faqs.push({ question, answer: textOf(definition, true) });\n }\n }\n\n const jobsSection = sectionOf(\"nearby-jobs\");\n let jobs: string[] | null = null;\n if (jobsSection) {\n const cards = descendants(jobsSection).filter(\n (element) => (element.tag === \"li\" || element.tag === \"article\") && !descendants(element).some((inner) => inner.tag === \"li\" || inner.tag === \"article\"),\n );\n const headings = new Set(descendants(jobsSection).filter((element) => /^h[1-6]$/.test(element.tag)).map((element) => textOf(element)));\n jobs = cards.length > 0\n ? cards.map((card) => textOf(card, true)).filter(Boolean)\n : readableLines(jobsSection).map((line) => line.text).filter((text) => !headings.has(text));\n }\n\n const pricesSection = sectionOf(\"price-guide\");\n const reviewsSection = sectionOf(\"reviews\");\n const reviews = reviewsSection\n ? {\n written: descendants(reviewsSection).filter((element) => element.tag === \"blockquote\").length,\n googleLink: descendants(reviewsSection).some((element) => element.tag === \"a\" && /(?:^|\\.)google\\.|g\\.page|goo\\.gl/i.test(attr(element, \"href\") ?? \"\")),\n rating:\n descendants(reviewsSection).some((element) => attr(element, \"data-bl\") === \"rating-chip\") ||\n /\\b[1-5](?:\\.\\d)?\\s*(?:out of 5|stars?|★)|\\b[1-5]\\.\\d\\b/i.test(textOf(reviewsSection, true)),\n }\n : null;\n\n const forms = elements.filter((element) => element.tag === \"form\" && !isHiddenFromPeople(element));\n const form = forms.find((element) => attr(element, \"data-bl\") === \"quote-form\") ?? forms[0];\n let quoteForm: LocationCopy[\"quoteForm\"] = null;\n if (form) {\n const field = descendants(form).find(\n (element) =>\n element.tag === \"input\" &&\n (attr(element, \"autocomplete\") === \"address-level2\" || /suburb|location/i.test(`${attr(element, \"name\") ?? \"\"} ${attr(element, \"id\") ?? \"\"}`)),\n );\n quoteForm = { suburbField: field !== undefined, suburbValue: normaliseText(field ? (attr(field, \"value\") ?? \"\") : \"\") };\n }\n\n return {\n lines,\n faqs,\n jobs,\n prices: pricesSection ? textOf(pricesSection, true) : null,\n reviews,\n quoteForm,\n telLink: elements.some((element) => element.tag === \"a\" && /^tel:/i.test(attr(element, \"href\") ?? \"\")),\n callBar: elements.some((element) => attr(element, \"data-bl\") === \"callbar\"),\n };\n}\n\n/** An internal link's normalised path, or null for other sites, anchors on the same page, mail and phone links. */\nexport function internalPath(href: string, from: string, hosts: ReadonlySet<string>): string | null {\n const value = href.trim();\n if (!value || value.startsWith(\"#\") || /^(?:mailto|tel|sms|javascript|data):/i.test(value)) return null;\n let url: URL;\n try {\n url = new URL(value, `http://localhost${from}`);\n } catch {\n return null;\n }\n if (!/^https?:$/.test(url.protocol) || !hosts.has(url.host) && !hosts.has(url.hostname)) return null;\n let path: string;\n try {\n path = decodeURIComponent(url.pathname);\n } catch {\n path = url.pathname;\n }\n return normalisePath(path);\n}\n\ninterface SitemapFile {\n readonly name: string;\n readonly paths: readonly string[];\n}\n\n/** The URL sets in the build's prerendered sitemap files (`*.xml.body`); sitemap indexes are skipped. */\nfunction readSitemaps(appDir: string): SitemapFile[] {\n if (!existsSync(appDir)) return [];\n const files: SitemapFile[] = [];\n const visit = (directory: string) => {\n for (const entry of readdirSync(directory, { withFileTypes: true })) {\n const full = join(directory, entry.name);\n if (entry.isDirectory()) visit(full);\n else if (entry.name.endsWith(\".xml.body\")) {\n const xml = readFileSync(full, \"utf8\");\n if (!/<urlset\\b/i.test(xml)) continue;\n const paths: string[] = [];\n for (const match of xml.matchAll(/<loc>\\s*([^<\\s]+)\\s*<\\/loc>/gi)) {\n try {\n paths.push(normalisePath(new URL(match[1]!.replace(/&/g, \"&\")).pathname));\n } catch {\n // A loc that is not a URL lists no page.\n }\n }\n const name = relative(appDir, full).replace(/\\.xml\\.body$/, \"\").split(\"/\").pop()!.replace(/-\\d+$/, \"\");\n files.push({ name, paths });\n }\n }\n };\n visit(appDir);\n return files;\n}\n\nfunction readJson(file: string): unknown {\n if (!existsSync(file)) return undefined;\n try {\n return JSON.parse(readFileSync(file, \"utf8\"));\n } catch {\n return undefined;\n }\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return Boolean(value) && typeof value === \"object\" && !Array.isArray(value);\n}\n"]}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The places the full tier reasons about: registry suburbs with their region and centre, and the regions. They come
|
|
3
|
+
* from the site's installed site-core geography at run time (the conformance package compiles without site-core), or
|
|
4
|
+
* from `defineFullGeography` in a test.
|
|
5
|
+
*/
|
|
6
|
+
import { type SuburbCentre, type SuburbCentreLookup } from "../../../page-standard.js";
|
|
7
|
+
export interface FullGeographySuburb {
|
|
8
|
+
readonly slug: string;
|
|
9
|
+
readonly name: string;
|
|
10
|
+
/** The registry region the suburb belongs to. */
|
|
11
|
+
readonly region: string;
|
|
12
|
+
/** The suburb's council, when known. */
|
|
13
|
+
readonly council?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface FullGeographyRegion {
|
|
16
|
+
readonly slug: string;
|
|
17
|
+
readonly name: string;
|
|
18
|
+
/** Every name the region goes by (name, search name and alternates). */
|
|
19
|
+
readonly names: readonly string[];
|
|
20
|
+
}
|
|
21
|
+
export interface FullGeography {
|
|
22
|
+
readonly centreOf: SuburbCentreLookup;
|
|
23
|
+
readonly suburbs: ReadonlyMap<string, FullGeographySuburb>;
|
|
24
|
+
/** Regions and sub-regions, by slug. */
|
|
25
|
+
readonly regions: ReadonlyMap<string, FullGeographyRegion>;
|
|
26
|
+
/** Masks every place name and postcode in lowercase text, so name-swapped copy compares as the same. */
|
|
27
|
+
readonly mask: (text: string) => string;
|
|
28
|
+
/** Lowercase place names (suburbs and regions) and their slugs. */
|
|
29
|
+
readonly names: ReadonlyMap<string, string>;
|
|
30
|
+
/** The slugs of the places a piece of copy names, longest name first ("North Ryde" before "Ryde"). */
|
|
31
|
+
readonly placesIn: (text: string) => string[];
|
|
32
|
+
}
|
|
33
|
+
export interface FullGeographyInput {
|
|
34
|
+
readonly suburbs: readonly (FullGeographySuburb & {
|
|
35
|
+
readonly centre?: SuburbCentre;
|
|
36
|
+
})[];
|
|
37
|
+
readonly regions: readonly {
|
|
38
|
+
readonly slug: string;
|
|
39
|
+
readonly name: string;
|
|
40
|
+
readonly names?: readonly string[];
|
|
41
|
+
}[];
|
|
42
|
+
readonly centreOf?: SuburbCentreLookup;
|
|
43
|
+
}
|
|
44
|
+
export declare function defineFullGeography(input: FullGeographyInput): FullGeography;
|
|
45
|
+
/** The site's installed site-core geography (3.4.0 or later). */
|
|
46
|
+
export declare function loadFullGeography(): Promise<FullGeography>;
|
|
47
|
+
export interface PathPlace {
|
|
48
|
+
readonly slug: string;
|
|
49
|
+
readonly kind: "suburb" | "region";
|
|
50
|
+
/** The path with the place replaced by `{place}`: pages sharing a stem are one family's pages. */
|
|
51
|
+
readonly stem: string;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* The place a path names: its last segment, or the end of its last segment after a hyphen (`/tree-removal-ryde`),
|
|
55
|
+
* when that is a registry suburb or region (`{region}-area` included). The longest match wins.
|
|
56
|
+
*/
|
|
57
|
+
export declare function placeOfPath(path: string, geography: FullGeography): PathPlace | undefined;
|
|
58
|
+
/** A place's display name, for messages and for matching it in copy. */
|
|
59
|
+
export declare function placeName(slug: string, geography: FullGeography): string;
|
|
60
|
+
//# sourceMappingURL=geography.d.ts.map
|
|
@@ -0,0 +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;;;GAGG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,aAAa,GAAG,SAAS,GAAG,SAAS,CAoBzF;AAMD,wEAAwE;AACxE,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,aAAa,GAAG,MAAM,CAExE"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The places the full tier reasons about: registry suburbs with their region and centre, and the regions. They come
|
|
3
|
+
* from the site's installed site-core geography at run time (the conformance package compiles without site-core), or
|
|
4
|
+
* from `defineFullGeography` in a test.
|
|
5
|
+
*/
|
|
6
|
+
import { createPlaceMask } from "../../../page-standard.js";
|
|
7
|
+
export function defineFullGeography(input) {
|
|
8
|
+
const suburbs = new Map();
|
|
9
|
+
const centres = new Map();
|
|
10
|
+
for (const suburb of input.suburbs) {
|
|
11
|
+
if (suburbs.has(suburb.slug))
|
|
12
|
+
continue;
|
|
13
|
+
suburbs.set(suburb.slug, { slug: suburb.slug, name: suburb.name, region: suburb.region, ...(suburb.council ? { council: suburb.council } : {}) });
|
|
14
|
+
if (suburb.centre)
|
|
15
|
+
centres.set(suburb.slug, suburb.centre);
|
|
16
|
+
}
|
|
17
|
+
const regions = new Map();
|
|
18
|
+
for (const region of input.regions) {
|
|
19
|
+
if (!regions.has(region.slug))
|
|
20
|
+
regions.set(region.slug, { slug: region.slug, name: region.name, names: [...new Set([region.name, ...(region.names ?? [])])] });
|
|
21
|
+
}
|
|
22
|
+
const names = new Map();
|
|
23
|
+
for (const suburb of suburbs.values())
|
|
24
|
+
names.set(suburb.name.toLowerCase(), suburb.slug);
|
|
25
|
+
for (const region of regions.values())
|
|
26
|
+
for (const name of region.names)
|
|
27
|
+
if (!names.has(name.toLowerCase()))
|
|
28
|
+
names.set(name.toLowerCase(), region.slug);
|
|
29
|
+
const alternation = [...names.keys()]
|
|
30
|
+
.filter((name) => name.length >= 3)
|
|
31
|
+
.sort((left, right) => right.length - left.length)
|
|
32
|
+
.map((name) => name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace(/\s+/g, "\\s+"));
|
|
33
|
+
const pattern = alternation.length > 0 ? new RegExp(`\\b(?:${alternation.join("|")})\\b`, "gi") : null;
|
|
34
|
+
return {
|
|
35
|
+
centreOf: input.centreOf ?? ((slug) => centres.get(slug)),
|
|
36
|
+
suburbs,
|
|
37
|
+
regions,
|
|
38
|
+
mask: createPlaceMask([...names.keys()]),
|
|
39
|
+
names,
|
|
40
|
+
placesIn: (text) => {
|
|
41
|
+
if (!pattern)
|
|
42
|
+
return [];
|
|
43
|
+
const found = new Set();
|
|
44
|
+
for (const match of text.matchAll(pattern)) {
|
|
45
|
+
const slug = names.get(match[0].toLowerCase().replace(/\s+/g, " "));
|
|
46
|
+
if (slug)
|
|
47
|
+
found.add(slug);
|
|
48
|
+
}
|
|
49
|
+
return [...found];
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
// A variable specifier, so the package compiles without site-core; it resolves to the site's installed copy.
|
|
54
|
+
const GEOGRAPHY_MODULE = "@brandlab-digital/site-core/geography";
|
|
55
|
+
let loaded;
|
|
56
|
+
/** The site's installed site-core geography (3.4.0 or later). */
|
|
57
|
+
export function loadFullGeography() {
|
|
58
|
+
loaded ??= import(GEOGRAPHY_MODULE).then((module) => {
|
|
59
|
+
if (typeof module.getSuburbCentre !== "function")
|
|
60
|
+
throw new Error("the installed site-core geography has no suburb centres (3.4.0 or later is needed)");
|
|
61
|
+
const registries = [module.sydneyGeography, module.centralCoastGeography];
|
|
62
|
+
return defineFullGeography({
|
|
63
|
+
suburbs: registries.flatMap((registry) => [...registry.suburbs, ...registry.nonSuburbLocalities].map((place) => ({ slug: place.slug, name: place.name, region: place.regionSlug, council: place.councilSlug }))),
|
|
64
|
+
regions: registries.flatMap((registry) => [
|
|
65
|
+
...registry.regions.map((region) => ({ slug: region.slug, name: region.name, names: [region.seoName, ...(region.alternateNames ?? [])] })),
|
|
66
|
+
...registry.subRegions.map((region) => ({ slug: region.slug, name: region.name })),
|
|
67
|
+
]),
|
|
68
|
+
centreOf: module.getSuburbCentre,
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
return loaded;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
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). The longest match wins.
|
|
76
|
+
*/
|
|
77
|
+
export function placeOfPath(path, geography) {
|
|
78
|
+
const segments = path.split("/");
|
|
79
|
+
const last = segments.pop() ?? "";
|
|
80
|
+
if (!last)
|
|
81
|
+
return undefined;
|
|
82
|
+
const parent = segments.join("/");
|
|
83
|
+
const kindOf = (slug) => {
|
|
84
|
+
if (geography.suburbs.has(slug))
|
|
85
|
+
return "suburb";
|
|
86
|
+
if (geography.regions.has(slug))
|
|
87
|
+
return "region";
|
|
88
|
+
if (slug.endsWith("-area") && geography.regions.has(slug.slice(0, -5)))
|
|
89
|
+
return "region";
|
|
90
|
+
return undefined;
|
|
91
|
+
};
|
|
92
|
+
const whole = kindOf(last);
|
|
93
|
+
if (whole)
|
|
94
|
+
return { slug: regionSlug(last, geography), kind: whole, stem: `${parent}/{place}` };
|
|
95
|
+
const parts = last.split("-");
|
|
96
|
+
for (let start = 1; start < parts.length; start += 1) {
|
|
97
|
+
const slug = parts.slice(start).join("-");
|
|
98
|
+
const kind = kindOf(slug);
|
|
99
|
+
if (kind)
|
|
100
|
+
return { slug: regionSlug(slug, geography), kind, stem: `${parent}/${parts.slice(0, start).join("-")}-{place}` };
|
|
101
|
+
}
|
|
102
|
+
return undefined;
|
|
103
|
+
}
|
|
104
|
+
function regionSlug(slug, geography) {
|
|
105
|
+
return !geography.suburbs.has(slug) && !geography.regions.has(slug) && slug.endsWith("-area") ? slug.slice(0, -5) : slug;
|
|
106
|
+
}
|
|
107
|
+
/** A place's display name, for messages and for matching it in copy. */
|
|
108
|
+
export function placeName(slug, geography) {
|
|
109
|
+
return geography.suburbs.get(slug)?.name ?? geography.regions.get(slug)?.name ?? slug;
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=geography.js.map
|
|
@@ -0,0 +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"]}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { SiteSystemFinding } from "../../checks.js";
|
|
2
|
+
import type { SiteSystemRenderedInput } from "../../runners.js";
|
|
3
|
+
import { type FullContext } from "./common.js";
|
|
4
|
+
import { type FullGeography } from "./geography.js";
|
|
5
|
+
export { FULL_LISTED_FAILURES, type FullContext } from "./common.js";
|
|
6
|
+
export { headingPhrase } from "./e10-phrases.js";
|
|
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";
|
|
9
|
+
export { K12_FAMILIES, isDataBackedReason } from "./k12-manifests.js";
|
|
10
|
+
export { L3_MAX_LIST, L3_MIN_LIST, L3_TOLERANCE_KM } from "./l3-neighbours.js";
|
|
11
|
+
export { L4_MIN_ROTATION_PAGES, L4_MIN_TEMPLATE_WORDS } from "./l4-local-copy.js";
|
|
12
|
+
export { yearStamp } from "./l10-location-copy.js";
|
|
13
|
+
export { L6_MIN_INBOUND, L6_MIN_PAGE_TYPES, L7_MAX_DEPTH, clickDepths } from "./links.js";
|
|
14
|
+
export { sydneyDay } from "./loc-index.js";
|
|
15
|
+
export { FULL_STRATEGY_FILE, WAVE_MAX_GAP_DAYS, WAVE_MIN_GAP_DAYS, matchesWaveRoute, normalisePhrase, readFullStrategy, waveScheduleProblems } from "./strategy.js";
|
|
16
|
+
/** 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 interface FullTierRuleOptions {
|
|
19
|
+
/** The day waves are judged on. Defaults to now. */
|
|
20
|
+
readonly asOf?: Date;
|
|
21
|
+
/** The build directory; defaults to `input.buildDir`, then `<projectRoot>/.next`. */
|
|
22
|
+
readonly buildDir?: string;
|
|
23
|
+
/** Places for a test; a real run loads the site's site-core geography. Null runs without places. */
|
|
24
|
+
readonly geography?: FullGeography | null;
|
|
25
|
+
/** Canonical section ids per family for a test; a real run loads site-core's `defaultSectionOrder`. */
|
|
26
|
+
readonly defaultOrder?: FullContext["defaultOrder"];
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Runs the full-tier rules over the built estate at `projectRoot`. A site that declares a lower tier (or none) gets no
|
|
30
|
+
* findings: the report shows the full checks as not run, which never blocks below the full tier.
|
|
31
|
+
*/
|
|
32
|
+
export declare function runFullTierRules(input: SiteSystemRenderedInput, options?: FullTierRuleOptions): Promise<readonly SiteSystemFinding[]>;
|
|
33
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +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;AAG/C,OAAO,EAAqB,KAAK,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAWvE,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,MAAM,gBAAgB,CAAC;AAClI,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,kBAAkB,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,eAAe,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAEpK,uFAAuF;AACvF,eAAO,MAAM,mBAAmB,6FAAqI,CAAC;AAEtK,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;AAoCD;;;GAGG;AACH,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,uBAAuB,EAC9B,OAAO,GAAE,mBAAwB,GAChC,OAAO,CAAC,SAAS,iBAAiB,EAAE,CAAC,CA+CvC"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The full-tier rules (release 3.12.0): page families, location estates, section manifests, links and the local data
|
|
3
|
+
* contract. They read the production build's route catalogue and pages (`.next`), the project's strategy record and
|
|
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 and the location indexing guard. The runner has
|
|
6
|
+
* the signature the ready gate registers.
|
|
7
|
+
*/
|
|
8
|
+
import { join } from "node:path";
|
|
9
|
+
import { readSiteSystemProjectConfig } from "../../config.js";
|
|
10
|
+
import {} from "./common.js";
|
|
11
|
+
import { checkE10 } from "./e10-phrases.js";
|
|
12
|
+
import { readEstate } from "./estate.js";
|
|
13
|
+
import { loadFullGeography } from "./geography.js";
|
|
14
|
+
import { checkK12 } from "./k12-manifests.js";
|
|
15
|
+
import { checkL3 } from "./l3-neighbours.js";
|
|
16
|
+
import { checkL4 } from "./l4-local-copy.js";
|
|
17
|
+
import { checkL9 } from "./l9-data-contract.js";
|
|
18
|
+
import { checkL10 } from "./l10-location-copy.js";
|
|
19
|
+
import { checkE5, checkL6, checkL7, checkL8, inboundLinks } from "./links.js";
|
|
20
|
+
import { checkLocationIndexing, sydneyDay } from "./loc-index.js";
|
|
21
|
+
import { checkS4 } from "./s4-route-files.js";
|
|
22
|
+
import { readFullStrategy } from "./strategy.js";
|
|
23
|
+
export { FULL_LISTED_FAILURES } from "./common.js";
|
|
24
|
+
export { headingPhrase } from "./e10-phrases.js";
|
|
25
|
+
export { readEstate } from "./estate.js";
|
|
26
|
+
export { defineFullGeography, loadFullGeography, placeOfPath } from "./geography.js";
|
|
27
|
+
export { K12_FAMILIES, isDataBackedReason } from "./k12-manifests.js";
|
|
28
|
+
export { L3_MAX_LIST, L3_MIN_LIST, L3_TOLERANCE_KM } from "./l3-neighbours.js";
|
|
29
|
+
export { L4_MIN_ROTATION_PAGES, L4_MIN_TEMPLATE_WORDS } from "./l4-local-copy.js";
|
|
30
|
+
export { yearStamp } from "./l10-location-copy.js";
|
|
31
|
+
export { L6_MIN_INBOUND, L6_MIN_PAGE_TYPES, L7_MAX_DEPTH, clickDepths } from "./links.js";
|
|
32
|
+
export { sydneyDay } from "./loc-index.js";
|
|
33
|
+
export { FULL_STRATEGY_FILE, WAVE_MAX_GAP_DAYS, WAVE_MIN_GAP_DAYS, matchesWaveRoute, normalisePhrase, readFullStrategy, waveScheduleProblems } from "./strategy.js";
|
|
34
|
+
/** 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"];
|
|
36
|
+
// Variable specifiers, so the package compiles without site-core; they resolve to the site's installed copy.
|
|
37
|
+
const SECTION_ORDER_MODULE = "@brandlab-digital/site-core/section-order";
|
|
38
|
+
async function loadDefaultOrder() {
|
|
39
|
+
try {
|
|
40
|
+
const module = (await import(SECTION_ORDER_MODULE));
|
|
41
|
+
const defaults = module.defaultSectionOrder;
|
|
42
|
+
if (typeof defaults !== "function")
|
|
43
|
+
return "the installed site-core has no defaultSectionOrder (3.11.0 or later is needed)";
|
|
44
|
+
return (family, trade) => {
|
|
45
|
+
try {
|
|
46
|
+
const order = defaults(family, trade);
|
|
47
|
+
return { cap: order.cap, entries: order.entries.map((entry) => ({ id: entry.id, requirement: entry.requirement })) };
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
return `site-core's section orders could not be imported: ${error instanceof Error ? error.message : String(error)}`;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/** The declared tier and the representative service route; null tier when the config cannot be read in full. */
|
|
59
|
+
function projectSettings(projectRoot) {
|
|
60
|
+
try {
|
|
61
|
+
const config = readSiteSystemProjectConfig(projectRoot);
|
|
62
|
+
// A config the gate cannot read in full could hide a full tier, so the checks run.
|
|
63
|
+
return { tier: config.complete ? (config.siteSystem?.tier ?? "none") : null, service: config.representativeRoutes["service"] };
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return { tier: null, service: undefined };
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Runs the full-tier rules over the built estate at `projectRoot`. A site that declares a lower tier (or none) gets no
|
|
71
|
+
* findings: the report shows the full checks as not run, which never blocks below the full tier.
|
|
72
|
+
*/
|
|
73
|
+
export async function runFullTierRules(input, options = {}) {
|
|
74
|
+
const settings = projectSettings(input.projectRoot);
|
|
75
|
+
if (settings.tier !== null && settings.tier !== "full")
|
|
76
|
+
return [];
|
|
77
|
+
let geography = null;
|
|
78
|
+
let geographyProblem = "";
|
|
79
|
+
if (options.geography !== undefined) {
|
|
80
|
+
geography = options.geography;
|
|
81
|
+
if (!geography)
|
|
82
|
+
geographyProblem = "no geography was supplied";
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
try {
|
|
86
|
+
geography = await loadFullGeography();
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
geographyProblem = `site-core's geography could not be imported: ${error instanceof Error ? error.message : String(error)}`;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
const estate = readEstate({
|
|
93
|
+
pages: input.pages,
|
|
94
|
+
buildDir: options.buildDir ?? input.buildDir ?? join(input.projectRoot, ".next"),
|
|
95
|
+
geography,
|
|
96
|
+
...(settings.service ? { seeds: { [settings.service]: "service" } } : {}),
|
|
97
|
+
});
|
|
98
|
+
const today = sydneyDay(options.asOf ?? new Date());
|
|
99
|
+
const context = {
|
|
100
|
+
estate,
|
|
101
|
+
geography,
|
|
102
|
+
geographyProblem,
|
|
103
|
+
strategy: readFullStrategy(input.projectRoot, today),
|
|
104
|
+
projectRoot: input.projectRoot,
|
|
105
|
+
today,
|
|
106
|
+
defaultOrder: options.defaultOrder ?? (await loadDefaultOrder()),
|
|
107
|
+
};
|
|
108
|
+
const inbound = inboundLinks(estate);
|
|
109
|
+
const findings = [
|
|
110
|
+
...checkK12(context),
|
|
111
|
+
...checkE5(context, inbound),
|
|
112
|
+
...checkE10(context),
|
|
113
|
+
...checkL3(context),
|
|
114
|
+
...checkL4(context),
|
|
115
|
+
...checkL6(context, inbound),
|
|
116
|
+
...checkL7(context),
|
|
117
|
+
...checkL8(context),
|
|
118
|
+
...checkL9(context),
|
|
119
|
+
...checkL10(context),
|
|
120
|
+
];
|
|
121
|
+
// The copy is only for L3, L4 and L10; let it go before the source scan.
|
|
122
|
+
for (const page of estate.pages)
|
|
123
|
+
page.copy = undefined;
|
|
124
|
+
return [...findings, ...checkS4(context), ...checkLocationIndexing(context)];
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +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"]}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* K12 on every family (Site System v2 §3.3, §13.2): each page of a family carries one section manifest
|
|
3
|
+
* (`<script type="application/json" data-bl-manifest>`, written by SectionSequence). Every required section renders
|
|
4
|
+
* or is skipped with a reason drawn from its data (`no-…`), never the sequencer's `not-provided`, `cap` or
|
|
5
|
+
* `duplicate`; every conditional section that does not render says its data is absent; the planned order is the
|
|
6
|
+
* family's canonical one or the alternative recorded in strategy.ts; and the page renders exactly the sections the
|
|
7
|
+
* manifest lists, in that order. A page over its cap (`cap-exceeded`) is a warning, not a failure. The contact page
|
|
8
|
+
* has its own check (K12-contact).
|
|
9
|
+
*/
|
|
10
|
+
import type { SiteSystemFinding } from "../../checks.js";
|
|
11
|
+
import { type FullContext } from "./common.js";
|
|
12
|
+
import type { EstatePage, ManifestData } from "./estate.js";
|
|
13
|
+
/** The families that carry a manifest (site-core BRANDLAB_PAGE_FAMILIES without the contact page). */
|
|
14
|
+
export declare const K12_FAMILIES: readonly ["home", "service", "service-in-suburb", "region", "suburb", "cost-guide", "services-hub", "locations-hub", "projects-hub", "project", "gallery", "reviews", "about", "faqs", "guides-hub", "guide", "council", "emergency"];
|
|
15
|
+
/** As site-core's isDataBackedSkipReason: the section's own data is absent, or its content is in the hero checklist. */
|
|
16
|
+
export declare function isDataBackedReason(reason: string): boolean;
|
|
17
|
+
export declare function manifestProblems(page: EstatePage, manifest: ManifestData, context: FullContext): string[];
|
|
18
|
+
export declare function checkK12(context: FullContext): SiteSystemFinding[];
|
|
19
|
+
//# sourceMappingURL=k12-manifests.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"k12-manifests.d.ts","sourceRoot":"","sources":["../../../../src/site-system/rules/full/k12-manifests.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAAyD,KAAK,WAAW,EAAE,MAAM,aAAa,CAAC;AACtG,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE5D,sGAAsG;AACtG,eAAO,MAAM,YAAY,uOAmBf,CAAC;AAEX,wHAAwH;AACxH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAE1D;AAMD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,YAAY,EAAE,OAAO,EAAE,WAAW,GAAG,MAAM,EAAE,CA8DzG;AAED,wBAAgB,QAAQ,CAAC,OAAO,EAAE,WAAW,GAAG,iBAAiB,EAAE,CA+BlE"}
|