@djangocfg/seo 2.1.50
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +192 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.mjs +3780 -0
- package/dist/cli.mjs.map +1 -0
- package/dist/crawler/index.d.ts +88 -0
- package/dist/crawler/index.mjs +610 -0
- package/dist/crawler/index.mjs.map +1 -0
- package/dist/google-console/index.d.ts +95 -0
- package/dist/google-console/index.mjs +539 -0
- package/dist/google-console/index.mjs.map +1 -0
- package/dist/index.d.ts +285 -0
- package/dist/index.mjs +3236 -0
- package/dist/index.mjs.map +1 -0
- package/dist/link-checker/index.d.ts +76 -0
- package/dist/link-checker/index.mjs +326 -0
- package/dist/link-checker/index.mjs.map +1 -0
- package/dist/markdown-report-B3QdDzxE.d.ts +193 -0
- package/dist/reports/index.d.ts +24 -0
- package/dist/reports/index.mjs +836 -0
- package/dist/reports/index.mjs.map +1 -0
- package/dist/routes/index.d.ts +69 -0
- package/dist/routes/index.mjs +372 -0
- package/dist/routes/index.mjs.map +1 -0
- package/dist/scanner-Cz4Th2Pt.d.ts +60 -0
- package/dist/types/index.d.ts +144 -0
- package/dist/types/index.mjs +3 -0
- package/dist/types/index.mjs.map +1 -0
- package/package.json +114 -0
- package/src/analyzer.ts +256 -0
- package/src/cli/commands/audit.ts +260 -0
- package/src/cli/commands/content.ts +180 -0
- package/src/cli/commands/crawl.ts +32 -0
- package/src/cli/commands/index.ts +12 -0
- package/src/cli/commands/inspect.ts +60 -0
- package/src/cli/commands/links.ts +41 -0
- package/src/cli/commands/robots.ts +36 -0
- package/src/cli/commands/routes.ts +126 -0
- package/src/cli/commands/sitemap.ts +48 -0
- package/src/cli/index.ts +149 -0
- package/src/cli/types.ts +40 -0
- package/src/config.ts +207 -0
- package/src/content/index.ts +51 -0
- package/src/content/link-checker.ts +182 -0
- package/src/content/link-fixer.ts +188 -0
- package/src/content/scanner.ts +200 -0
- package/src/content/sitemap-generator.ts +321 -0
- package/src/content/types.ts +140 -0
- package/src/crawler/crawler.ts +425 -0
- package/src/crawler/index.ts +10 -0
- package/src/crawler/robots-parser.ts +171 -0
- package/src/crawler/sitemap-validator.ts +204 -0
- package/src/google-console/analyzer.ts +317 -0
- package/src/google-console/auth.ts +100 -0
- package/src/google-console/client.ts +281 -0
- package/src/google-console/index.ts +9 -0
- package/src/index.ts +144 -0
- package/src/link-checker/index.ts +461 -0
- package/src/reports/claude-context.ts +149 -0
- package/src/reports/generator.ts +244 -0
- package/src/reports/index.ts +27 -0
- package/src/reports/json-report.ts +320 -0
- package/src/reports/markdown-report.ts +246 -0
- package/src/reports/split-report.ts +252 -0
- package/src/routes/analyzer.ts +324 -0
- package/src/routes/index.ts +25 -0
- package/src/routes/scanner.ts +298 -0
- package/src/types/index.ts +222 -0
- package/src/utils/index.ts +154 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/reports/json-report.ts","../../src/reports/markdown-report.ts","../../src/reports/split-report.ts","../../src/reports/claude-context.ts","../../src/reports/generator.ts"],"names":["formatCategory","existsSync","readdirSync","rmSync","join","mkdirSync","writeFileSync"],"mappings":";;;;;;;AA0BO,SAAS,kBAAA,CACd,OAAA,EACA,IAAA,EAKA,OAAA,GAA6B,EAAC,EACnB;AACX,EAAA,MAAM,EAAE,QAAQ,cAAA,GAAiB,IAAI,YAAA,GAAe,IAAG,GAAI,IAAA;AAC3D,EAAA,MAAM,eAAA,GAAkB,QAAQ,eAAA,IAAmB,EAAA;AAGnD,EAAA,MAAM,aAAA,GAAgB,kBAAA,CAAmB,MAAA,EAAQ,eAAe,CAAA;AAEhE,EAAA,MAAM,MAAA,GAAoB;AAAA,IACxB,IAAI,gBAAA,EAAiB;AAAA,IACrB,OAAA;AAAA,IACA,WAAA,EAAA,iBAAa,IAAI,IAAA,EAAK,EAAE,WAAA,EAAY;AAAA,IACpC,OAAA,EAAS,eAAA,CAAgB,MAAA,EAAQ,cAAA,EAAgB,YAAY,CAAA;AAAA;AAAA,IAC7D,MAAA,EAAQ,WAAW,aAAa,CAAA;AAAA,IAChC,cAAA,EAAgB,QAAQ,cAAA,GAAiB,cAAA,CAAe,MAAM,CAAA,EAAG,GAAG,IAAI,EAAC;AAAA,IACzE,YAAA,EAAc,QAAQ,cAAA,GAAiB,YAAA,CAAa,MAAM,CAAA,EAAG,GAAG,IAAI,EAAC;AAAA,IACrE,eAAA,EAAiB,uBAAA,CAAwB,MAAA,EAAQ,eAAe;AAAA,GAClE;AAEA,EAAA,OAAO,MAAA;AACT;AAKA,SAAS,kBAAA,CAAmB,QAAoB,OAAA,EAA6B;AAC3E,EAAA,MAAM,OAAA,uBAAc,GAAA,EAAwB;AAE5C,EAAA,KAAA,MAAW,SAAS,MAAA,EAAQ;AAC1B,IAAA,MAAM,WAAW,OAAA,CAAQ,GAAA,CAAI,KAAA,CAAM,KAAK,KAAK,EAAC;AAC9C,IAAA,QAAA,CAAS,KAAK,KAAK,CAAA;AACnB,IAAA,OAAA,CAAQ,GAAA,CAAI,KAAA,CAAM,KAAA,EAAO,QAAQ,CAAA;AAAA,EACnC;AAEA,EAAA,MAAM,UAAsB,EAAC;AAC7B,EAAA,KAAA,MAAW,GAAG,KAAK,CAAA,IAAK,OAAA,EAAS;AAC/B,IAAA,MAAM,MAAA,GAAS,KAAA,CAAM,IAAA,CAAK,CAAC,GAAG,CAAA,KAAM;AAClC,MAAA,MAAM,aAAA,GAAgB,EAAE,QAAA,EAAU,CAAA,EAAG,OAAO,CAAA,EAAG,OAAA,EAAS,CAAA,EAAG,IAAA,EAAM,CAAA,EAAE;AACnE,MAAA,OAAO,cAAc,CAAA,CAAE,QAAQ,CAAA,GAAI,aAAA,CAAc,EAAE,QAAQ,CAAA;AAAA,IAC7D,CAAC,CAAA;AACD,IAAA,OAAA,CAAQ,KAAK,GAAG,MAAA,CAAO,KAAA,CAAM,CAAA,EAAG,OAAO,CAAC,CAAA;AAAA,EAC1C;AAEA,EAAA,OAAO,OAAA;AACT;AAKA,SAAS,eAAA,CACP,MAAA,EACA,cAAA,EACA,YAAA,EACe;AACf,EAAA,MAAM,YAAY,IAAA,CAAK,GAAA;AAAA,IACrB,cAAA,CAAe,MAAA;AAAA,IACf,YAAA,CAAa,MAAA;AAAA,IACb,IAAI,IAAI,MAAA,CAAO,GAAA,CAAI,CAAC,CAAA,KAAM,CAAA,CAAE,GAAG,CAAC,CAAA,CAAE;AAAA,GACpC;AAEA,EAAA,MAAM,cAAc,cAAA,CAAe,MAAA;AAAA,IACjC,CAAC,CAAA,KAAM,CAAA,CAAE,iBAAA,CAAkB,aAAA,KAAkB;AAAA,GAC/C,CAAE,MAAA;AAEF,EAAA,MAAM,iBAAiB,cAAA,CAAe,MAAA;AAAA,IACpC,CAAC,CAAA,KACC,CAAA,CAAE,iBAAA,CAAkB,aAAA,KAAkB,aAAA,IACtC,CAAA,CAAE,iBAAA,CAAkB,aAAA,KAAkB,+BAAA,IACtC,CAAA,CAAE,iBAAA,CAAkB,aAAA,KAAkB;AAAA,GAC1C,CAAE,MAAA;AAEF,EAAA,MAAM,mBAAmB,MAAA,CAAO,MAAA;AAAA,IAC9B,CAAC,KAAK,KAAA,KAAU;AACd,MAAA,GAAA,CAAI,MAAM,QAAQ,CAAA,GAAA,CAAK,IAAI,KAAA,CAAM,QAAQ,KAAK,CAAA,IAAK,CAAA;AACnD,MAAA,OAAO,GAAA;AAAA,IACT,CAAA;AAAA,IACA;AAAC,GACH;AAEA,EAAA,MAAM,mBAAmB,MAAA,CAAO,MAAA;AAAA,IAC9B,CAAC,KAAK,KAAA,KAAU;AACd,MAAA,GAAA,CAAI,MAAM,QAAQ,CAAA,GAAA,CAAK,IAAI,KAAA,CAAM,QAAQ,KAAK,CAAA,IAAK,CAAA;AACnD,MAAA,OAAO,GAAA;AAAA,IACT,CAAA;AAAA,IACA;AAAC,GACH;AAGA,EAAA,MAAM,WAAA,GAAc,oBAAA,CAAqB,MAAA,EAAQ,SAAS,CAAA;AAE1D,EAAA,OAAO;AAAA,IACL,SAAA;AAAA,IACA,WAAA;AAAA,IACA,cAAA;AAAA,IACA,gBAAA;AAAA,IACA,gBAAA;AAAA,IACA;AAAA,GACF;AACF;AAKA,SAAS,oBAAA,CAAqB,QAAoB,SAAA,EAA2B;AAC3E,EAAA,IAAI,SAAA,KAAc,GAAG,OAAO,GAAA;AAE5B,EAAA,MAAM,eAAA,GAAiD;AAAA,IACrD,QAAA,EAAU,EAAA;AAAA,IACV,KAAA,EAAO,CAAA;AAAA,IACP,OAAA,EAAS,CAAA;AAAA,IACT,IAAA,EAAM;AAAA,GACR;AAEA,EAAA,MAAM,eAAe,MAAA,CAAO,MAAA;AAAA,IAC1B,CAAC,GAAA,EAAK,KAAA,KAAU,GAAA,GAAM,eAAA,CAAgB,MAAM,QAAQ,CAAA;AAAA,IACpD;AAAA,GACF;AAGA,EAAA,MAAM,aAAa,SAAA,GAAY,EAAA;AAC/B,EAAA,MAAM,YAAA,GAAe,IAAA,CAAK,GAAA,CAAI,YAAA,GAAe,YAAY,CAAC,CAAA;AAE1D,EAAA,OAAO,IAAA,CAAK,KAAA,CAAA,CAAO,CAAA,GAAI,YAAA,IAAgB,GAAG,CAAA;AAC5C;AAKA,SAAS,uBAAA,CAAwB,MAAA,EAAoB,OAAA,GAAkB,EAAA,EAAsB;AAC3F,EAAA,MAAM,kBAAoC,EAAC;AAC3C,EAAA,MAAM,WAAA,uBAAkB,GAAA,EAAwB;AAGhD,EAAA,KAAA,MAAW,SAAS,MAAA,EAAQ;AAC1B,IAAA,MAAM,MAAM,CAAA,EAAG,KAAA,CAAM,QAAQ,CAAA,CAAA,EAAI,MAAM,KAAK,CAAA,CAAA;AAC5C,IAAA,IAAI,CAAC,WAAA,CAAY,GAAA,CAAI,GAAG,CAAA,EAAG;AACzB,MAAA,WAAA,CAAY,GAAA,CAAI,GAAA,EAAK,EAAE,CAAA;AAAA,IACzB;AACA,IAAA,WAAA,CAAY,GAAA,CAAI,GAAG,CAAA,CAAG,IAAA,CAAK,KAAK,CAAA;AAAA,EAClC;AAGA,EAAA,KAAA,MAAW,GAAG,aAAa,CAAA,IAAK,WAAA,EAAa;AAC3C,IAAA,MAAM,UAAA,GAAa,cAAc,CAAC,CAAA;AAClC,IAAA,IAAI,CAAC,UAAA,EAAY;AACjB,IAAA,MAAM,WAAW,UAAA,CAAW,QAAA;AAE5B,IAAA,MAAM,QAAA,GACJ,aAAa,UAAA,GACT,CAAA,GACA,aAAa,OAAA,GACX,CAAA,GACA,QAAA,KAAa,SAAA,GACX,CAAA,GACA,CAAA;AAEV,IAAA,MAAM,SACJ,QAAA,IAAY,CAAA,GAAI,MAAA,GAAS,QAAA,KAAa,IAAI,QAAA,GAAW,KAAA;AAEvD,IAAA,MAAM,UAAU,aAAA,CAAc,GAAA,CAAI,CAAC,CAAA,KAAM,EAAE,GAAG,CAAA;AAC9C,IAAA,MAAM,aAAa,OAAA,CAAQ,MAAA;AAC3B,IAAA,MAAM,WAAA,GAAc,OAAA,CAAQ,KAAA,CAAM,CAAA,EAAG,OAAO,CAAA;AAE5C,IAAA,eAAA,CAAgB,IAAA,CAAK;AAAA,MACnB,QAAA;AAAA,MACA,UAAU,UAAA,CAAW,QAAA;AAAA,MACrB,OAAO,UAAA,CAAW,KAAA;AAAA,MAClB,WAAA,EAAa,UAAA,GAAa,OAAA,GACtB,CAAA,EAAG,UAAA,CAAW,WAAW,CAAA,UAAA,EAAa,OAAO,CAAA,IAAA,EAAO,UAAU,CAAA,MAAA,CAAA,GAC9D,UAAA,CAAW,WAAA;AAAA,MACf,YAAA,EAAc,WAAA;AAAA,MACd,eAAA,EAAiB,MAAA;AAAA,MACjB,WAAA,EAAa,CAAC,UAAA,CAAW,cAAc;AAAA,KACxC,CAAA;AAAA,EACH;AAGA,EAAA,OAAO,eAAA,CAAgB,KAAK,CAAC,CAAA,EAAG,MAAM,CAAA,CAAE,QAAA,GAAW,EAAE,QAAQ,CAAA;AAC/D;AAKA,SAAS,WAAW,MAAA,EAAgC;AAClD,EAAA,MAAM,aAAA,GAA+C;AAAA,IACnD,QAAA,EAAU,CAAA;AAAA,IACV,KAAA,EAAO,CAAA;AAAA,IACP,OAAA,EAAS,CAAA;AAAA,IACT,IAAA,EAAM;AAAA,GACR;AAEA,EAAA,OAAO,CAAC,GAAG,MAAM,EAAE,IAAA,CAAK,CAAC,GAAG,CAAA,KAAM;AAChC,IAAA,MAAM,eAAe,aAAA,CAAc,CAAA,CAAE,QAAQ,CAAA,GAAI,aAAA,CAAc,EAAE,QAAQ,CAAA;AACzE,IAAA,IAAI,YAAA,KAAiB,GAAG,OAAO,YAAA;AAC/B,IAAA,OAAO,CAAA,CAAE,QAAA,CAAS,aAAA,CAAc,CAAA,CAAE,QAAQ,CAAA;AAAA,EAC5C,CAAC,CAAA;AACH;AAKA,SAAS,gBAAA,GAA2B;AAClC,EAAA,MAAM,SAAA,GAAY,IAAA,CAAK,GAAA,EAAI,CAAE,SAAS,EAAE,CAAA;AACxC,EAAA,MAAM,MAAA,GAAS,KAAK,MAAA,EAAO,CAAE,SAAS,EAAE,CAAA,CAAE,SAAA,CAAU,CAAA,EAAG,CAAC,CAAA;AACxD,EAAA,OAAO,CAAA,WAAA,EAAc,SAAS,CAAA,CAAA,EAAI,MAAM,CAAA,CAAA;AAC1C;AAKO,SAAS,gBAAA,CAAiB,MAAA,EAAmB,MAAA,GAAS,IAAA,EAAc;AACzE,EAAA,OAAO,KAAK,SAAA,CAAU,MAAA,EAAQ,IAAA,EAAM,MAAA,GAAS,IAAI,CAAC,CAAA;AACpD;AAMO,IAAM,gBAAA,GAAmB;AAAA,EAC9B,OAAA,EAAS,yCAAA;AAAA,EACT,KAAA,EAAO,YAAA;AAAA,EACP,WAAA,EAAa,iEAAA;AAAA,EACb,IAAA,EAAM,QAAA;AAAA,EACN,UAAA,EAAY;AAAA,IACV,EAAA,EAAI,EAAE,IAAA,EAAM,QAAA,EAAU,aAAa,0BAAA,EAA2B;AAAA,IAC9D,OAAA,EAAS,EAAE,IAAA,EAAM,QAAA,EAAU,aAAa,mBAAA,EAAoB;AAAA,IAC5D,WAAA,EAAa,EAAE,IAAA,EAAM,QAAA,EAAU,QAAQ,WAAA,EAAY;AAAA,IACnD,OAAA,EAAS;AAAA,MACP,IAAA,EAAM,QAAA;AAAA,MACN,WAAA,EAAa,8BAAA;AAAA,MACb,UAAA,EAAY;AAAA,QACV,SAAA,EAAW,EAAE,IAAA,EAAM,QAAA,EAAS;AAAA,QAC5B,WAAA,EAAa,EAAE,IAAA,EAAM,QAAA,EAAS;AAAA,QAC9B,cAAA,EAAgB,EAAE,IAAA,EAAM,QAAA,EAAS;AAAA,QACjC,WAAA,EAAa;AAAA,UACX,IAAA,EAAM,QAAA;AAAA,UACN,OAAA,EAAS,CAAA;AAAA,UACT,OAAA,EAAS,GAAA;AAAA,UACT,WAAA,EAAa;AAAA;AACf;AACF,KACF;AAAA,IACA,MAAA,EAAQ;AAAA,MACN,IAAA,EAAM,OAAA;AAAA,MACN,WAAA,EAAa,gDAAA;AAAA,MACb,KAAA,EAAO;AAAA,QACL,IAAA,EAAM,QAAA;AAAA,QACN,UAAA,EAAY;AAAA,UACV,QAAA,EAAU;AAAA,YACR,IAAA,EAAM,QAAA;AAAA,YACN,IAAA,EAAM,CAAC,UAAA,EAAY,OAAA,EAAS,WAAW,MAAM;AAAA,WAC/C;AAAA,UACA,QAAA,EAAU;AAAA,YACR,IAAA,EAAM,QAAA;AAAA,YACN,IAAA,EAAM;AAAA,cACJ,UAAA;AAAA,cACA,UAAA;AAAA,cACA,SAAA;AAAA,cACA,WAAA;AAAA,cACA,QAAA;AAAA,cACA,aAAA;AAAA,cACA,iBAAA;AAAA,cACA;AAAA;AACF,WACF;AAAA,UACA,KAAA,EAAO,EAAE,IAAA,EAAM,QAAA,EAAS;AAAA,UACxB,WAAA,EAAa,EAAE,IAAA,EAAM,QAAA,EAAS;AAAA,UAC9B,cAAA,EAAgB,EAAE,IAAA,EAAM,QAAA,EAAS;AAAA,UACjC,GAAA,EAAK,EAAE,IAAA,EAAM,QAAA;AAAS;AACxB;AACF,KACF;AAAA,IACA,eAAA,EAAiB;AAAA,MACf,IAAA,EAAM,OAAA;AAAA,MACN,WAAA,EAAa,0BAAA;AAAA,MACb,KAAA,EAAO;AAAA,QACL,IAAA,EAAM,QAAA;AAAA,QACN,UAAA,EAAY;AAAA,UACV,UAAU,EAAE,IAAA,EAAM,UAAU,OAAA,EAAS,CAAA,EAAG,SAAS,CAAA,EAAE;AAAA,UACnD,KAAA,EAAO,EAAE,IAAA,EAAM,QAAA,EAAS;AAAA,UACxB,YAAA,EAAc,EAAE,IAAA,EAAM,OAAA,EAAS,OAAO,EAAE,IAAA,EAAM,UAAS,EAAE;AAAA,UACzD,WAAA,EAAa,EAAE,IAAA,EAAM,OAAA,EAAS,OAAO,EAAE,IAAA,EAAM,UAAS;AAAE;AAC1D;AACF;AACF;AAEJ;;;AC/SO,SAAS,sBAAA,CACd,MAAA,EACA,OAAA,GAAiC,EAAC,EAC1B;AACR,EAAA,MAAM,EAAE,gBAAA,GAAmB,IAAA,EAAM,cAAc,IAAA,EAAM,eAAA,GAAkB,IAAG,GAAI,OAAA;AAE9E,EAAA,MAAM,QAAkB,EAAC;AAGzB,EAAA,KAAA,CAAM,KAAK,CAAA,qBAAA,CAAuB,CAAA;AAClC,EAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AACb,EAAA,KAAA,CAAM,IAAA,CAAK,CAAA,UAAA,EAAa,MAAA,CAAO,OAAO,CAAA,CAAE,CAAA;AACxC,EAAA,KAAA,CAAM,IAAA,CAAK,kBAAkB,IAAI,IAAA,CAAK,OAAO,WAAW,CAAA,CAAE,cAAA,EAAgB,CAAA,CAAE,CAAA;AAC5E,EAAA,KAAA,CAAM,IAAA,CAAK,CAAA,eAAA,EAAkB,MAAA,CAAO,EAAE,CAAA,CAAE,CAAA;AACxC,EAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AAGb,EAAA,KAAA,CAAM,KAAK,YAAY,CAAA;AACvB,EAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AACb,EAAA,KAAA,CAAM,KAAK,CAAA,kBAAA,CAAoB,CAAA;AAC/B,EAAA,KAAA,CAAM,KAAK,CAAA,kBAAA,CAAoB,CAAA;AAC/B,EAAA,KAAA,CAAM,IAAA,CAAK,CAAA,iBAAA,EAAoB,mBAAA,CAAoB,MAAA,CAAO,OAAA,CAAQ,WAAW,CAAC,CAAA,GAAA,EAAM,MAAA,CAAO,OAAA,CAAQ,WAAW,CAAA,QAAA,CAAU,CAAA;AACxH,EAAA,KAAA,CAAM,IAAA,CAAK,CAAA,eAAA,EAAkB,MAAA,CAAO,OAAA,CAAQ,SAAS,CAAA,EAAA,CAAI,CAAA;AACzD,EAAA,KAAA,CAAM,IAAA,CAAK,CAAA,iBAAA,EAAoB,MAAA,CAAO,OAAA,CAAQ,WAAW,CAAA,EAAA,CAAI,CAAA;AAC7D,EAAA,KAAA,CAAM,IAAA,CAAK,CAAA,gBAAA,EAAmB,MAAA,CAAO,OAAA,CAAQ,cAAc,CAAA,EAAA,CAAI,CAAA;AAC/D,EAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AAGb,EAAA,KAAA,CAAM,KAAK,wBAAwB,CAAA;AACnC,EAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AACb,EAAA,MAAM,UAAA,GAA8B,CAAC,UAAA,EAAY,OAAA,EAAS,WAAW,MAAM,CAAA;AAC3E,EAAA,KAAA,MAAW,YAAY,UAAA,EAAY;AACjC,IAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,OAAA,CAAQ,gBAAA,CAAiB,QAAQ,CAAA,IAAK,CAAA;AAC3D,IAAA,KAAA,CAAM,IAAA,CAAK,CAAA,EAAA,EAAK,gBAAA,CAAiB,QAAQ,CAAC,CAAA,GAAA,EAAM,UAAA,CAAW,QAAQ,CAAC,CAAA,IAAA,EAAO,KAAK,CAAA,CAAE,CAAA;AAAA,EACpF;AACA,EAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AAGb,EAAA,KAAA,CAAM,KAAK,wBAAwB,CAAA;AACnC,EAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AACb,EAAA,MAAM,aAAa,MAAA,CAAO,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,gBAAgB,CAAA,CAAE,IAAA;AAAA,IACjE,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,CAAA,KAAM,CAAA,GAAI;AAAA,GACxB;AACA,EAAA,KAAA,MAAW,CAAC,QAAA,EAAU,KAAK,CAAA,IAAK,UAAA,EAAY;AAC1C,IAAA,KAAA,CAAM,IAAA,CAAK,CAAA,EAAA,EAAK,gBAAA,CAAiB,QAAyB,CAAC,CAAA,GAAA,EAAM,cAAA,CAAe,QAAQ,CAAC,CAAA,IAAA,EAAO,KAAK,CAAA,CAAE,CAAA;AAAA,EACzG;AACA,EAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AAGb,EAAA,KAAA,CAAM,KAAK,gCAAgC,CAAA;AAC3C,EAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AAEb,EAAA,KAAA,MAAW,GAAA,IAAO,OAAO,eAAA,EAAiB;AACxC,IAAA,KAAA,CAAM,IAAA,CAAK,CAAA,IAAA,EAAO,gBAAA,CAAiB,GAAA,CAAI,QAAQ,CAAC,CAAA,UAAA,EAAa,GAAA,CAAI,QAAQ,CAAA,EAAA,EAAK,GAAA,CAAI,KAAK,CAAA,CAAE,CAAA;AACzF,IAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AACb,IAAA,KAAA,CAAM,KAAK,CAAA,cAAA,EAAiB,cAAA,CAAe,GAAA,CAAI,QAAQ,CAAC,CAAA,CAAE,CAAA;AAC1D,IAAA,KAAA,CAAM,KAAK,CAAA,YAAA,EAAe,UAAA,CAAW,GAAA,CAAI,eAAe,CAAC,CAAA,CAAE,CAAA;AAC3D,IAAA,KAAA,CAAM,IAAA,CAAK,CAAA,mBAAA,EAAsB,GAAA,CAAI,YAAA,CAAa,MAAM,CAAA,CAAE,CAAA;AAC1D,IAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AACb,IAAA,KAAA,CAAM,IAAA,CAAK,CAAA,EAAG,GAAA,CAAI,WAAW,CAAA,CAAE,CAAA;AAC/B,IAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AACb,IAAA,KAAA,CAAM,KAAK,mBAAmB,CAAA;AAC9B,IAAA,KAAA,MAAW,MAAA,IAAU,IAAI,WAAA,EAAa;AACpC,MAAA,KAAA,CAAM,IAAA,CAAK,CAAA,EAAA,EAAK,MAAM,CAAA,CAAE,CAAA;AAAA,IAC1B;AACA,IAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AAEb,IAAA,IAAI,WAAA,IAAe,GAAA,CAAI,YAAA,CAAa,MAAA,GAAS,CAAA,EAAG;AAC9C,MAAA,MAAM,UAAA,GAAa,GAAA,CAAI,YAAA,CAAa,KAAA,CAAM,GAAG,eAAe,CAAA;AAC5D,MAAA,KAAA,CAAM,KAAK,WAAW,CAAA;AACtB,MAAA,KAAA,CAAM,IAAA,CAAK,CAAA,wBAAA,EAA2B,GAAA,CAAI,YAAA,CAAa,MAAM,CAAA,WAAA,CAAa,CAAA;AAC1E,MAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AACb,MAAA,KAAA,MAAW,OAAO,UAAA,EAAY;AAC5B,QAAA,KAAA,CAAM,IAAA,CAAK,CAAA,EAAA,EAAK,GAAG,CAAA,CAAE,CAAA;AAAA,MACvB;AACA,MAAA,IAAI,GAAA,CAAI,YAAA,CAAa,MAAA,GAAS,eAAA,EAAiB;AAC7C,QAAA,KAAA,CAAM,KAAK,CAAA,UAAA,EAAa,GAAA,CAAI,YAAA,CAAa,MAAA,GAAS,eAAe,CAAA,KAAA,CAAO,CAAA;AAAA,MAC1E;AACA,MAAA,KAAA,CAAM,KAAK,YAAY,CAAA;AACvB,MAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AAAA,IACf;AAEA,IAAA,KAAA,CAAM,KAAK,KAAK,CAAA;AAChB,IAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AAAA,EACf;AAGA,EAAA,IAAI,gBAAA,EAAkB;AACpB,IAAA,KAAA,CAAM,KAAK,eAAe,CAAA;AAC1B,IAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AAEb,IAAA,MAAM,gBAAA,GAAmB,OAAA,CAAQ,MAAA,CAAO,MAAA,EAAQ,UAAU,CAAA;AAE1D,IAAA,KAAA,MAAW,CAAC,QAAA,EAAU,MAAM,KAAK,MAAA,CAAO,OAAA,CAAQ,gBAAgB,CAAA,EAAG;AACjE,MAAA,KAAA,CAAM,IAAA,CAAK,OAAO,gBAAA,CAAiB,QAAyB,CAAC,CAAA,CAAA,EAAI,cAAA,CAAe,QAAQ,CAAC,CAAA,CAAE,CAAA;AAC3F,MAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AAEb,MAAA,KAAA,MAAW,SAAS,MAAA,EAAQ;AAC1B,QAAA,KAAA,CAAM,IAAA;AAAA,UACJ,QAAQ,gBAAA,CAAiB,KAAA,CAAM,QAAQ,CAAC,CAAA,CAAA,EAAI,MAAM,KAAK,CAAA;AAAA,SACzD;AACA,QAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AACb,QAAA,KAAA,CAAM,IAAA,CAAK,CAAA,WAAA,EAAc,KAAA,CAAM,GAAG,CAAA,EAAA,CAAI,CAAA;AACtC,QAAA,KAAA,CAAM,KAAK,CAAA,cAAA,EAAiB,UAAA,CAAW,KAAA,CAAM,QAAQ,CAAC,CAAA,CAAE,CAAA;AACxD,QAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AACb,QAAA,KAAA,CAAM,IAAA,CAAK,MAAM,WAAW,CAAA;AAC5B,QAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AACb,QAAA,KAAA,CAAM,IAAA,CAAK,CAAA,oBAAA,EAAuB,KAAA,CAAM,cAAc,CAAA,CAAE,CAAA;AACxD,QAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AAAA,MACf;AAAA,IACF;AAAA,EACF;AAGA,EAAA,KAAA,CAAM,KAAK,KAAK,CAAA;AAChB,EAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AACb,EAAA,KAAA,CAAM,KAAK,+DAA+D,CAAA;AAC1E,EAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AACb,EAAA,KAAA,CAAM,KAAK,4FAA4F,CAAA;AAEvG,EAAA,OAAO,KAAA,CAAM,KAAK,IAAI,CAAA;AACxB;AAKO,SAAS,kBAAkB,MAAA,EAA2B;AAC3D,EAAA,MAAM,QAAkB,EAAC;AAEzB,EAAA,KAAA,CAAM,KAAK,wCAAwC,CAAA;AACnD,EAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AACb,EAAA,KAAA,CAAM,KAAK,YAAY,CAAA;AACvB,EAAA,KAAA,CAAM,IAAA,CAAK,CAAA,MAAA,EAAS,MAAA,CAAO,OAAO,CAAA,CAAE,CAAA;AACpC,EAAA,KAAA,CAAM,IAAA,CAAK,CAAA,cAAA,EAAiB,MAAA,CAAO,OAAA,CAAQ,WAAW,CAAA,IAAA,CAAM,CAAA;AAC5D,EAAA,KAAA,CAAM,KAAK,CAAA,iBAAA,EAAoB,MAAA,CAAO,QAAQ,gBAAA,CAAiB,QAAA,IAAY,CAAC,CAAA,CAAE,CAAA;AAC9E,EAAA,KAAA,CAAM,KAAK,CAAA,QAAA,EAAW,MAAA,CAAO,QAAQ,gBAAA,CAAiB,KAAA,IAAS,CAAC,CAAA,CAAE,CAAA;AAClE,EAAA,KAAA,CAAM,KAAK,CAAA,UAAA,EAAa,MAAA,CAAO,QAAQ,gBAAA,CAAiB,OAAA,IAAW,CAAC,CAAA,CAAE,CAAA;AACtE,EAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AACb,EAAA,KAAA,CAAM,KAAK,yBAAyB,CAAA;AACpC,EAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AAEb,EAAA,MAAM,kBAAA,GAAqB,MAAA,CAAO,eAAA,CAAgB,KAAA,CAAM,GAAG,CAAC,CAAA;AAC5D,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,kBAAA,CAAmB,QAAQ,CAAA,EAAA,EAAK;AAClD,IAAA,MAAM,GAAA,GAAM,mBAAmB,CAAC,CAAA;AAChC,IAAA,IAAI,CAAC,GAAA,EAAK;AACV,IAAA,KAAA,CAAM,IAAA,CAAK,CAAA,EAAG,CAAA,GAAI,CAAC,CAAA,IAAA,EAAO,GAAA,CAAI,KAAK,CAAA,IAAA,EAAO,GAAA,CAAI,YAAA,CAAa,MAAM,CAAA,MAAA,CAAQ,CAAA;AACzE,IAAA,KAAA,CAAM,KAAK,CAAA,KAAA,EAAQ,GAAA,CAAI,WAAA,CAAY,CAAC,CAAC,CAAA,CAAE,CAAA;AAAA,EACzC;AAEA,EAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AACb,EAAA,KAAA,CAAM,KAAK,qBAAqB,CAAA;AAChC,EAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AAEb,EAAA,MAAM,mBAAmB,MAAA,CAAO,OAAA,CAAQ,OAAO,OAAA,CAAQ,gBAAgB,EACpE,IAAA,CAAK,CAAC,GAAG,CAAC,CAAA,EAAG,GAAG,CAAC,CAAA,KAAM,IAAI,CAAC,CAAA;AAE/B,EAAA,KAAA,MAAW,CAAC,QAAA,EAAU,KAAK,CAAA,IAAK,gBAAA,EAAkB;AAChD,IAAA,KAAA,CAAM,KAAK,CAAA,EAAA,EAAK,cAAA,CAAe,QAAQ,CAAC,CAAA,EAAA,EAAK,KAAK,CAAA,OAAA,CAAS,CAAA;AAAA,EAC7D;AAEA,EAAA,OAAO,KAAA,CAAM,KAAK,IAAI,CAAA;AACxB;AAGA,SAAS,iBAAiB,QAAA,EAAiC;AACzD,EAAA,MAAM,MAAA,GAAwC;AAAA,IAC5C,QAAA,EAAU,WAAA;AAAA,IACV,KAAA,EAAO,WAAA;AAAA,IACP,OAAA,EAAS,WAAA;AAAA,IACT,IAAA,EAAM;AAAA,GACR;AACA,EAAA,OAAO,OAAO,QAAQ,CAAA;AACxB;AAEA,SAAS,iBAAiB,QAAA,EAAiC;AACzD,EAAA,MAAM,MAAA,GAAwC;AAAA,IAC5C,QAAA,EAAU,WAAA;AAAA,IACV,QAAA,EAAU,iBAAA;AAAA,IACV,OAAA,EAAS,WAAA;AAAA,IACT,SAAA,EAAW,cAAA;AAAA,IACX,MAAA,EAAQ,WAAA;AAAA,IACR,WAAA,EAAa,QAAA;AAAA,IACb,iBAAA,EAAmB,iBAAA;AAAA,IACnB,QAAA,EAAU;AAAA,GACZ;AACA,EAAA,OAAO,MAAA,CAAO,QAAQ,CAAA,IAAK,WAAA;AAC7B;AAEA,SAAS,iBAAiB,QAAA,EAA0B;AAClD,EAAA,MAAM,MAAA,GAAiC;AAAA,IACrC,CAAA,EAAG,WAAA;AAAA,IACH,CAAA,EAAG,cAAA;AAAA,IACH,CAAA,EAAG,WAAA;AAAA,IACH,CAAA,EAAG,WAAA;AAAA,IACH,CAAA,EAAG;AAAA,GACL;AACA,EAAA,OAAO,MAAA,CAAO,QAAQ,CAAA,IAAK,WAAA;AAC7B;AAEA,SAAS,oBAAoB,KAAA,EAAuB;AAClD,EAAA,IAAI,KAAA,IAAS,IAAI,OAAO,WAAA;AACxB,EAAA,IAAI,KAAA,IAAS,IAAI,OAAO,WAAA;AACxB,EAAA,IAAI,KAAA,IAAS,IAAI,OAAO,WAAA;AACxB,EAAA,OAAO,WAAA;AACT;AAEA,SAAS,WAAW,GAAA,EAAqB;AACvC,EAAA,OAAO,GAAA,CAAI,OAAO,CAAC,CAAA,CAAE,aAAY,GAAI,GAAA,CAAI,MAAM,CAAC,CAAA;AAClD;AAEA,SAAS,eAAe,QAAA,EAA0B;AAChD,EAAA,OAAO,QAAA,CACJ,MAAM,GAAG,CAAA,CACT,IAAI,UAAU,CAAA,CACd,KAAK,GAAG,CAAA;AACb;AAEA,SAAS,OAAA,CAAW,OAAY,GAAA,EAAmC;AACjE,EAAA,OAAO,KAAA,CAAM,MAAA;AAAA,IACX,CAAC,KAAK,IAAA,KAAS;AACb,MAAA,MAAM,QAAA,GAAW,MAAA,CAAO,IAAA,CAAK,GAAG,CAAC,CAAA;AACjC,MAAA,IAAI,CAAC,GAAA,CAAI,QAAQ,CAAA,EAAG;AAClB,QAAA,GAAA,CAAI,QAAQ,IAAI,EAAC;AAAA,MACnB;AACA,MAAA,GAAA,CAAI,QAAQ,CAAA,CAAE,IAAA,CAAK,IAAI,CAAA;AACvB,MAAA,OAAO,GAAA;AAAA,IACT,CAAA;AAAA,IACA;AAAC,GACH;AACF;AC5OA,IAAM,SAAA,GAAY,GAAA;AAiBX,SAAS,oBAAA,CACd,QACA,OAAA,EACmB;AACnB,EAAA,MAAM,EAAE,SAAA,EAAW,cAAA,GAAiB,IAAA,EAAK,GAAI,OAAA;AAG7C,EAAA,IAAI,cAAA,IAAkB,UAAA,CAAW,SAAS,CAAA,EAAG;AAC3C,IAAA,MAAM,KAAA,GAAQ,YAAY,SAAS,CAAA;AACnC,IAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACxB,MAAA,IAAI,KAAK,UAAA,CAAW,MAAM,KAAK,IAAA,CAAK,QAAA,CAAS,KAAK,CAAA,EAAG;AACnD,QAAA,MAAA,CAAO,KAAK,SAAA,EAAW,IAAI,GAAG,EAAE,KAAA,EAAO,MAAM,CAAA;AAAA,MAC/C;AAAA,IACF;AAAA,EACF;AAGA,EAAA,IAAI,CAAC,UAAA,CAAW,SAAS,CAAA,EAAG;AAC1B,IAAA,SAAA,CAAU,SAAA,EAAW,EAAE,SAAA,EAAW,IAAA,EAAM,CAAA;AAAA,EAC1C;AAEA,EAAA,MAAM,SAAA,GAAA,iBAAY,IAAI,IAAA,EAAK,EAAE,WAAA,EAAY,CAAE,OAAA,CAAQ,OAAA,EAAS,GAAG,CAAA,CAAE,KAAA,CAAM,CAAA,EAAG,EAAE,CAAA;AAC5E,EAAA,MAAM,QAAA,GAAW,IAAI,GAAA,CAAI,MAAA,CAAO,OAAO,CAAA,CAAE,QAAA,CAAS,OAAA,CAAQ,KAAA,EAAO,GAAG,CAAA;AACpE,EAAA,MAAM,MAAA,GAAS,CAAA,IAAA,EAAO,QAAQ,CAAA,CAAA,EAAI,SAAS,CAAA,CAAA;AAE3C,EAAA,MAAM,gBAA0B,EAAC;AAGjC,EAAA,MAAM,gBAAA,GAAmB,qBAAA,CAAsB,MAAA,CAAO,MAAM,CAAA;AAC5D,EAAA,MAAM,UAAA,GAAa,MAAA,CAAO,IAAA,CAAK,gBAAgB,CAAA;AAG/C,EAAA,KAAA,MAAW,YAAY,UAAA,EAAY;AACjC,IAAA,MAAM,MAAA,GAAS,gBAAA,CAAiB,QAAQ,CAAA,IAAK,EAAC;AAC9C,IAAA,IAAI,MAAA,CAAO,WAAW,CAAA,EAAG;AAEzB,IAAA,MAAM,MAAA,GAAS,eAAA,CAAgB,MAAgB,CAAA;AAE/C,IAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,MAAA,CAAO,QAAQ,CAAA,EAAA,EAAK;AACtC,MAAA,MAAM,SAAS,MAAA,CAAO,MAAA,GAAS,IAAI,CAAA,CAAA,EAAI,CAAA,GAAI,CAAC,CAAA,CAAA,GAAK,EAAA;AACjD,MAAA,MAAM,WAAW,CAAA,EAAG,MAAM,CAAA,CAAA,EAAI,QAAQ,GAAG,MAAM,CAAA,GAAA,CAAA;AAC/C,MAAA,MAAM,QAAA,GAAW,IAAA,CAAK,SAAA,EAAW,QAAQ,CAAA;AAEzC,MAAA,MAAM,KAAA,GAAQ,OAAO,CAAC,CAAA;AACtB,MAAA,IAAI,CAAC,KAAA,EAAO;AACZ,MAAA,MAAM,OAAA,GAAU,oBAAA,CAAqB,MAAA,CAAO,OAAA,EAAS,UAAU,KAAA,EAAO;AAAA,QACpE,IAAA,EAAM,MAAA,CAAO,MAAA,GAAS,CAAA,GAAI,IAAI,CAAA,GAAI,MAAA;AAAA,QAClC,UAAA,EAAY,MAAA,CAAO,MAAA,GAAS,CAAA,GAAI,OAAO,MAAA,GAAS;AAAA,OACjD,CAAA;AAED,MAAA,aAAA,CAAc,QAAA,EAAU,SAAS,OAAO,CAAA;AACxC,MAAA,aAAA,CAAc,KAAK,QAAQ,CAAA;AAAA,IAC7B;AAAA,EACF;AAGA,EAAA,MAAM,aAAA,GAAgB,GAAG,MAAM,CAAA,SAAA,CAAA;AAC/B,EAAA,MAAM,aAAA,GAAgB,IAAA,CAAK,SAAA,EAAW,aAAa,CAAA;AACnD,EAAA,MAAM,YAAA,GAAe,iBAAA,CAAkB,MAAA,EAAQ,aAAa,CAAA;AAC5D,EAAA,aAAA,CAAc,aAAA,EAAe,cAAc,OAAO,CAAA;AAElD,EAAA,OAAO;AAAA,IACL,SAAA,EAAW,aAAA;AAAA,IACX,aAAA;AAAA,IACA,UAAA,EAAY,cAAc,MAAA,GAAS;AAAA,GACrC;AACF;AAKA,SAAS,iBAAA,CAAkB,QAAmB,aAAA,EAAiC;AAC7E,EAAA,MAAM,QAAkB,EAAC;AAEzB,EAAA,KAAA,CAAM,KAAK,oBAAoB,CAAA;AAC/B,EAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AACb,EAAA,KAAA,CAAM,IAAA,CAAK,CAAA,MAAA,EAAS,MAAA,CAAO,OAAO,CAAA,CAAE,CAAA;AACpC,EAAA,KAAA,CAAM,IAAA,CAAK,CAAA,OAAA,EAAU,MAAA,CAAO,OAAA,CAAQ,WAAW,CAAA,IAAA,CAAM,CAAA;AACrD,EAAA,KAAA,CAAM,IAAA,CAAK,SAAS,MAAA,CAAO,WAAA,CAAY,MAAM,CAAA,EAAG,EAAE,CAAC,CAAA,CAAE,CAAA;AACrD,EAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AAGb,EAAA,KAAA,CAAM,KAAK,WAAW,CAAA;AACtB,EAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AACb,EAAA,KAAA,CAAM,KAAK,sBAAsB,CAAA;AACjC,EAAA,KAAA,CAAM,KAAK,sBAAsB,CAAA;AACjC,EAAA,MAAM,UAAA,GAA8B,CAAC,UAAA,EAAY,OAAA,EAAS,WAAW,MAAM,CAAA;AAC3E,EAAA,KAAA,MAAW,OAAO,UAAA,EAAY;AAC5B,IAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,OAAA,CAAQ,gBAAA,CAAiB,GAAG,CAAA,IAAK,CAAA;AACtD,IAAA,IAAI,QAAQ,CAAA,EAAG;AACb,MAAA,KAAA,CAAM,IAAA,CAAK,CAAA,EAAA,EAAK,GAAG,CAAA,GAAA,EAAM,KAAK,CAAA,EAAA,CAAI,CAAA;AAAA,IACpC;AAAA,EACF;AACA,EAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AAGb,EAAA,KAAA,CAAM,KAAK,YAAY,CAAA;AACvB,EAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AACb,EAAA,MAAM,OAAA,GAAU,MAAA,CAAO,eAAA,CAAgB,KAAA,CAAM,GAAG,EAAE,CAAA;AAClD,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,OAAA,CAAQ,QAAQ,CAAA,EAAA,EAAK;AACvC,IAAA,MAAM,GAAA,GAAM,QAAQ,CAAC,CAAA;AACrB,IAAA,IAAI,CAAC,GAAA,EAAK;AACV,IAAA,KAAA,CAAM,IAAA,CAAK,CAAA,EAAG,CAAA,GAAI,CAAC,CAAA,IAAA,EAAO,GAAA,CAAI,KAAK,CAAA,IAAA,EAAO,GAAA,CAAI,YAAA,CAAa,MAAM,CAAA,CAAA,CAAG,CAAA;AACpE,IAAA,KAAA,CAAM,KAAK,CAAA,GAAA,EAAM,GAAA,CAAI,WAAA,CAAY,CAAC,CAAC,CAAA,CAAE,CAAA;AAAA,EACvC;AACA,EAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AAGb,EAAA,KAAA,CAAM,KAAK,UAAU,CAAA;AACrB,EAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AACb,EAAA,KAAA,MAAW,QAAQ,aAAA,EAAe;AAChC,IAAA,KAAA,CAAM,IAAA,CAAK,CAAA,GAAA,EAAM,IAAI,CAAA,IAAA,EAAO,IAAI,CAAA,CAAA,CAAG,CAAA;AAAA,EACrC;AAEA,EAAA,OAAO,KAAA,CAAM,KAAK,IAAI,CAAA;AACxB;AAKA,SAAS,oBAAA,CACP,OAAA,EACA,QAAA,EACA,MAAA,EACA,IAAA,EACQ;AACR,EAAA,MAAM,QAAkB,EAAC;AACzB,EAAA,MAAM,OAAA,GAAU,KAAK,IAAA,GAAO,CAAA,OAAA,EAAU,KAAK,IAAI,CAAA,CAAA,EAAI,IAAA,CAAK,UAAU,CAAA,CAAA,CAAA,GAAM,EAAA;AAExE,EAAA,KAAA,CAAM,KAAK,CAAA,EAAA,EAAKA,eAAAA,CAAe,QAAQ,CAAC,CAAA,EAAG,OAAO,CAAA,CAAE,CAAA;AACpD,EAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AACb,EAAA,KAAA,CAAM,IAAA,CAAK,CAAA,MAAA,EAAS,OAAO,CAAA,CAAE,CAAA;AAC7B,EAAA,KAAA,CAAM,IAAA,CAAK,CAAA,QAAA,EAAW,MAAA,CAAO,MAAM,CAAA,CAAE,CAAA;AACrC,EAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AAGb,EAAA,MAAM,OAAA,uBAAc,GAAA,EAAwB;AAC5C,EAAA,KAAA,MAAW,SAAS,MAAA,EAAQ;AAC1B,IAAA,MAAM,QAAQ,OAAA,CAAQ,GAAA,CAAI,KAAA,CAAM,KAAK,KAAK,EAAC;AAC3C,IAAA,KAAA,CAAM,KAAK,KAAK,CAAA;AAChB,IAAA,OAAA,CAAQ,GAAA,CAAI,KAAA,CAAM,KAAA,EAAO,KAAK,CAAA;AAAA,EAChC;AAEA,EAAA,KAAA,MAAW,CAAC,KAAA,EAAO,WAAW,CAAA,IAAK,OAAA,EAAS;AAC1C,IAAA,MAAM,KAAA,GAAQ,YAAY,CAAC,CAAA;AAC3B,IAAA,IAAI,CAAC,KAAA,EAAO;AAEZ,IAAA,KAAA,CAAM,IAAA,CAAK,CAAA,GAAA,EAAM,KAAK,CAAA,CAAE,CAAA;AACxB,IAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AACb,IAAA,KAAA,CAAM,IAAA,CAAK,CAAA,UAAA,EAAa,KAAA,CAAM,QAAQ,CAAA,CAAE,CAAA;AACxC,IAAA,KAAA,CAAM,IAAA,CAAK,CAAA,OAAA,EAAU,WAAA,CAAY,MAAM,CAAA,CAAE,CAAA;AACzC,IAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AACb,IAAA,KAAA,CAAM,IAAA,CAAK,CAAA,EAAA,EAAK,KAAA,CAAM,cAAc,CAAA,CAAE,CAAA;AACtC,IAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AAGb,IAAA,KAAA,CAAM,KAAK,OAAO,CAAA;AAClB,IAAA,KAAA,MAAW,KAAA,IAAS,WAAA,CAAY,KAAA,CAAM,CAAA,EAAG,EAAE,CAAA,EAAG;AAC5C,MAAA,KAAA,CAAM,IAAA,CAAK,CAAA,EAAA,EAAK,KAAA,CAAM,GAAG,CAAA,CAAE,CAAA;AAAA,IAC7B;AACA,IAAA,IAAI,WAAA,CAAY,SAAS,EAAA,EAAI;AAC3B,MAAA,KAAA,CAAM,IAAA,CAAK,CAAA,OAAA,EAAU,WAAA,CAAY,MAAA,GAAS,EAAE,CAAA,KAAA,CAAO,CAAA;AAAA,IACrD;AACA,IAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AAAA,EACf;AAEA,EAAA,OAAO,KAAA,CAAM,KAAK,IAAI,CAAA;AACxB;AAKA,SAAS,eAAA,CAAgB,QAAoB,QAAA,EAAuC;AAElF,EAAA,MAAM,OAAA,uBAAc,GAAA,EAAwB;AAC5C,EAAA,KAAA,MAAW,SAAS,MAAA,EAAQ;AAC1B,IAAA,MAAM,QAAQ,OAAA,CAAQ,GAAA,CAAI,KAAA,CAAM,KAAK,KAAK,EAAC;AAC3C,IAAA,KAAA,CAAM,KAAK,KAAK,CAAA;AAChB,IAAA,OAAA,CAAQ,GAAA,CAAI,KAAA,CAAM,KAAA,EAAO,KAAK,CAAA;AAAA,EAChC;AAEA,EAAA,MAAM,SAAuB,EAAC;AAC9B,EAAA,IAAI,eAA2B,EAAC;AAChC,EAAA,IAAI,YAAA,GAAe,EAAA;AAEnB,EAAA,KAAA,MAAW,GAAG,WAAW,CAAA,IAAK,OAAA,EAAS;AAErC,IAAA,MAAM,QAAA,GAAW,IAAA,CAAK,GAAA,CAAI,EAAA,EAAI,YAAY,MAAM,CAAA;AAChD,IAAA,MAAM,aAAa,CAAA,GAAI,QAAA;AAEvB,IAAA,IAAI,YAAA,GAAe,UAAA,GAAa,SAAA,IAAa,YAAA,CAAa,SAAS,CAAA,EAAG;AACpE,MAAA,MAAA,CAAO,KAAK,YAAY,CAAA;AACxB,MAAA,YAAA,GAAe,EAAC;AAChB,MAAA,YAAA,GAAe,EAAA;AAAA,IACjB;AAEA,IAAA,YAAA,CAAa,IAAA,CAAK,GAAG,WAAW,CAAA;AAChC,IAAA,YAAA,IAAgB,UAAA;AAAA,EAClB;AAEA,EAAA,IAAI,YAAA,CAAa,SAAS,CAAA,EAAG;AAC3B,IAAA,MAAA,CAAO,KAAK,YAAY,CAAA;AAAA,EAC1B;AAEA,EAAA,OAAO,OAAO,MAAA,GAAS,CAAA,GAAI,MAAA,GAAS,CAAC,EAAE,CAAA;AACzC;AAEA,SAAS,sBAAsB,MAAA,EAAuD;AACpF,EAAA,MAAM,SAAqD,EAAC;AAE5D,EAAA,KAAA,MAAW,SAAS,MAAA,EAAQ;AAC1B,IAAA,IAAI,CAAC,MAAA,CAAO,KAAA,CAAM,QAAQ,CAAA,EAAG;AAC3B,MAAA,MAAA,CAAO,KAAA,CAAM,QAAQ,CAAA,GAAI,EAAC;AAAA,IAC5B;AACA,IAAA,MAAA,CAAO,KAAA,CAAM,QAAQ,CAAA,CAAG,IAAA,CAAK,KAAK,CAAA;AAAA,EACpC;AAEA,EAAA,OAAO,MAAA;AACT;AAEA,SAASA,gBAAe,QAAA,EAA0B;AAChD,EAAA,OAAO,SACJ,KAAA,CAAM,GAAG,EACT,GAAA,CAAI,CAAA,CAAA,KAAK,EAAE,MAAA,CAAO,CAAC,CAAA,CAAE,WAAA,KAAgB,CAAA,CAAE,KAAA,CAAM,CAAC,CAAC,CAAA,CAC/C,KAAK,GAAG,CAAA;AACb;;;ACjPO,SAAS,sBAAsB,MAAA,EAA2B;AAC/D,EAAA,MAAM,QAAkB,EAAC;AAGzB,EAAA,KAAA,CAAM,KAAK,kBAAkB,CAAA;AAC7B,EAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AACb,EAAA,KAAA,CAAM,KAAK,gFAAgF,CAAA;AAC3F,EAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AACb,EAAA,KAAA,CAAM,KAAK,aAAa,CAAA;AACxB,EAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AACb,EAAA,KAAA,CAAM,KAAK,SAAS,CAAA;AACpB,EAAA,KAAA,CAAM,KAAK,wCAAwC,CAAA;AACnD,EAAA,KAAA,CAAM,KAAK,gEAAgE,CAAA;AAC3E,EAAA,KAAA,CAAM,KAAK,qDAAqD,CAAA;AAChE,EAAA,KAAA,CAAM,KAAK,iDAAiD,CAAA;AAC5D,EAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AACb,EAAA,KAAA,CAAM,KAAK,4CAA4C,CAAA;AACvD,EAAA,KAAA,CAAM,KAAK,4DAA4D,CAAA;AACvE,EAAA,KAAA,CAAM,KAAK,+DAA+D,CAAA;AAC1E,EAAA,KAAA,CAAM,KAAK,yDAAyD,CAAA;AACpE,EAAA,KAAA,CAAM,KAAK,gEAAgE,CAAA;AAC3E,EAAA,KAAA,CAAM,KAAK,KAAK,CAAA;AAChB,EAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AACb,EAAA,KAAA,CAAM,KAAK,YAAY,CAAA;AACvB,EAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AACb,EAAA,KAAA,CAAM,KAAK,uCAAuC,CAAA;AAClD,EAAA,KAAA,CAAM,KAAK,2CAA2C,CAAA;AACtD,EAAA,KAAA,CAAM,KAAK,qCAAqC,CAAA;AAChD,EAAA,KAAA,CAAM,KAAK,qEAAqE,CAAA;AAChF,EAAA,KAAA,CAAM,KAAK,4CAA4C,CAAA;AACvD,EAAA,KAAA,CAAM,KAAK,0DAA0D,CAAA;AACrE,EAAA,KAAA,CAAM,KAAK,2DAA2D,CAAA;AACtE,EAAA,KAAA,CAAM,KAAK,2DAA2D,CAAA;AACtE,EAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AACb,EAAA,KAAA,CAAM,KAAK,YAAY,CAAA;AACvB,EAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AACb,EAAA,KAAA,CAAM,KAAK,oDAAoD,CAAA;AAC/D,EAAA,KAAA,CAAM,KAAK,uDAAuD,CAAA;AAClE,EAAA,KAAA,CAAM,KAAK,+CAA+C,CAAA;AAC1D,EAAA,KAAA,CAAM,KAAK,mDAAmD,CAAA;AAC9D,EAAA,KAAA,CAAM,KAAK,0CAA0C,CAAA;AACrD,EAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AAGb,EAAA,KAAA,CAAM,KAAK,mBAAmB,CAAA;AAC9B,EAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AACb,EAAA,KAAA,CAAM,KAAK,oDAAoD,CAAA;AAC/D,EAAA,KAAA,CAAM,KAAK,4CAA4C,CAAA;AACvD,EAAA,KAAA,CAAM,KAAK,mDAAmD,CAAA;AAC9D,EAAA,KAAA,CAAM,KAAK,4CAA4C,CAAA;AACvD,EAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AACb,EAAA,KAAA,CAAM,KAAK,qBAAqB,CAAA;AAChC,EAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AACb,EAAA,KAAA,CAAM,KAAK,qDAAqD,CAAA;AAChE,EAAA,KAAA,CAAM,KAAK,qDAAqD,CAAA;AAChE,EAAA,KAAA,CAAM,KAAK,qDAAqD,CAAA;AAChE,EAAA,KAAA,CAAM,KAAK,8DAA8D,CAAA;AACzE,EAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AAGb,EAAA,KAAA,CAAM,KAAK,mBAAmB,CAAA;AAC9B,EAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AACb,EAAA,KAAA,CAAM,KAAK,qDAAqD,CAAA;AAChE,EAAA,KAAA,CAAM,KAAK,2CAA2C,CAAA;AACtD,EAAA,KAAA,CAAM,KAAK,uCAAuC,CAAA;AAClD,EAAA,KAAA,CAAM,KAAK,+CAA+C,CAAA;AAC1D,EAAA,KAAA,CAAM,KAAK,gCAAgC,CAAA;AAC3C,EAAA,KAAA,CAAM,KAAK,+BAA+B,CAAA;AAC1C,EAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AAGb,EAAA,KAAA,CAAM,KAAK,oBAAoB,CAAA;AAC/B,EAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AACb,EAAA,KAAA,CAAM,KAAK,oCAAoC,CAAA;AAC/C,EAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AACb,EAAA,KAAA,CAAM,KAAK,6CAA6C,CAAA;AACxD,EAAA,KAAA,CAAM,KAAK,iDAAiD,CAAA;AAC5D,EAAA,KAAA,CAAM,KAAK,mDAAmD,CAAA;AAC9D,EAAA,KAAA,CAAM,KAAK,2DAA2D,CAAA;AACtE,EAAA,KAAA,CAAM,KAAK,mDAAmD,CAAA;AAC9D,EAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AACb,EAAA,KAAA,CAAM,KAAK,sCAAsC,CAAA;AACjD,EAAA,KAAA,CAAM,KAAK,6CAA6C,CAAA;AACxD,EAAA,KAAA,CAAM,KAAK,iDAAiD,CAAA;AAC5D,EAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AACb,EAAA,KAAA,CAAM,KAAK,iCAAiC,CAAA;AAC5C,EAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AACb,EAAA,KAAA,CAAM,KAAK,yCAAyC,CAAA;AACpD,EAAA,KAAA,CAAM,KAAK,eAAe,CAAA;AAC1B,EAAA,KAAA,CAAM,KAAK,yCAAyC,CAAA;AACpD,EAAA,KAAA,CAAM,KAAK,wDAAwD,CAAA;AACnE,EAAA,KAAA,CAAM,KAAK,KAAK,CAAA;AAChB,EAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AACb,EAAA,KAAA,CAAM,KAAK,sDAAsD,CAAA;AACjE,EAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AAGb,EAAA,KAAA,CAAM,KAAK,KAAK,CAAA;AAChB,EAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AACb,EAAA,KAAA,CAAM,KAAK,kBAAkB,CAAA;AAC7B,EAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AACb,EAAA,KAAA,CAAM,IAAA,CAAK,CAAA,MAAA,EAAS,MAAA,CAAO,OAAO,CAAA,CAAE,CAAA;AACpC,EAAA,KAAA,CAAM,IAAA,CAAK,CAAA,OAAA,EAAU,MAAA,CAAO,OAAA,CAAQ,WAAW,CAAA,IAAA,CAAM,CAAA;AACrD,EAAA,KAAA,CAAM,IAAA,CAAK,SAAS,MAAA,CAAO,WAAA,CAAY,MAAM,CAAA,EAAG,EAAE,CAAC,CAAA,CAAE,CAAA;AACrD,EAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AAGb,EAAA,KAAA,CAAM,KAAK,YAAY,CAAA;AACvB,EAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AACb,EAAA,MAAM,EAAE,QAAA,GAAW,CAAA,EAAG,KAAA,GAAQ,CAAA,EAAG,OAAA,GAAU,CAAA,EAAG,IAAA,GAAO,CAAA,EAAE,GAAI,MAAA,CAAO,OAAA,CAAQ,gBAAA;AAC1E,EAAA,IAAI,WAAW,CAAA,EAAG,KAAA,CAAM,IAAA,CAAK,CAAA,YAAA,EAAe,QAAQ,CAAA,CAAE,CAAA;AACtD,EAAA,IAAI,QAAQ,CAAA,EAAG,KAAA,CAAM,IAAA,CAAK,CAAA,SAAA,EAAY,KAAK,CAAA,CAAE,CAAA;AAC7C,EAAA,IAAI,UAAU,CAAA,EAAG,KAAA,CAAM,IAAA,CAAK,CAAA,WAAA,EAAc,OAAO,CAAA,CAAE,CAAA;AACnD,EAAA,IAAI,OAAO,CAAA,EAAG,KAAA,CAAM,IAAA,CAAK,CAAA,QAAA,EAAW,IAAI,CAAA,CAAE,CAAA;AAC1C,EAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AAGb,EAAA,KAAA,CAAM,KAAK,iBAAiB,CAAA;AAC5B,EAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AACb,EAAA,MAAM,OAAA,GAAU,MAAA,CAAO,eAAA,CAAgB,KAAA,CAAM,GAAG,CAAC,CAAA;AACjD,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,OAAA,CAAQ,QAAQ,CAAA,EAAA,EAAK;AACvC,IAAA,MAAM,GAAA,GAAM,QAAQ,CAAC,CAAA;AACrB,IAAA,IAAI,CAAC,GAAA,EAAK;AACV,IAAA,KAAA,CAAM,IAAA,CAAK,CAAA,EAAG,CAAA,GAAI,CAAC,CAAA,IAAA,EAAO,GAAA,CAAI,KAAK,CAAA,IAAA,EAAO,GAAA,CAAI,YAAA,CAAa,MAAM,CAAA,MAAA,CAAQ,CAAA;AAAA,EAC3E;AACA,EAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AAGb,EAAA,KAAA,CAAM,KAAK,kBAAkB,CAAA;AAC7B,EAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AACb,EAAA,KAAA,CAAM,KAAK,sCAAsC,CAAA;AACjD,EAAA,KAAA,CAAM,KAAK,iCAAiC,CAAA;AAC5C,EAAA,KAAA,CAAM,KAAK,2CAA2C,CAAA;AACtD,EAAA,KAAA,CAAM,KAAK,uCAAuC,CAAA;AAClD,EAAA,KAAA,CAAM,KAAK,+CAA+C,CAAA;AAC1D,EAAA,KAAA,CAAM,KAAK,EAAE,CAAA;AAEb,EAAA,OAAO,KAAA,CAAM,KAAK,IAAI,CAAA;AACxB;;;ACpGA,eAAsB,sBAAA,CACpB,OAAA,EACA,IAAA,EAKA,OAAA,EAC2B;AAC3B,EAAA,MAAM;AAAA,IACJ,SAAA;AAAA,IACA,OAAA;AAAA,IACA,cAAA,GAAiB,KAAA;AAAA,IACjB,SAAA,GAAY,IAAA;AAAA,IACZ,cAAA,GAAiB,IAAA;AAAA,IACjB,eAAA,GAAkB;AAAA,GACpB,GAAI,OAAA;AAGJ,EAAA,IAAI,cAAA,IAAkBC,UAAAA,CAAW,SAAS,CAAA,EAAG;AAC3C,IAAA,IAAI;AACF,MAAA,MAAM,KAAA,GAAQC,YAAY,SAAS,CAAA;AACnC,MAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACxB,QAAA,IAAI,IAAA,CAAK,UAAA,CAAW,MAAM,CAAA,EAAG;AAC3B,UAAAC,MAAAA,CAAOC,KAAK,SAAA,EAAW,IAAI,GAAG,EAAE,KAAA,EAAO,MAAM,CAAA;AAAA,QAC/C;AAAA,MACF;AAAA,IACF,CAAA,CAAA,MAAQ;AAAA,IAER;AAAA,EACF;AAGA,EAAA,IAAI,CAACH,UAAAA,CAAW,SAAS,CAAA,EAAG;AAC1B,IAAAI,SAAAA,CAAU,SAAA,EAAW,EAAE,SAAA,EAAW,MAAM,CAAA;AAAA,EAC1C;AAGA,EAAA,MAAM,SAAS,kBAAA,CAAmB,OAAA,EAAS,MAAM,EAAE,cAAA,EAAgB,iBAAiB,CAAA;AAEpF,EAAA,MAAM,MAAA,GAA2B;AAAA,IAC/B,MAAA;AAAA,IACA,OAAO;AAAC,GACV;AAGA,EAAA,MAAM,KAAK,SAAA,GACP,CAAA,CAAA,EAAA,iBAAI,IAAI,IAAA,IAAO,WAAA,EAAY,CAAE,OAAA,CAAQ,OAAA,EAAS,GAAG,CAAA,CAAE,KAAA,CAAM,CAAA,EAAG,EAAE,CAAC,CAAA,CAAA,GAC/D,EAAA;AACJ,EAAA,MAAM,QAAA,GAAW,IAAI,GAAA,CAAI,OAAO,EAAE,QAAA,CAAS,OAAA,CAAQ,OAAO,GAAG,CAAA;AAG7D,EAAA,IAAI,OAAA,CAAQ,QAAA,CAAS,MAAM,CAAA,EAAG;AAC5B,IAAA,MAAM,QAAA,GAAW,CAAA,WAAA,EAAc,QAAQ,CAAA,EAAG,EAAE,CAAA,KAAA,CAAA;AAC5C,IAAA,MAAM,QAAA,GAAWD,IAAAA,CAAK,SAAA,EAAW,QAAQ,CAAA;AACzC,IAAA,MAAM,OAAA,GAAU,gBAAA,CAAiB,MAAA,EAAQ,IAAI,CAAA;AAC7C,IAAAE,aAAAA,CAAc,QAAA,EAAU,OAAA,EAAS,OAAO,CAAA;AACxC,IAAA,MAAA,CAAO,MAAM,IAAA,GAAO,QAAA;AACpB,IAAA,OAAA,CAAQ,OAAA,CAAQ,CAAA,mBAAA,EAAsB,QAAQ,CAAA,CAAE,CAAA;AAAA,EAClD;AAGA,EAAA,IAAI,OAAA,CAAQ,QAAA,CAAS,UAAU,CAAA,EAAG;AAChC,IAAA,MAAM,QAAA,GAAW,CAAA,WAAA,EAAc,QAAQ,CAAA,EAAG,EAAE,CAAA,GAAA,CAAA;AAC5C,IAAA,MAAM,QAAA,GAAWF,IAAAA,CAAK,SAAA,EAAW,QAAQ,CAAA;AACzC,IAAA,MAAM,OAAA,GAAU,uBAAuB,MAAA,EAAQ;AAAA,MAC7C,gBAAA,EAAkB,IAAA;AAAA,MAClB,WAAA,EAAa;AAAA,KACd,CAAA;AACD,IAAAE,aAAAA,CAAc,QAAA,EAAU,OAAA,EAAS,OAAO,CAAA;AACxC,IAAA,MAAA,CAAO,MAAM,QAAA,GAAW,QAAA;AACxB,IAAA,OAAA,CAAQ,OAAA,CAAQ,CAAA,uBAAA,EAA0B,QAAQ,CAAA,CAAE,CAAA;AAAA,EACtD;AAGA,EAAA,IAAI,OAAA,CAAQ,QAAA,CAAS,YAAY,CAAA,EAAG;AAClC,IAAA,MAAM,QAAA,GAAW,CAAA,eAAA,EAAkB,QAAQ,CAAA,EAAG,EAAE,CAAA,GAAA,CAAA;AAChD,IAAA,MAAM,QAAA,GAAWF,IAAAA,CAAK,SAAA,EAAW,QAAQ,CAAA;AACzC,IAAA,MAAM,OAAA,GAAU,kBAAkB,MAAM,CAAA;AACxC,IAAAE,aAAAA,CAAc,QAAA,EAAU,OAAA,EAAS,OAAO,CAAA;AACxC,IAAA,MAAA,CAAO,MAAM,SAAA,GAAY,QAAA;AACzB,IAAA,OAAA,CAAQ,OAAA,CAAQ,CAAA,kBAAA,EAAqB,QAAQ,CAAA,CAAE,CAAA;AAAA,EACjD;AAGA,EAAA,IAAI,OAAA,CAAQ,QAAA,CAAS,OAAO,CAAA,EAAG;AAC7B,IAAA,MAAM,WAAA,GAAc,qBAAqB,MAAA,EAAQ;AAAA,MAC/C,SAAA;AAAA,MACA,cAAA,EAAgB;AAAA;AAAA,KACjB,CAAA;AACD,IAAA,MAAA,CAAO,MAAM,KAAA,GAAQ;AAAA,MACnB,KAAA,EAAOF,IAAAA,CAAK,SAAA,EAAW,WAAA,CAAY,SAAS,CAAA;AAAA,MAC5C,UAAA,EAAY,YAAY,aAAA,CAAc,GAAA,CAAI,OAAKA,IAAAA,CAAK,SAAA,EAAW,CAAC,CAAC;AAAA,KACnE;AACA,IAAA,OAAA,CAAQ,OAAA,CAAQ,wBAAwB,WAAA,CAAY,UAAU,mBAAmB,WAAA,CAAY,aAAA,CAAc,MAAM,CAAA,YAAA,CAAc,CAAA;AAAA,EACjI;AAGA,EAAA,MAAM,aAAA,GAAgB,sBAAsB,MAAM,CAAA;AAClD,EAAA,MAAM,cAAA,GAAiBA,IAAAA,CAAK,SAAA,EAAW,WAAW,CAAA;AAClD,EAAAE,aAAAA,CAAc,cAAA,EAAgB,aAAA,EAAe,OAAO,CAAA;AACpD,EAAA,OAAA,CAAQ,OAAA,CAAQ,CAAA,kBAAA,EAAqB,cAAc,CAAA,CAAE,CAAA;AAErD,EAAA,OAAO,MAAA;AACT;AAKO,SAAS,mBAAmB,MAAA,EAAyB;AAC1D,EAAA,OAAA,CAAQ,GAAA;AAAA,IACN,CAAA,YAAA,EAAe,OAAO,OAAO;AAAA,cAAA,EACV,MAAA,CAAO,QAAQ,WAAW,CAAA;AAAA,YAAA,EAC5B,MAAA,CAAO,QAAQ,SAAS;AAAA,SAAA,EAC3B,OAAO,OAAA,CAAQ,WAAW,CAAA,gBAAA,EAAmB,MAAA,CAAO,QAAQ,cAAc;AAAA,QAAA,EAC3E,MAAA,CAAO,OAAO,MAAM,CAAA;AAAA,GACnC;AAEA,EAAA,IAAI,MAAA,CAAO,OAAA,CAAQ,gBAAA,CAAiB,QAAA,EAAU;AAC5C,IAAA,OAAA,CAAQ,MAAM,CAAA,iBAAA,EAAoB,MAAA,CAAO,OAAA,CAAQ,gBAAA,CAAiB,QAAQ,CAAA,CAAE,CAAA;AAAA,EAC9E;AACA,EAAA,IAAI,MAAA,CAAO,OAAA,CAAQ,gBAAA,CAAiB,KAAA,EAAO;AACzC,IAAA,OAAA,CAAQ,KAAK,CAAA,QAAA,EAAW,MAAA,CAAO,OAAA,CAAQ,gBAAA,CAAiB,KAAK,CAAA,CAAE,CAAA;AAAA,EACjE;AACA,EAAA,IAAI,MAAA,CAAO,OAAA,CAAQ,gBAAA,CAAiB,OAAA,EAAS;AAC3C,IAAA,OAAA,CAAQ,KAAK,CAAA,UAAA,EAAa,MAAA,CAAO,OAAA,CAAQ,gBAAA,CAAiB,OAAO,CAAA,CAAE,CAAA;AAAA,EACrE;AAEA,EAAA,OAAA,CAAQ,IAAI,EAAE,CAAA;AACd,EAAA,OAAA,CAAQ,KAAK,sBAAsB,CAAA;AACnC,EAAA,KAAA,MAAW,OAAO,MAAA,CAAO,eAAA,CAAgB,KAAA,CAAM,CAAA,EAAG,CAAC,CAAA,EAAG;AACpD,IAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,EAAA,EAAK,GAAA,CAAI,QAAQ,CAAA,EAAA,EAAK,GAAA,CAAI,KAAK,CAAA,EAAA,EAAK,GAAA,CAAI,YAAA,CAAa,MAAM,CAAA,MAAA,CAAQ,CAAA;AAAA,EACjF;AACF;AAKO,SAAS,aAAa,OAAA,EAAiC;AAC5D,EAAA,IAAI,OAAA,CAAQ,WAAW,CAAA,EAAG;AACxB,IAAA,MAAM,IAAI,MAAM,kCAAkC,CAAA;AAAA,EACpD;AAEA,EAAA,MAAM,MAAA,GAAoB;AAAA,IACxB,IAAI,CAAA,OAAA,EAAU,IAAA,CAAK,KAAI,CAAE,QAAA,CAAS,EAAE,CAAC,CAAA,CAAA;AAAA,IACrC,OAAA,EAAS,QAAQ,GAAA,CAAI,CAAC,MAAM,CAAA,CAAE,OAAO,CAAA,CAAE,IAAA,CAAK,IAAI,CAAA;AAAA,IAChD,WAAA,EAAA,iBAAa,IAAI,IAAA,EAAK,EAAE,WAAA,EAAY;AAAA,IACpC,OAAA,EAAS;AAAA,MACP,SAAA,EAAW,OAAA,CAAQ,MAAA,CAAO,CAAC,GAAA,EAAK,MAAM,GAAA,GAAM,CAAA,CAAE,OAAA,CAAQ,SAAA,EAAW,CAAC,CAAA;AAAA,MAClE,WAAA,EAAa,OAAA,CAAQ,MAAA,CAAO,CAAC,GAAA,EAAK,MAAM,GAAA,GAAM,CAAA,CAAE,OAAA,CAAQ,WAAA,EAAa,CAAC,CAAA;AAAA,MACtE,cAAA,EAAgB,OAAA,CAAQ,MAAA,CAAO,CAAC,GAAA,EAAK,MAAM,GAAA,GAAM,CAAA,CAAE,OAAA,CAAQ,cAAA,EAAgB,CAAC,CAAA;AAAA,MAC5E,kBAAkB,EAAC;AAAA,MACnB,kBAAkB,EAAC;AAAA,MACnB,aAAa,IAAA,CAAK,KAAA;AAAA,QAChB,OAAA,CAAQ,MAAA,CAAO,CAAC,GAAA,EAAK,CAAA,KAAM,GAAA,GAAM,CAAA,CAAE,OAAA,CAAQ,WAAA,EAAa,CAAC,CAAA,GAAI,OAAA,CAAQ;AAAA;AACvE,KACF;AAAA,IACA,QAAQ,OAAA,CAAQ,OAAA,CAAQ,CAAC,CAAA,KAAM,EAAE,MAAM,CAAA;AAAA,IACvC,gBAAgB,OAAA,CAAQ,OAAA,CAAQ,CAAC,CAAA,KAAM,EAAE,cAAc,CAAA;AAAA,IACvD,cAAc,OAAA,CAAQ,OAAA,CAAQ,CAAC,CAAA,KAAM,EAAE,YAAY,CAAA;AAAA,IACnD,iBAAiB;AAAC,GACpB;AAGA,EAAA,KAAA,MAAW,UAAU,OAAA,EAAS;AAC5B,IAAA,KAAA,MAAW,CAAC,UAAU,KAAK,CAAA,IAAK,OAAO,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,gBAAgB,CAAA,EAAG;AAC/E,MAAA,MAAM,GAAA,GAAM,QAAA;AACZ,MAAA,MAAA,CAAO,OAAA,CAAQ,iBAAiB,GAAG,CAAA,GAAA,CAChC,OAAO,OAAA,CAAQ,gBAAA,CAAiB,GAAG,CAAA,IAAK,CAAA,IAAK,KAAA;AAAA,IAClD;AACA,IAAA,KAAA,MAAW,CAAC,UAAU,KAAK,CAAA,IAAK,OAAO,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,gBAAgB,CAAA,EAAG;AAC/E,MAAA,MAAM,GAAA,GAAM,QAAA;AACZ,MAAA,MAAA,CAAO,OAAA,CAAQ,iBAAiB,GAAG,CAAA,GAAA,CAChC,OAAO,OAAA,CAAQ,gBAAA,CAAiB,GAAG,CAAA,IAAK,CAAA,IAAK,KAAA;AAAA,IAClD;AAAA,EACF;AAGA,EAAA,MAAM,MAAA,uBAAa,GAAA,EAA8C;AACjE,EAAA,KAAA,MAAW,UAAU,OAAA,EAAS;AAC5B,IAAA,KAAA,MAAW,GAAA,IAAO,OAAO,eAAA,EAAiB;AACxC,MAAA,MAAM,MAAM,CAAA,EAAG,GAAA,CAAI,QAAQ,CAAA,CAAA,EAAI,IAAI,KAAK,CAAA,CAAA;AACxC,MAAA,IAAI,MAAA,CAAO,GAAA,CAAI,GAAG,CAAA,EAAG;AACnB,QAAA,MAAM,QAAA,GAAW,MAAA,CAAO,GAAA,CAAI,GAAG,CAAA;AAC/B,QAAA,QAAA,CAAS,YAAA,CAAa,IAAA,CAAK,GAAG,GAAA,CAAI,YAAY,CAAA;AAAA,MAChD,CAAA,MAAO;AACL,QAAA,MAAA,CAAO,GAAA,CAAI,GAAA,EAAK,EAAE,GAAG,GAAA,EAAK,YAAA,EAAc,CAAC,GAAG,GAAA,CAAI,YAAY,CAAA,EAAG,CAAA;AAAA,MACjE;AAAA,IACF;AAAA,EACF;AACA,EAAA,MAAA,CAAO,kBAAkB,KAAA,CAAM,IAAA,CAAK,MAAA,CAAO,MAAA,EAAQ,CAAA,CAAE,IAAA;AAAA,IACnD,CAAC,CAAA,EAAG,CAAA,KAAM,CAAA,CAAE,WAAW,CAAA,CAAE;AAAA,GAC3B;AAEA,EAAA,OAAO,MAAA;AACT","file":"index.mjs","sourcesContent":["/**\n * @djangocfg/seo - JSON Report Generator\n * Generate AI-friendly JSON reports\n */\n\nimport type {\n SeoReport,\n SeoIssue,\n UrlInspectionResult,\n CrawlResult,\n ReportSummary,\n Recommendation,\n IssueCategory,\n IssueSeverity,\n} from '../types/index.js';\n\nexport interface JsonReportOptions {\n includeRawData?: boolean;\n prettyPrint?: boolean;\n /** Maximum URLs to include per issue group (default: 10) */\n maxUrlsPerIssue?: number;\n}\n\n/**\n * Generate a comprehensive JSON report\n */\nexport function generateJsonReport(\n siteUrl: string,\n data: {\n issues: SeoIssue[];\n urlInspections?: UrlInspectionResult[];\n crawlResults?: CrawlResult[];\n },\n options: JsonReportOptions = {}\n): SeoReport {\n const { issues, urlInspections = [], crawlResults = [] } = data;\n const maxUrlsPerIssue = options.maxUrlsPerIssue ?? 10;\n\n // Limit issues to maxUrlsPerIssue per title group\n const limitedIssues = limitIssuesByTitle(issues, maxUrlsPerIssue);\n\n const report: SeoReport = {\n id: generateReportId(),\n siteUrl,\n generatedAt: new Date().toISOString(),\n summary: generateSummary(issues, urlInspections, crawlResults), // Use original for accurate counts\n issues: sortIssues(limitedIssues),\n urlInspections: options.includeRawData ? urlInspections.slice(0, 100) : [],\n crawlResults: options.includeRawData ? crawlResults.slice(0, 100) : [],\n recommendations: generateRecommendations(issues, maxUrlsPerIssue),\n };\n\n return report;\n}\n\n/**\n * Limit issues to maxUrls per issue title group\n */\nfunction limitIssuesByTitle(issues: SeoIssue[], maxUrls: number): SeoIssue[] {\n const byTitle = new Map<string, SeoIssue[]>();\n\n for (const issue of issues) {\n const existing = byTitle.get(issue.title) || [];\n existing.push(issue);\n byTitle.set(issue.title, existing);\n }\n\n const limited: SeoIssue[] = [];\n for (const [, group] of byTitle) {\n const sorted = group.sort((a, b) => {\n const severityOrder = { critical: 0, error: 1, warning: 2, info: 3 };\n return severityOrder[a.severity] - severityOrder[b.severity];\n });\n limited.push(...sorted.slice(0, maxUrls));\n }\n\n return limited;\n}\n\n/**\n * Generate report summary\n */\nfunction generateSummary(\n issues: SeoIssue[],\n urlInspections: UrlInspectionResult[],\n crawlResults: CrawlResult[]\n): ReportSummary {\n const totalUrls = Math.max(\n urlInspections.length,\n crawlResults.length,\n new Set(issues.map((i) => i.url)).size\n );\n\n const indexedUrls = urlInspections.filter(\n (r) => r.indexStatusResult.coverageState === 'SUBMITTED_AND_INDEXED'\n ).length;\n\n const notIndexedUrls = urlInspections.filter(\n (r) =>\n r.indexStatusResult.coverageState === 'NOT_INDEXED' ||\n r.indexStatusResult.coverageState === 'CRAWLED_CURRENTLY_NOT_INDEXED' ||\n r.indexStatusResult.coverageState === 'DISCOVERED_CURRENTLY_NOT_INDEXED'\n ).length;\n\n const issuesByCategory = issues.reduce(\n (acc, issue) => {\n acc[issue.category] = (acc[issue.category] || 0) + 1;\n return acc;\n },\n {} as Record<IssueCategory, number>\n );\n\n const issuesBySeverity = issues.reduce(\n (acc, issue) => {\n acc[issue.severity] = (acc[issue.severity] || 0) + 1;\n return acc;\n },\n {} as Record<IssueSeverity, number>\n );\n\n // Calculate health score (0-100)\n const healthScore = calculateHealthScore(issues, totalUrls);\n\n return {\n totalUrls,\n indexedUrls,\n notIndexedUrls,\n issuesByCategory,\n issuesBySeverity,\n healthScore,\n };\n}\n\n/**\n * Calculate overall SEO health score\n */\nfunction calculateHealthScore(issues: SeoIssue[], totalUrls: number): number {\n if (totalUrls === 0) return 100;\n\n const severityWeights: Record<IssueSeverity, number> = {\n critical: 10,\n error: 5,\n warning: 2,\n info: 0.5,\n };\n\n const totalPenalty = issues.reduce(\n (sum, issue) => sum + severityWeights[issue.severity],\n 0\n );\n\n // Max penalty per URL is 20 points\n const maxPenalty = totalUrls * 20;\n const penaltyRatio = Math.min(totalPenalty / maxPenalty, 1);\n\n return Math.round((1 - penaltyRatio) * 100);\n}\n\n/**\n * Generate prioritized recommendations\n */\nfunction generateRecommendations(issues: SeoIssue[], maxUrls: number = 10): Recommendation[] {\n const recommendations: Recommendation[] = [];\n const issueGroups = new Map<string, SeoIssue[]>();\n\n // Group issues by title (similar issues)\n for (const issue of issues) {\n const key = `${issue.category}:${issue.title}`;\n if (!issueGroups.has(key)) {\n issueGroups.set(key, []);\n }\n issueGroups.get(key)!.push(issue);\n }\n\n // Create recommendations for each group\n for (const [, groupedIssues] of issueGroups) {\n const firstIssue = groupedIssues[0];\n if (!firstIssue) continue;\n const severity = firstIssue.severity;\n\n const priority: 1 | 2 | 3 | 4 | 5 =\n severity === 'critical'\n ? 1\n : severity === 'error'\n ? 2\n : severity === 'warning'\n ? 3\n : 4;\n\n const impact: 'high' | 'medium' | 'low' =\n priority <= 2 ? 'high' : priority === 3 ? 'medium' : 'low';\n\n const allUrls = groupedIssues.map((i) => i.url);\n const totalCount = allUrls.length;\n const limitedUrls = allUrls.slice(0, maxUrls);\n\n recommendations.push({\n priority,\n category: firstIssue.category,\n title: firstIssue.title,\n description: totalCount > maxUrls\n ? `${firstIssue.description} (showing ${maxUrls} of ${totalCount} URLs)`\n : firstIssue.description,\n affectedUrls: limitedUrls,\n estimatedImpact: impact,\n actionItems: [firstIssue.recommendation],\n });\n }\n\n // Sort by priority\n return recommendations.sort((a, b) => a.priority - b.priority);\n}\n\n/**\n * Sort issues by severity and category\n */\nfunction sortIssues(issues: SeoIssue[]): SeoIssue[] {\n const severityOrder: Record<IssueSeverity, number> = {\n critical: 0,\n error: 1,\n warning: 2,\n info: 3,\n };\n\n return [...issues].sort((a, b) => {\n const severityDiff = severityOrder[a.severity] - severityOrder[b.severity];\n if (severityDiff !== 0) return severityDiff;\n return a.category.localeCompare(b.category);\n });\n}\n\n/**\n * Generate unique report ID\n */\nfunction generateReportId(): string {\n const timestamp = Date.now().toString(36);\n const random = Math.random().toString(36).substring(2, 8);\n return `seo-report-${timestamp}-${random}`;\n}\n\n/**\n * Export report as JSON string\n */\nexport function exportJsonReport(report: SeoReport, pretty = true): string {\n return JSON.stringify(report, null, pretty ? 2 : 0);\n}\n\n/**\n * Generate AI-optimized schema for the report\n * This schema helps AI understand the report structure\n */\nexport const AI_REPORT_SCHEMA = {\n $schema: 'http://json-schema.org/draft-07/schema#',\n title: 'SEO Report',\n description: 'AI-friendly SEO analysis report with issues and recommendations',\n type: 'object',\n properties: {\n id: { type: 'string', description: 'Unique report identifier' },\n siteUrl: { type: 'string', description: 'Analyzed site URL' },\n generatedAt: { type: 'string', format: 'date-time' },\n summary: {\n type: 'object',\n description: 'Quick overview of SEO health',\n properties: {\n totalUrls: { type: 'number' },\n indexedUrls: { type: 'number' },\n notIndexedUrls: { type: 'number' },\n healthScore: {\n type: 'number',\n minimum: 0,\n maximum: 100,\n description: '0-100 score, higher is better',\n },\n },\n },\n issues: {\n type: 'array',\n description: 'List of detected SEO issues sorted by severity',\n items: {\n type: 'object',\n properties: {\n severity: {\n type: 'string',\n enum: ['critical', 'error', 'warning', 'info'],\n },\n category: {\n type: 'string',\n enum: [\n 'indexing',\n 'crawling',\n 'content',\n 'technical',\n 'mobile',\n 'performance',\n 'structured-data',\n 'security',\n ],\n },\n title: { type: 'string' },\n description: { type: 'string' },\n recommendation: { type: 'string' },\n url: { type: 'string' },\n },\n },\n },\n recommendations: {\n type: 'array',\n description: 'Prioritized action items',\n items: {\n type: 'object',\n properties: {\n priority: { type: 'number', minimum: 1, maximum: 5 },\n title: { type: 'string' },\n affectedUrls: { type: 'array', items: { type: 'string' } },\n actionItems: { type: 'array', items: { type: 'string' } },\n },\n },\n },\n },\n};\n","/**\n * @djangocfg/seo - Markdown Report Generator\n * Generate human-readable Markdown reports\n */\n\nimport type { SeoReport, SeoIssue, IssueSeverity, IssueCategory } from '../types/index.js';\n\nexport interface MarkdownReportOptions {\n includeRawIssues?: boolean;\n includeUrls?: boolean;\n maxUrlsPerIssue?: number;\n}\n\n/**\n * Generate a Markdown report from SEO data\n */\nexport function generateMarkdownReport(\n report: SeoReport,\n options: MarkdownReportOptions = {}\n): string {\n const { includeRawIssues = true, includeUrls = true, maxUrlsPerIssue = 10 } = options;\n\n const lines: string[] = [];\n\n // Header\n lines.push(`# SEO Analysis Report`);\n lines.push('');\n lines.push(`**Site:** ${report.siteUrl}`);\n lines.push(`**Generated:** ${new Date(report.generatedAt).toLocaleString()}`);\n lines.push(`**Report ID:** ${report.id}`);\n lines.push('');\n\n // Summary\n lines.push('## Summary');\n lines.push('');\n lines.push(`| Metric | Value |`);\n lines.push(`|--------|-------|`);\n lines.push(`| Health Score | ${getHealthScoreEmoji(report.summary.healthScore)} **${report.summary.healthScore}/100** |`);\n lines.push(`| Total URLs | ${report.summary.totalUrls} |`);\n lines.push(`| Indexed URLs | ${report.summary.indexedUrls} |`);\n lines.push(`| Not Indexed | ${report.summary.notIndexedUrls} |`);\n lines.push('');\n\n // Issues by Severity\n lines.push('### Issues by Severity');\n lines.push('');\n const severities: IssueSeverity[] = ['critical', 'error', 'warning', 'info'];\n for (const severity of severities) {\n const count = report.summary.issuesBySeverity[severity] || 0;\n lines.push(`- ${getSeverityEmoji(severity)} **${capitalize(severity)}:** ${count}`);\n }\n lines.push('');\n\n // Issues by Category\n lines.push('### Issues by Category');\n lines.push('');\n const categories = Object.entries(report.summary.issuesByCategory).sort(\n ([, a], [, b]) => b - a\n );\n for (const [category, count] of categories) {\n lines.push(`- ${getCategoryEmoji(category as IssueCategory)} **${formatCategory(category)}:** ${count}`);\n }\n lines.push('');\n\n // Recommendations\n lines.push('## Prioritized Recommendations');\n lines.push('');\n\n for (const rec of report.recommendations) {\n lines.push(`### ${getPriorityEmoji(rec.priority)} Priority ${rec.priority}: ${rec.title}`);\n lines.push('');\n lines.push(`**Category:** ${formatCategory(rec.category)}`);\n lines.push(`**Impact:** ${capitalize(rec.estimatedImpact)}`);\n lines.push(`**Affected URLs:** ${rec.affectedUrls.length}`);\n lines.push('');\n lines.push(`${rec.description}`);\n lines.push('');\n lines.push('**Action Items:**');\n for (const action of rec.actionItems) {\n lines.push(`- ${action}`);\n }\n lines.push('');\n\n if (includeUrls && rec.affectedUrls.length > 0) {\n const urlsToShow = rec.affectedUrls.slice(0, maxUrlsPerIssue);\n lines.push('<details>');\n lines.push(`<summary>Affected URLs (${rec.affectedUrls.length})</summary>`);\n lines.push('');\n for (const url of urlsToShow) {\n lines.push(`- ${url}`);\n }\n if (rec.affectedUrls.length > maxUrlsPerIssue) {\n lines.push(`- ... and ${rec.affectedUrls.length - maxUrlsPerIssue} more`);\n }\n lines.push('</details>');\n lines.push('');\n }\n\n lines.push('---');\n lines.push('');\n }\n\n // Detailed Issues\n if (includeRawIssues) {\n lines.push('## All Issues');\n lines.push('');\n\n const issuesByCategory = groupBy(report.issues, 'category');\n\n for (const [category, issues] of Object.entries(issuesByCategory)) {\n lines.push(`### ${getCategoryEmoji(category as IssueCategory)} ${formatCategory(category)}`);\n lines.push('');\n\n for (const issue of issues) {\n lines.push(\n `#### ${getSeverityEmoji(issue.severity)} ${issue.title}`\n );\n lines.push('');\n lines.push(`**URL:** \\`${issue.url}\\``);\n lines.push(`**Severity:** ${capitalize(issue.severity)}`);\n lines.push('');\n lines.push(issue.description);\n lines.push('');\n lines.push(`**Recommendation:** ${issue.recommendation}`);\n lines.push('');\n }\n }\n }\n\n // Footer\n lines.push('---');\n lines.push('');\n lines.push('*Report generated by [@djangocfg/seo](https://djangocfg.com)*');\n lines.push('');\n lines.push('> This report is designed to be processed by AI assistants for automated SEO improvements.');\n\n return lines.join('\\n');\n}\n\n/**\n * Generate a concise summary for AI consumption\n */\nexport function generateAiSummary(report: SeoReport): string {\n const lines: string[] = [];\n\n lines.push('# SEO Report Summary for AI Processing');\n lines.push('');\n lines.push('## Context');\n lines.push(`Site: ${report.siteUrl}`);\n lines.push(`Health Score: ${report.summary.healthScore}/100`);\n lines.push(`Critical Issues: ${report.summary.issuesBySeverity.critical || 0}`);\n lines.push(`Errors: ${report.summary.issuesBySeverity.error || 0}`);\n lines.push(`Warnings: ${report.summary.issuesBySeverity.warning || 0}`);\n lines.push('');\n lines.push('## Top Priority Actions');\n lines.push('');\n\n const topRecommendations = report.recommendations.slice(0, 5);\n for (let i = 0; i < topRecommendations.length; i++) {\n const rec = topRecommendations[i];\n if (!rec) continue;\n lines.push(`${i + 1}. **${rec.title}** (${rec.affectedUrls.length} URLs)`);\n lines.push(` - ${rec.actionItems[0]}`);\n }\n\n lines.push('');\n lines.push('## Issue Categories');\n lines.push('');\n\n const sortedCategories = Object.entries(report.summary.issuesByCategory)\n .sort(([, a], [, b]) => b - a);\n\n for (const [category, count] of sortedCategories) {\n lines.push(`- ${formatCategory(category)}: ${count} issues`);\n }\n\n return lines.join('\\n');\n}\n\n// Helper functions\nfunction getSeverityEmoji(severity: IssueSeverity): string {\n const emojis: Record<IssueSeverity, string> = {\n critical: '🔴',\n error: '🟠',\n warning: '🟡',\n info: '🔵',\n };\n return emojis[severity];\n}\n\nfunction getCategoryEmoji(category: IssueCategory): string {\n const emojis: Record<IssueCategory, string> = {\n indexing: '📑',\n crawling: '🕷️',\n content: '📝',\n technical: '⚙️',\n mobile: '📱',\n performance: '⚡',\n 'structured-data': '🏷️',\n security: '🔒',\n };\n return emojis[category] || '📋';\n}\n\nfunction getPriorityEmoji(priority: number): string {\n const emojis: Record<number, string> = {\n 1: '🚨',\n 2: '⚠️',\n 3: '📌',\n 4: '💡',\n 5: 'ℹ️',\n };\n return emojis[priority] || '📋';\n}\n\nfunction getHealthScoreEmoji(score: number): string {\n if (score >= 90) return '🟢';\n if (score >= 70) return '🟡';\n if (score >= 50) return '🟠';\n return '🔴';\n}\n\nfunction capitalize(str: string): string {\n return str.charAt(0).toUpperCase() + str.slice(1);\n}\n\nfunction formatCategory(category: string): string {\n return category\n .split('-')\n .map(capitalize)\n .join(' ');\n}\n\nfunction groupBy<T>(array: T[], key: keyof T): Record<string, T[]> {\n return array.reduce(\n (acc, item) => {\n const groupKey = String(item[key]);\n if (!acc[groupKey]) {\n acc[groupKey] = [];\n }\n acc[groupKey].push(item);\n return acc;\n },\n {} as Record<string, T[]>\n );\n}\n","/**\n * @djangocfg/seo - Split Report Generator\n * Generates AI-friendly split reports (max 1000 lines each)\n */\n\nimport { writeFileSync, mkdirSync, existsSync, rmSync, readdirSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type { SeoReport, SeoIssue, IssueCategory, IssueSeverity } from '../types/index.js';\n\nconst MAX_LINES = 1000;\n\nexport interface SplitReportOptions {\n outputDir: string;\n /** Clear output directory before generating */\n clearOutputDir?: boolean;\n}\n\nexport interface SplitReportResult {\n indexFile: string;\n categoryFiles: string[];\n totalFiles: number;\n}\n\n/**\n * Generate split reports for AI processing\n */\nexport function generateSplitReports(\n report: SeoReport,\n options: SplitReportOptions\n): SplitReportResult {\n const { outputDir, clearOutputDir = true } = options;\n\n // Clear output directory\n if (clearOutputDir && existsSync(outputDir)) {\n const files = readdirSync(outputDir);\n for (const file of files) {\n if (file.startsWith('seo-') && file.endsWith('.md')) {\n rmSync(join(outputDir, file), { force: true });\n }\n }\n }\n\n // Ensure output directory exists\n if (!existsSync(outputDir)) {\n mkdirSync(outputDir, { recursive: true });\n }\n\n const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);\n const siteName = new URL(report.siteUrl).hostname.replace(/\\./g, '-');\n const prefix = `seo-${siteName}-${timestamp}`;\n\n const categoryFiles: string[] = [];\n\n // Group issues by category\n const issuesByCategory = groupIssuesByCategory(report.issues);\n const categories = Object.keys(issuesByCategory) as IssueCategory[];\n\n // Generate category files\n for (const category of categories) {\n const issues = issuesByCategory[category] || [];\n if (issues.length === 0) continue;\n\n const chunks = splitIntoChunks(issues, category);\n\n for (let i = 0; i < chunks.length; i++) {\n const suffix = chunks.length > 1 ? `-${i + 1}` : '';\n const filename = `${prefix}-${category}${suffix}.md`;\n const filepath = join(outputDir, filename);\n\n const chunk = chunks[i];\n if (!chunk) continue;\n const content = generateCategoryFile(report.siteUrl, category, chunk, {\n part: chunks.length > 1 ? i + 1 : undefined,\n totalParts: chunks.length > 1 ? chunks.length : undefined,\n });\n\n writeFileSync(filepath, content, 'utf-8');\n categoryFiles.push(filename);\n }\n }\n\n // Generate index file\n const indexFilename = `${prefix}-index.md`;\n const indexFilepath = join(outputDir, indexFilename);\n const indexContent = generateIndexFile(report, categoryFiles);\n writeFileSync(indexFilepath, indexContent, 'utf-8');\n\n return {\n indexFile: indexFilename,\n categoryFiles,\n totalFiles: categoryFiles.length + 1,\n };\n}\n\n/**\n * Generate compact index file\n */\nfunction generateIndexFile(report: SeoReport, categoryFiles: string[]): string {\n const lines: string[] = [];\n\n lines.push('# SEO Report Index');\n lines.push('');\n lines.push(`Site: ${report.siteUrl}`);\n lines.push(`Score: ${report.summary.healthScore}/100`);\n lines.push(`Date: ${report.generatedAt.slice(0, 10)}`);\n lines.push('');\n\n // Severity counts\n lines.push('## Issues');\n lines.push('');\n lines.push('| Severity | Count |');\n lines.push('|----------|-------|');\n const severities: IssueSeverity[] = ['critical', 'error', 'warning', 'info'];\n for (const sev of severities) {\n const count = report.summary.issuesBySeverity[sev] || 0;\n if (count > 0) {\n lines.push(`| ${sev} | ${count} |`);\n }\n }\n lines.push('');\n\n // Top 10 recommendations (compact)\n lines.push('## Actions');\n lines.push('');\n const topRecs = report.recommendations.slice(0, 10);\n for (let i = 0; i < topRecs.length; i++) {\n const rec = topRecs[i];\n if (!rec) continue;\n lines.push(`${i + 1}. **${rec.title}** (${rec.affectedUrls.length})`);\n lines.push(` ${rec.actionItems[0]}`);\n }\n lines.push('');\n\n // File index\n lines.push('## Files');\n lines.push('');\n for (const file of categoryFiles) {\n lines.push(`- [${file}](./${file})`);\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Generate compact category file\n */\nfunction generateCategoryFile(\n siteUrl: string,\n category: IssueCategory,\n issues: SeoIssue[],\n opts: { part?: number; totalParts?: number }\n): string {\n const lines: string[] = [];\n const partStr = opts.part ? ` (Part ${opts.part}/${opts.totalParts})` : '';\n\n lines.push(`# ${formatCategory(category)}${partStr}`);\n lines.push('');\n lines.push(`Site: ${siteUrl}`);\n lines.push(`Issues: ${issues.length}`);\n lines.push('');\n\n // Group by title for compact display\n const byTitle = new Map<string, SeoIssue[]>();\n for (const issue of issues) {\n const group = byTitle.get(issue.title) || [];\n group.push(issue);\n byTitle.set(issue.title, group);\n }\n\n for (const [title, groupIssues] of byTitle) {\n const first = groupIssues[0];\n if (!first) continue;\n\n lines.push(`## ${title}`);\n lines.push('');\n lines.push(`Severity: ${first.severity}`);\n lines.push(`Count: ${groupIssues.length}`);\n lines.push('');\n lines.push(`> ${first.recommendation}`);\n lines.push('');\n\n // Compact URL list\n lines.push('URLs:');\n for (const issue of groupIssues.slice(0, 20)) {\n lines.push(`- ${issue.url}`);\n }\n if (groupIssues.length > 20) {\n lines.push(`- ... +${groupIssues.length - 20} more`);\n }\n lines.push('');\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Split issues into chunks that fit within MAX_LINES\n */\nfunction splitIntoChunks(issues: SeoIssue[], category: IssueCategory): SeoIssue[][] {\n // Estimate lines per issue group (title + severity + recommendation + URLs)\n const byTitle = new Map<string, SeoIssue[]>();\n for (const issue of issues) {\n const group = byTitle.get(issue.title) || [];\n group.push(issue);\n byTitle.set(issue.title, group);\n }\n\n const chunks: SeoIssue[][] = [];\n let currentChunk: SeoIssue[] = [];\n let currentLines = 10; // Header overhead\n\n for (const [, groupIssues] of byTitle) {\n // Estimate: 6 lines header + min(20, count) URLs + 2 spacing\n const urlCount = Math.min(20, groupIssues.length);\n const groupLines = 8 + urlCount;\n\n if (currentLines + groupLines > MAX_LINES && currentChunk.length > 0) {\n chunks.push(currentChunk);\n currentChunk = [];\n currentLines = 10;\n }\n\n currentChunk.push(...groupIssues);\n currentLines += groupLines;\n }\n\n if (currentChunk.length > 0) {\n chunks.push(currentChunk);\n }\n\n return chunks.length > 0 ? chunks : [[]];\n}\n\nfunction groupIssuesByCategory(issues: SeoIssue[]): Record<IssueCategory, SeoIssue[]> {\n const result: Partial<Record<IssueCategory, SeoIssue[]>> = {};\n\n for (const issue of issues) {\n if (!result[issue.category]) {\n result[issue.category] = [];\n }\n result[issue.category]!.push(issue);\n }\n\n return result as Record<IssueCategory, SeoIssue[]>;\n}\n\nfunction formatCategory(category: string): string {\n return category\n .split('-')\n .map(s => s.charAt(0).toUpperCase() + s.slice(1))\n .join(' ');\n}\n","/**\n * @djangocfg/seo - CLAUDE.md Generator\n * Generates AI context file with package docs and current audit state\n */\n\nimport type { SeoReport } from '../types/index.js';\n\n/**\n * Generate CLAUDE.md content for AI context\n */\nexport function generateClaudeContext(report: SeoReport): string {\n const lines: string[] = [];\n\n // Package docs (static)\n lines.push('# @djangocfg/seo');\n lines.push('');\n lines.push('SEO audit toolkit. Generates AI-optimized split reports (max 1000 lines each).');\n lines.push('');\n lines.push('## Commands');\n lines.push('');\n lines.push('```bash');\n lines.push('# Audit (HTTP-based, crawls live site)');\n lines.push('pnpm seo:audit # Full audit (split reports)');\n lines.push('pnpm seo:audit --env dev # Audit local dev');\n lines.push('pnpm seo:audit --format all # All formats');\n lines.push('');\n lines.push('# Content (file-based, scans MDX/content/)');\n lines.push('pnpm exec djangocfg-seo content check # Check MDX links');\n lines.push('pnpm exec djangocfg-seo content fix # Show fixable links');\n lines.push('pnpm exec djangocfg-seo content fix --fix # Apply fixes');\n lines.push('pnpm exec djangocfg-seo content sitemap # Generate sitemap.ts');\n lines.push('```');\n lines.push('');\n lines.push('## Options');\n lines.push('');\n lines.push('- `--env, -e` - prod (default) or dev');\n lines.push('- `--site, -s` - Site URL (overrides env)');\n lines.push('- `--output, -o` - Output directory');\n lines.push('- `--format, -f` - split (default), json, markdown, ai-summary, all');\n lines.push('- `--max-pages` - Max pages (default: 100)');\n lines.push('- `--service-account` - Google service account JSON path');\n lines.push('- `--content-dir` - Content directory (default: content/)');\n lines.push('- `--base-path` - Base URL path for docs (default: /docs)');\n lines.push('');\n lines.push('## Reports');\n lines.push('');\n lines.push('- `seo-*-index.md` - Summary + links to categories');\n lines.push('- `seo-*-technical.md` - Broken links, sitemap issues');\n lines.push('- `seo-*-content.md` - H1, meta, title issues');\n lines.push('- `seo-*-performance.md` - Load time, TTFB issues');\n lines.push('- `seo-ai-summary-*.md` - Quick overview');\n lines.push('');\n\n // Issue types - important for AI to understand priorities\n lines.push('## Issue Severity');\n lines.push('');\n lines.push('- **critical** - Blocks indexing (fix immediately)');\n lines.push('- **error** - SEO problems (high priority)');\n lines.push('- **warning** - Recommendations (medium priority)');\n lines.push('- **info** - Best practices (low priority)');\n lines.push('');\n lines.push('## Issue Categories');\n lines.push('');\n lines.push('- **technical** - Broken links, sitemap, robots.txt');\n lines.push('- **content** - Missing H1, meta description, title');\n lines.push('- **indexing** - Not indexed, crawl errors from GSC');\n lines.push('- **performance** - Slow load time (>3s), high TTFB (>800ms)');\n lines.push('');\n\n // Routes info\n lines.push('## Routes Scanner');\n lines.push('');\n lines.push('Scans Next.js App Router `app/` directory. Handles:');\n lines.push('- Route groups `(group)` - ignored in URL');\n lines.push('- Dynamic `[slug]` - shown as `:slug`');\n lines.push('- Catch-all `[...slug]` - shown as `:...slug`');\n lines.push('- Parallel `@folder` - skipped');\n lines.push('- Private `_folder` - skipped');\n lines.push('');\n\n // Link guidelines\n lines.push('## Link Guidelines');\n lines.push('');\n lines.push('### Nextra/MDX Projects (content/)');\n lines.push('');\n lines.push('For non-index files (e.g., `overview.mdx`):');\n lines.push('- **Sibling file**: `../sibling` (one level up)');\n lines.push('- **Other section**: `/docs/full/path` (absolute)');\n lines.push('- **AVOID**: `./sibling` (browser adds filename to path!)');\n lines.push('- **AVOID**: `../../deep/path` (hard to maintain)');\n lines.push('');\n lines.push('For index files (e.g., `index.mdx`):');\n lines.push('- **Child file**: `./child` works correctly');\n lines.push('- **Sibling folder**: `../sibling/` or absolute');\n lines.push('');\n lines.push('### Next.js App Router Projects');\n lines.push('');\n lines.push('Use declarative routes from `_routes/`:');\n lines.push('```typescript');\n lines.push('import { routes } from \"@/app/_routes\";');\n lines.push('<Link href={routes.dashboard.machines}>Machines</Link>');\n lines.push('```');\n lines.push('');\n lines.push('Benefits: type-safe, refactor-friendly, centralized.');\n lines.push('');\n\n // Current audit state (dynamic)\n lines.push('---');\n lines.push('');\n lines.push('## Current Audit');\n lines.push('');\n lines.push(`Site: ${report.siteUrl}`);\n lines.push(`Score: ${report.summary.healthScore}/100`);\n lines.push(`Date: ${report.generatedAt.slice(0, 10)}`);\n lines.push('');\n\n // Issue summary\n lines.push('### Issues');\n lines.push('');\n const { critical = 0, error = 0, warning = 0, info = 0 } = report.summary.issuesBySeverity;\n if (critical > 0) lines.push(`- Critical: ${critical}`);\n if (error > 0) lines.push(`- Error: ${error}`);\n if (warning > 0) lines.push(`- Warning: ${warning}`);\n if (info > 0) lines.push(`- Info: ${info}`);\n lines.push('');\n\n // Top 5 actions\n lines.push('### Top Actions');\n lines.push('');\n const topRecs = report.recommendations.slice(0, 5);\n for (let i = 0; i < topRecs.length; i++) {\n const rec = topRecs[i];\n if (!rec) continue;\n lines.push(`${i + 1}. **${rec.title}** (${rec.affectedUrls.length} URLs)`);\n }\n lines.push('');\n\n // Report files in this directory\n lines.push('### Report Files');\n lines.push('');\n lines.push('See split reports in this directory:');\n lines.push('- `seo-*-index.md` - Start here');\n lines.push('- `seo-*-technical.md` - Technical issues');\n lines.push('- `seo-*-content.md` - Content issues');\n lines.push('- `seo-*-performance.md` - Performance issues');\n lines.push('');\n\n return lines.join('\\n');\n}\n","/**\n * @djangocfg/seo - Report Generator\n * Main report generation orchestrator\n */\n\nimport { writeFileSync, mkdirSync, existsSync, rmSync, readdirSync } from 'node:fs';\nimport { join } from 'node:path';\nimport consola from 'consola';\nimport { generateJsonReport, exportJsonReport } from './json-report.js';\nimport { generateMarkdownReport, generateAiSummary } from './markdown-report.js';\nimport { generateSplitReports } from './split-report.js';\nimport { generateClaudeContext } from './claude-context.js';\nimport type {\n SeoReport,\n SeoIssue,\n UrlInspectionResult,\n CrawlResult,\n IssueCategory,\n IssueSeverity,\n} from '../types/index.js';\n\nexport interface ReportGeneratorOptions {\n outputDir: string;\n formats: ('json' | 'markdown' | 'ai-summary' | 'split')[];\n includeRawData?: boolean;\n timestamp?: boolean;\n /** Clear output directory before generating reports */\n clearOutputDir?: boolean;\n /** Maximum URLs to show per issue group (default: 10) */\n maxUrlsPerIssue?: number;\n}\n\nexport interface GeneratedReports {\n report: SeoReport;\n files: {\n json?: string;\n markdown?: string;\n aiSummary?: string;\n split?: {\n index: string;\n categories: string[];\n };\n };\n}\n\n/**\n * Generate and save SEO reports\n */\nexport async function generateAndSaveReports(\n siteUrl: string,\n data: {\n issues: SeoIssue[];\n urlInspections?: UrlInspectionResult[];\n crawlResults?: CrawlResult[];\n },\n options: ReportGeneratorOptions\n): Promise<GeneratedReports> {\n const {\n outputDir,\n formats,\n includeRawData = false,\n timestamp = true,\n clearOutputDir = true,\n maxUrlsPerIssue = 10,\n } = options;\n\n // Clear output directory if requested\n if (clearOutputDir && existsSync(outputDir)) {\n try {\n const files = readdirSync(outputDir);\n for (const file of files) {\n if (file.startsWith('seo-')) {\n rmSync(join(outputDir, file), { force: true });\n }\n }\n } catch {\n // Ignore errors\n }\n }\n\n // Ensure output directory exists\n if (!existsSync(outputDir)) {\n mkdirSync(outputDir, { recursive: true });\n }\n\n // Generate the report\n const report = generateJsonReport(siteUrl, data, { includeRawData, maxUrlsPerIssue });\n\n const result: GeneratedReports = {\n report,\n files: {},\n };\n\n // Generate timestamp for filenames\n const ts = timestamp\n ? `-${new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19)}`\n : '';\n const siteName = new URL(siteUrl).hostname.replace(/\\./g, '-');\n\n // Generate JSON report\n if (formats.includes('json')) {\n const filename = `seo-report-${siteName}${ts}.json`;\n const filepath = join(outputDir, filename);\n const content = exportJsonReport(report, true);\n writeFileSync(filepath, content, 'utf-8');\n result.files.json = filepath;\n consola.success(`JSON report saved: ${filepath}`);\n }\n\n // Generate Markdown report\n if (formats.includes('markdown')) {\n const filename = `seo-report-${siteName}${ts}.md`;\n const filepath = join(outputDir, filename);\n const content = generateMarkdownReport(report, {\n includeRawIssues: true,\n includeUrls: true,\n });\n writeFileSync(filepath, content, 'utf-8');\n result.files.markdown = filepath;\n consola.success(`Markdown report saved: ${filepath}`);\n }\n\n // Generate AI summary\n if (formats.includes('ai-summary')) {\n const filename = `seo-ai-summary-${siteName}${ts}.md`;\n const filepath = join(outputDir, filename);\n const content = generateAiSummary(report);\n writeFileSync(filepath, content, 'utf-8');\n result.files.aiSummary = filepath;\n consola.success(`AI summary saved: ${filepath}`);\n }\n\n // Generate split reports (AI-optimized, max 1000 lines each)\n if (formats.includes('split')) {\n const splitResult = generateSplitReports(report, {\n outputDir,\n clearOutputDir: false, // Already cleared above\n });\n result.files.split = {\n index: join(outputDir, splitResult.indexFile),\n categories: splitResult.categoryFiles.map(f => join(outputDir, f)),\n };\n consola.success(`Split reports saved: ${splitResult.totalFiles} files (index + ${splitResult.categoryFiles.length} categories)`);\n }\n\n // Always generate CLAUDE.md for AI context\n const claudeContent = generateClaudeContext(report);\n const claudeFilepath = join(outputDir, 'CLAUDE.md');\n writeFileSync(claudeFilepath, claudeContent, 'utf-8');\n consola.success(`AI context saved: ${claudeFilepath}`);\n\n return result;\n}\n\n/**\n * Print report summary to console\n */\nexport function printReportSummary(report: SeoReport): void {\n consola.box(\n `SEO Report: ${report.siteUrl}\\n` +\n `Health Score: ${report.summary.healthScore}/100\\n` +\n `Total URLs: ${report.summary.totalUrls}\\n` +\n `Indexed: ${report.summary.indexedUrls} | Not Indexed: ${report.summary.notIndexedUrls}\\n` +\n `Issues: ${report.issues.length}`\n );\n\n if (report.summary.issuesBySeverity.critical) {\n consola.error(`Critical issues: ${report.summary.issuesBySeverity.critical}`);\n }\n if (report.summary.issuesBySeverity.error) {\n consola.warn(`Errors: ${report.summary.issuesBySeverity.error}`);\n }\n if (report.summary.issuesBySeverity.warning) {\n consola.info(`Warnings: ${report.summary.issuesBySeverity.warning}`);\n }\n\n consola.log('');\n consola.info('Top recommendations:');\n for (const rec of report.recommendations.slice(0, 3)) {\n consola.log(` ${rec.priority}. ${rec.title} (${rec.affectedUrls.length} URLs)`);\n }\n}\n\n/**\n * Merge multiple reports into one\n */\nexport function mergeReports(reports: SeoReport[]): SeoReport {\n if (reports.length === 0) {\n throw new Error('Cannot merge empty reports array');\n }\n\n const merged: SeoReport = {\n id: `merged-${Date.now().toString(36)}`,\n siteUrl: reports.map((r) => r.siteUrl).join(', '),\n generatedAt: new Date().toISOString(),\n summary: {\n totalUrls: reports.reduce((sum, r) => sum + r.summary.totalUrls, 0),\n indexedUrls: reports.reduce((sum, r) => sum + r.summary.indexedUrls, 0),\n notIndexedUrls: reports.reduce((sum, r) => sum + r.summary.notIndexedUrls, 0),\n issuesByCategory: {} as any,\n issuesBySeverity: {} as any,\n healthScore: Math.round(\n reports.reduce((sum, r) => sum + r.summary.healthScore, 0) / reports.length\n ),\n },\n issues: reports.flatMap((r) => r.issues),\n urlInspections: reports.flatMap((r) => r.urlInspections),\n crawlResults: reports.flatMap((r) => r.crawlResults),\n recommendations: [],\n };\n\n // Merge issue counts\n for (const report of reports) {\n for (const [category, count] of Object.entries(report.summary.issuesByCategory)) {\n const cat = category as IssueCategory;\n merged.summary.issuesByCategory[cat] =\n (merged.summary.issuesByCategory[cat] || 0) + count;\n }\n for (const [severity, count] of Object.entries(report.summary.issuesBySeverity)) {\n const sev = severity as IssueSeverity;\n merged.summary.issuesBySeverity[sev] =\n (merged.summary.issuesBySeverity[sev] || 0) + count;\n }\n }\n\n // Merge and deduplicate recommendations\n const recMap = new Map<string, typeof merged.recommendations[0]>();\n for (const report of reports) {\n for (const rec of report.recommendations) {\n const key = `${rec.category}:${rec.title}`;\n if (recMap.has(key)) {\n const existing = recMap.get(key)!;\n existing.affectedUrls.push(...rec.affectedUrls);\n } else {\n recMap.set(key, { ...rec, affectedUrls: [...rec.affectedUrls] });\n }\n }\n }\n merged.recommendations = Array.from(recMap.values()).sort(\n (a, b) => a.priority - b.priority\n );\n\n return merged;\n}\n"]}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { S as ScanResult, R as RouteInfo } from '../scanner-Cz4Th2Pt.js';
|
|
2
|
+
export { a as ScanOptions, f as findAppDir, g as getStaticUrls, r as routeToUrl, s as scanRoutes } from '../scanner-Cz4Th2Pt.js';
|
|
3
|
+
import { SeoIssue } from '../types/index.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @djangocfg/seo - Routes Analyzer
|
|
7
|
+
* Compare routes with sitemap and verify accessibility
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
interface SitemapUrl {
|
|
11
|
+
loc: string;
|
|
12
|
+
lastmod?: string;
|
|
13
|
+
changefreq?: string;
|
|
14
|
+
priority?: string;
|
|
15
|
+
}
|
|
16
|
+
interface RouteComparisonResult {
|
|
17
|
+
/** Routes found in app/ */
|
|
18
|
+
appRoutes: RouteInfo[];
|
|
19
|
+
/** URLs found in sitemap */
|
|
20
|
+
sitemapUrls: string[];
|
|
21
|
+
/** Static routes missing from sitemap */
|
|
22
|
+
missingFromSitemap: RouteInfo[];
|
|
23
|
+
/** Sitemap URLs that don't match any route */
|
|
24
|
+
extraInSitemap: string[];
|
|
25
|
+
/** Matching routes */
|
|
26
|
+
matching: RouteInfo[];
|
|
27
|
+
}
|
|
28
|
+
interface RouteVerificationResult {
|
|
29
|
+
/** Route being verified */
|
|
30
|
+
route: RouteInfo;
|
|
31
|
+
/** Full URL */
|
|
32
|
+
url: string;
|
|
33
|
+
/** HTTP status code */
|
|
34
|
+
statusCode: number;
|
|
35
|
+
/** Is accessible (2xx) */
|
|
36
|
+
isAccessible: boolean;
|
|
37
|
+
/** Error message if failed */
|
|
38
|
+
error?: string;
|
|
39
|
+
/** Response time in ms */
|
|
40
|
+
responseTime: number;
|
|
41
|
+
}
|
|
42
|
+
interface VerifyOptions {
|
|
43
|
+
/** Base URL (e.g., http://localhost:3000) */
|
|
44
|
+
baseUrl: string;
|
|
45
|
+
/** Request timeout in ms */
|
|
46
|
+
timeout?: number;
|
|
47
|
+
/** Max concurrent requests */
|
|
48
|
+
concurrency?: number;
|
|
49
|
+
/** Only verify static routes */
|
|
50
|
+
staticOnly?: boolean;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Compare routes with sitemap URLs
|
|
54
|
+
*/
|
|
55
|
+
declare function compareWithSitemap(scanResult: ScanResult, sitemapUrls: string[], baseUrl: string): RouteComparisonResult;
|
|
56
|
+
/**
|
|
57
|
+
* Verify routes are accessible
|
|
58
|
+
*/
|
|
59
|
+
declare function verifyRoutes(scanResult: ScanResult, options: VerifyOptions): Promise<RouteVerificationResult[]>;
|
|
60
|
+
/**
|
|
61
|
+
* Analyze routes and generate SEO issues
|
|
62
|
+
*/
|
|
63
|
+
declare function analyzeRoutes(scanResult: ScanResult, comparison?: RouteComparisonResult, verification?: RouteVerificationResult[]): SeoIssue[];
|
|
64
|
+
/**
|
|
65
|
+
* Generate routes summary
|
|
66
|
+
*/
|
|
67
|
+
declare function generateRoutesSummary(scanResult: ScanResult, comparison?: RouteComparisonResult, verification?: RouteVerificationResult[]): string;
|
|
68
|
+
|
|
69
|
+
export { type RouteComparisonResult, RouteInfo, type RouteVerificationResult, ScanResult, type SitemapUrl, type VerifyOptions, analyzeRoutes, compareWithSitemap, generateRoutesSummary, verifyRoutes };
|
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
import { existsSync, statSync, readdirSync } from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import pLimit from 'p-limit';
|
|
4
|
+
|
|
5
|
+
// src/routes/scanner.ts
|
|
6
|
+
var PAGE_FILES = ["page.tsx", "page.ts", "page.jsx", "page.js"];
|
|
7
|
+
var ROUTE_FILES = ["route.tsx", "route.ts", "route.jsx", "route.js"];
|
|
8
|
+
var SPECIAL_FILES = ["layout", "loading", "error", "not-found", "template"];
|
|
9
|
+
function findAppDir(startDir = process.cwd()) {
|
|
10
|
+
const candidates = [
|
|
11
|
+
join(startDir, "app"),
|
|
12
|
+
join(startDir, "src", "app")
|
|
13
|
+
];
|
|
14
|
+
for (const dir of candidates) {
|
|
15
|
+
if (existsSync(dir) && statSync(dir).isDirectory()) {
|
|
16
|
+
return dir;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
function scanRoutes(options = {}) {
|
|
22
|
+
const {
|
|
23
|
+
appDir = findAppDir() || "./app",
|
|
24
|
+
includeApi = true,
|
|
25
|
+
includeSpecial = false
|
|
26
|
+
} = options;
|
|
27
|
+
if (!existsSync(appDir)) {
|
|
28
|
+
throw new Error(`App directory not found: ${appDir}`);
|
|
29
|
+
}
|
|
30
|
+
const routes = [];
|
|
31
|
+
scanDirectory(appDir, "", routes, { includeApi, includeSpecial });
|
|
32
|
+
return {
|
|
33
|
+
routes,
|
|
34
|
+
staticRoutes: routes.filter((r) => !r.isDynamic && r.type === "page"),
|
|
35
|
+
dynamicRoutes: routes.filter((r) => r.isDynamic && r.type === "page"),
|
|
36
|
+
apiRoutes: routes.filter((r) => r.type === "api"),
|
|
37
|
+
appDir
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function scanDirectory(dir, routePath, routes, options) {
|
|
41
|
+
let entries;
|
|
42
|
+
try {
|
|
43
|
+
entries = readdirSync(dir);
|
|
44
|
+
} catch {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
for (const entry of entries) {
|
|
48
|
+
const fullPath = join(dir, entry);
|
|
49
|
+
let stat;
|
|
50
|
+
try {
|
|
51
|
+
stat = statSync(fullPath);
|
|
52
|
+
} catch {
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
if (stat.isDirectory()) {
|
|
56
|
+
if (entry.startsWith("_") || entry.startsWith(".")) continue;
|
|
57
|
+
if (entry.startsWith("(") && entry.endsWith(")")) {
|
|
58
|
+
scanDirectory(fullPath, routePath, routes, options);
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
if (entry.startsWith("@")) {
|
|
62
|
+
scanDirectory(fullPath, routePath, routes, options);
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
if (entry.startsWith("(") && !entry.endsWith(")")) {
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
const segment = processSegment(entry);
|
|
69
|
+
const newRoutePath = routePath + "/" + segment.urlSegment;
|
|
70
|
+
scanDirectory(fullPath, newRoutePath, routes, options);
|
|
71
|
+
} else if (stat.isFile()) {
|
|
72
|
+
if (PAGE_FILES.includes(entry)) {
|
|
73
|
+
const route = createRouteInfo(routePath || "/", dir, "page");
|
|
74
|
+
routes.push(route);
|
|
75
|
+
}
|
|
76
|
+
if (options.includeApi && ROUTE_FILES.includes(entry)) {
|
|
77
|
+
const route = createRouteInfo(routePath || "/", dir, "api");
|
|
78
|
+
routes.push(route);
|
|
79
|
+
}
|
|
80
|
+
if (options.includeSpecial) {
|
|
81
|
+
const baseName = entry.replace(/\.(tsx?|jsx?|js)$/, "");
|
|
82
|
+
if (SPECIAL_FILES.includes(baseName)) {
|
|
83
|
+
const route = createRouteInfo(routePath || "/", dir, baseName);
|
|
84
|
+
routes.push(route);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
function processSegment(segment) {
|
|
91
|
+
if (segment.startsWith("[[...") && segment.endsWith("]]")) {
|
|
92
|
+
const paramName = segment.slice(5, -2);
|
|
93
|
+
return {
|
|
94
|
+
urlSegment: segment,
|
|
95
|
+
isDynamic: true,
|
|
96
|
+
paramName,
|
|
97
|
+
isCatchAll: true,
|
|
98
|
+
isOptionalCatchAll: true
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
if (segment.startsWith("[...") && segment.endsWith("]")) {
|
|
102
|
+
const paramName = segment.slice(4, -1);
|
|
103
|
+
return {
|
|
104
|
+
urlSegment: segment,
|
|
105
|
+
isDynamic: true,
|
|
106
|
+
paramName,
|
|
107
|
+
isCatchAll: true,
|
|
108
|
+
isOptionalCatchAll: false
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
if (segment.startsWith("[") && segment.endsWith("]")) {
|
|
112
|
+
const paramName = segment.slice(1, -1);
|
|
113
|
+
return {
|
|
114
|
+
urlSegment: segment,
|
|
115
|
+
isDynamic: true,
|
|
116
|
+
paramName,
|
|
117
|
+
isCatchAll: false,
|
|
118
|
+
isOptionalCatchAll: false
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
return {
|
|
122
|
+
urlSegment: segment,
|
|
123
|
+
isDynamic: false,
|
|
124
|
+
isCatchAll: false,
|
|
125
|
+
isOptionalCatchAll: false
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
function createRouteInfo(path, filePath, type) {
|
|
129
|
+
const segments = path.split("/").filter(Boolean);
|
|
130
|
+
const dynamicSegments = [];
|
|
131
|
+
let isDynamic = false;
|
|
132
|
+
let isCatchAll = false;
|
|
133
|
+
let isOptionalCatchAll = false;
|
|
134
|
+
for (const segment of segments) {
|
|
135
|
+
const info = processSegment(segment);
|
|
136
|
+
if (info.isDynamic) {
|
|
137
|
+
isDynamic = true;
|
|
138
|
+
if (info.paramName) {
|
|
139
|
+
dynamicSegments.push(info.paramName);
|
|
140
|
+
}
|
|
141
|
+
if (info.isCatchAll) isCatchAll = true;
|
|
142
|
+
if (info.isOptionalCatchAll) isOptionalCatchAll = true;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
let routeGroup;
|
|
146
|
+
const groupMatch = filePath.match(/\(([^)]+)\)/);
|
|
147
|
+
if (groupMatch) {
|
|
148
|
+
routeGroup = groupMatch[1];
|
|
149
|
+
}
|
|
150
|
+
return {
|
|
151
|
+
path: path || "/",
|
|
152
|
+
filePath,
|
|
153
|
+
type,
|
|
154
|
+
isDynamic,
|
|
155
|
+
dynamicSegments,
|
|
156
|
+
isCatchAll,
|
|
157
|
+
isOptionalCatchAll,
|
|
158
|
+
routeGroup
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
function routeToUrl(route, params) {
|
|
162
|
+
let url = route.path;
|
|
163
|
+
for (const segment of route.dynamicSegments) {
|
|
164
|
+
const value = params?.[segment] || `{${segment}}`;
|
|
165
|
+
if (route.isOptionalCatchAll) {
|
|
166
|
+
url = url.replace(`[[...${segment}]]`, value);
|
|
167
|
+
} else if (route.isCatchAll) {
|
|
168
|
+
url = url.replace(`[...${segment}]`, value);
|
|
169
|
+
} else {
|
|
170
|
+
url = url.replace(`[${segment}]`, value);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return url;
|
|
174
|
+
}
|
|
175
|
+
function getStaticUrls(scanResult, baseUrl) {
|
|
176
|
+
return scanResult.staticRoutes.map((route) => {
|
|
177
|
+
const url = new URL(route.path, baseUrl);
|
|
178
|
+
return url.href;
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
function compareWithSitemap(scanResult, sitemapUrls, baseUrl) {
|
|
182
|
+
const appRoutes = scanResult.routes.filter((r) => r.type === "page");
|
|
183
|
+
const sitemapPaths = new Set(
|
|
184
|
+
sitemapUrls.map((url) => {
|
|
185
|
+
try {
|
|
186
|
+
return new URL(url).pathname;
|
|
187
|
+
} catch {
|
|
188
|
+
return url;
|
|
189
|
+
}
|
|
190
|
+
})
|
|
191
|
+
);
|
|
192
|
+
const missingFromSitemap = [];
|
|
193
|
+
const matching = [];
|
|
194
|
+
for (const route of scanResult.staticRoutes) {
|
|
195
|
+
const path = route.path === "/" ? "/" : route.path;
|
|
196
|
+
if (sitemapPaths.has(path) || sitemapPaths.has(path + "/") || sitemapPaths.has(path.replace(/\/$/, ""))) {
|
|
197
|
+
matching.push(route);
|
|
198
|
+
} else {
|
|
199
|
+
missingFromSitemap.push(route);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
const staticPaths = new Set(scanResult.staticRoutes.map((r) => r.path));
|
|
203
|
+
const dynamicPatterns = scanResult.dynamicRoutes.map((r) => routeToRegex(r.path));
|
|
204
|
+
const extraInSitemap = [];
|
|
205
|
+
for (const path of sitemapPaths) {
|
|
206
|
+
if (staticPaths.has(path) || staticPaths.has(path + "/") || staticPaths.has(path.replace(/\/$/, ""))) {
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
const matchesDynamic = dynamicPatterns.some((regex) => regex.test(path));
|
|
210
|
+
if (matchesDynamic) continue;
|
|
211
|
+
extraInSitemap.push(path);
|
|
212
|
+
}
|
|
213
|
+
return {
|
|
214
|
+
appRoutes,
|
|
215
|
+
sitemapUrls,
|
|
216
|
+
missingFromSitemap,
|
|
217
|
+
extraInSitemap,
|
|
218
|
+
matching
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
function routeToRegex(routePath) {
|
|
222
|
+
let pattern = routePath.replace(/[.+?^${}()|\\]/g, "\\$&").replace(/\[\[\.\.\.([^\]]+)\]\]/g, "(?:/.*)?").replace(/\[\.\.\.([^\]]+)\]/g, "/.+").replace(/\[([^\]]+)\]/g, "/[^/]+");
|
|
223
|
+
pattern = `^${pattern}/?$`;
|
|
224
|
+
return new RegExp(pattern);
|
|
225
|
+
}
|
|
226
|
+
async function verifyRoutes(scanResult, options) {
|
|
227
|
+
const {
|
|
228
|
+
baseUrl,
|
|
229
|
+
timeout = 1e4,
|
|
230
|
+
concurrency = 5,
|
|
231
|
+
staticOnly = true
|
|
232
|
+
} = options;
|
|
233
|
+
const routes = staticOnly ? scanResult.staticRoutes : scanResult.routes.filter((r) => r.type === "page");
|
|
234
|
+
const limit = pLimit(concurrency);
|
|
235
|
+
const results = await Promise.all(
|
|
236
|
+
routes.map(
|
|
237
|
+
(route) => limit(async () => {
|
|
238
|
+
const url = new URL(route.path, baseUrl).href;
|
|
239
|
+
const startTime = Date.now();
|
|
240
|
+
try {
|
|
241
|
+
const controller = new AbortController();
|
|
242
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
243
|
+
const response = await fetch(url, {
|
|
244
|
+
method: "HEAD",
|
|
245
|
+
signal: controller.signal,
|
|
246
|
+
redirect: "follow"
|
|
247
|
+
});
|
|
248
|
+
clearTimeout(timeoutId);
|
|
249
|
+
return {
|
|
250
|
+
route,
|
|
251
|
+
url,
|
|
252
|
+
statusCode: response.status,
|
|
253
|
+
isAccessible: response.status >= 200 && response.status < 400,
|
|
254
|
+
responseTime: Date.now() - startTime
|
|
255
|
+
};
|
|
256
|
+
} catch (error) {
|
|
257
|
+
return {
|
|
258
|
+
route,
|
|
259
|
+
url,
|
|
260
|
+
statusCode: 0,
|
|
261
|
+
isAccessible: false,
|
|
262
|
+
error: error.message,
|
|
263
|
+
responseTime: Date.now() - startTime
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
})
|
|
267
|
+
)
|
|
268
|
+
);
|
|
269
|
+
return results;
|
|
270
|
+
}
|
|
271
|
+
function analyzeRoutes(scanResult, comparison, verification) {
|
|
272
|
+
const issues = [];
|
|
273
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
274
|
+
if (comparison && comparison.missingFromSitemap.length > 0) {
|
|
275
|
+
for (const route of comparison.missingFromSitemap) {
|
|
276
|
+
issues.push({
|
|
277
|
+
id: `route-missing-sitemap-${route.path}`,
|
|
278
|
+
category: "indexing",
|
|
279
|
+
severity: "warning",
|
|
280
|
+
title: "Route missing from sitemap",
|
|
281
|
+
description: `Static route ${route.path} is not in sitemap.xml`,
|
|
282
|
+
url: route.path,
|
|
283
|
+
recommendation: "Add this route to your sitemap for better indexing",
|
|
284
|
+
detectedAt: now
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
if (comparison && comparison.extraInSitemap.length > 0) {
|
|
289
|
+
for (const path of comparison.extraInSitemap.slice(0, 20)) {
|
|
290
|
+
issues.push({
|
|
291
|
+
id: `sitemap-orphan-${path}`,
|
|
292
|
+
category: "indexing",
|
|
293
|
+
severity: "info",
|
|
294
|
+
title: "Sitemap URL without matching route",
|
|
295
|
+
description: `URL ${path} in sitemap doesn't match any app/ route`,
|
|
296
|
+
url: path,
|
|
297
|
+
recommendation: "Verify this URL is still valid or remove from sitemap",
|
|
298
|
+
detectedAt: now
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
if (verification) {
|
|
303
|
+
for (const result of verification) {
|
|
304
|
+
if (!result.isAccessible) {
|
|
305
|
+
issues.push({
|
|
306
|
+
id: `route-inaccessible-${result.route.path}`,
|
|
307
|
+
category: "technical",
|
|
308
|
+
severity: result.statusCode === 404 ? "error" : "warning",
|
|
309
|
+
title: `Route returns ${result.statusCode || "error"}`,
|
|
310
|
+
description: result.error || `Route ${result.route.path} returned status ${result.statusCode}`,
|
|
311
|
+
url: result.url,
|
|
312
|
+
recommendation: "Fix the route or remove it from app/",
|
|
313
|
+
detectedAt: now
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
if (scanResult.dynamicRoutes.length > scanResult.staticRoutes.length * 2) {
|
|
319
|
+
issues.push({
|
|
320
|
+
id: "too-many-dynamic-routes",
|
|
321
|
+
category: "content",
|
|
322
|
+
severity: "info",
|
|
323
|
+
title: "High ratio of dynamic routes",
|
|
324
|
+
description: `${scanResult.dynamicRoutes.length} dynamic vs ${scanResult.staticRoutes.length} static routes`,
|
|
325
|
+
url: "/",
|
|
326
|
+
recommendation: "Ensure dynamic routes have proper generateStaticParams for SSG",
|
|
327
|
+
detectedAt: now
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
return issues;
|
|
331
|
+
}
|
|
332
|
+
function generateRoutesSummary(scanResult, comparison, verification) {
|
|
333
|
+
const lines = [];
|
|
334
|
+
lines.push("## Routes Summary");
|
|
335
|
+
lines.push("");
|
|
336
|
+
lines.push(`Total routes: ${scanResult.routes.length}`);
|
|
337
|
+
lines.push(`\u251C\u2500\u2500 Static: ${scanResult.staticRoutes.length}`);
|
|
338
|
+
lines.push(`\u251C\u2500\u2500 Dynamic: ${scanResult.dynamicRoutes.length}`);
|
|
339
|
+
lines.push(`\u2514\u2500\u2500 API: ${scanResult.apiRoutes.length}`);
|
|
340
|
+
lines.push("");
|
|
341
|
+
if (comparison) {
|
|
342
|
+
lines.push("### Sitemap Comparison");
|
|
343
|
+
lines.push("");
|
|
344
|
+
lines.push(`Sitemap URLs: ${comparison.sitemapUrls.length}`);
|
|
345
|
+
lines.push(`\u251C\u2500\u2500 Matching: ${comparison.matching.length}`);
|
|
346
|
+
lines.push(`\u251C\u2500\u2500 Missing from sitemap: ${comparison.missingFromSitemap.length}`);
|
|
347
|
+
lines.push(`\u2514\u2500\u2500 Extra in sitemap: ${comparison.extraInSitemap.length}`);
|
|
348
|
+
lines.push("");
|
|
349
|
+
}
|
|
350
|
+
if (verification) {
|
|
351
|
+
const accessible = verification.filter((r) => r.isAccessible).length;
|
|
352
|
+
const broken = verification.filter((r) => !r.isAccessible);
|
|
353
|
+
lines.push("### Route Verification");
|
|
354
|
+
lines.push("");
|
|
355
|
+
lines.push(`Accessible: ${accessible}/${verification.length}`);
|
|
356
|
+
if (broken.length > 0) {
|
|
357
|
+
lines.push("");
|
|
358
|
+
lines.push("Broken routes:");
|
|
359
|
+
for (const r of broken.slice(0, 10)) {
|
|
360
|
+
lines.push(` - ${r.route.path} \u2192 ${r.statusCode || r.error}`);
|
|
361
|
+
}
|
|
362
|
+
if (broken.length > 10) {
|
|
363
|
+
lines.push(` - ... +${broken.length - 10} more`);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
return lines.join("\n");
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
export { analyzeRoutes, compareWithSitemap, findAppDir, generateRoutesSummary, getStaticUrls, routeToUrl, scanRoutes, verifyRoutes };
|
|
371
|
+
//# sourceMappingURL=index.mjs.map
|
|
372
|
+
//# sourceMappingURL=index.mjs.map
|