@blaaiz/docs-core 0.7.6 → 0.8.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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/generator/index.ts"],"names":[],"mappings":";;;;AAgJA,SAAS,UAAU,KAAA,EAAuB;AACxC,EAAA,OAAO,KAAA,CACJ,OAAA,CAAQ,oBAAA,EAAsB,OAAO,CAAA,CACrC,WAAA,EAAY,CACZ,OAAA,CAAQ,aAAA,EAAe,GAAG,CAAA,CAC1B,OAAA,CAAQ,YAAY,EAAE,CAAA;AAC3B;AAOA,SAAS,QAAA,CACP,SAAA,EACA,MAAA,EACA,OAAA,EACQ;AACR,EAAA,KAAA,MAAW,aAAa,CAAC,SAAA,CAAU,OAAA,EAAS,SAAA,CAAU,WAAW,CAAA,EAAG;AAClE,IAAA,IAAI,cAAc,MAAA,EAAW;AAC3B,MAAA,MAAM,IAAA,GAAO,UAAU,SAAS,CAAA;AAChC,MAAA,IAAI,SAAS,EAAA,EAAI;AACf,QAAA,OAAO,IAAA;AAAA,MACT;AAAA,IACF;AAAA,EACF;AACA,EAAA,OAAO,SAAA,CAAU,CAAA,EAAG,MAAM,CAAA,CAAA,EAAI,OAAO,CAAA,CAAE,CAAA;AACzC;AAOA,SAAS,iBAAiB,MAAA,EAA+B;AACvD,EAAA,MAAM,KAAA,GAAQ,6BAAA,CAA8B,IAAA,CAAK,MAAM,CAAA;AACvD,EAAA,IAAI,CAAC,KAAA,GAAQ,CAAC,CAAA,EAAG;AACf,IAAA,OAAO,IAAA;AAAA,EACT;AACA,EAAA,MAAM,IAAA,GAAO,wBAAA,CAAyB,IAAA,CAAK,KAAA,CAAM,CAAC,CAAC,CAAA;AACnD,EAAA,IAAI,CAAC,IAAA,GAAO,CAAC,CAAA,EAAG;AACd,IAAA,OAAO,IAAA;AAAA,EACT;AACA,EAAA,OAAO,IAAA,CAAK,CAAC,CAAA,CAAE,OAAA,CAAQ,kBAAkB,IAAI,CAAA;AAC/C;AAGA,SAAS,eAAe,OAAA,EAAgC;AACtD,EAAA,MAAM,KAAA,GAAQ,+BAAA,CAAgC,IAAA,CAAK,OAAO,CAAA;AAC1D,EAAA,IAAI,CAAC,KAAA,GAAQ,CAAC,CAAA,EAAG;AACf,IAAA,OAAO,IAAA;AAAA,EACT;AACA,EAAA,IAAI;AACF,IAAA,MAAM,GAAA,GAAM,IAAA,CAAK,KAAA,CAAM,KAAA,CAAM,CAAC,CAAC,CAAA;AAC/B,IAAA,MAAM,EAAA,GAAK,IAAI,CAAC,CAAA;AAChB,IAAA,OAAO,EAAA,GAAK,GAAG,EAAA,CAAG,MAAA,CAAO,aAAa,CAAA,CAAA,EAAI,EAAA,CAAG,IAAI,CAAA,CAAA,GAAK,IAAA;AAAA,EACxD,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,IAAA;AAAA,EACT;AACF;AAMA,IAAM,eAAA,GAA+B,EAAE,KAAA,EAAO,kBAAA,EAAoB,SAAS,OAAA,EAAQ;AAGnF,SAAS,eAAe,QAAA,EAAiD;AACvE,EAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,SAAS,CAAA,IAAK,aAAA;AACrC,EAAA,IAAI,KAAA,KAAU,aAAA,IAAiB,KAAA,KAAU,UAAA,EAAY;AACnD,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,CAAA,mCAAA,EAAsC,IAAA,CAAK,SAAA,CAAU,KAAK,CAAC,CAAA,qGAAA;AAAA,KAE7D;AAAA,EACF;AACA,EAAA,OAAO,KAAA;AACT;AAMA,SAAS,eAAA,CAAgB,OAAgB,YAAA,EAAuC;AAC9E,EAAA,MAAM,OAAA,GAAU,CAAA,oBAAA,EAAuB,MAAA,CAAO,YAAY,CAAC,CAAA,YAAA,CAAA;AAC3D,EAAA,IAAI,OAAO,KAAA,KAAU,QAAA,IAAY,KAAA,KAAU,IAAA,EAAM;AAC/C,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,2EAA2E,OAAO,CAAA,iFAAA;AAAA,KAEpF;AAAA,EACF;AACA,EAAA,MAAM,SAAA,GAAY,KAAA;AAClB,EAAA,KAAA,MAAW,IAAA,IAAQ,CAAC,eAAA,EAAiB,eAAe,CAAA,EAAY;AAC9D,IAAA,IAAI,OAAO,SAAA,CAAU,IAAI,CAAA,KAAM,UAAA,EAAY;AACzC,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAAA,uCAAA,EAA0C,IAAI,CAAA,4BAAA,EAA+B,OAAO,CAAA,SAAA,EACxE,IAAI,CAAA,MAAA,EAAS,IAAA,KAAS,eAAA,GAAkB,2BAAA,GAA8B,oBAAoB,CAAA,CAAA;AAAA,OACxG;AAAA,IACF;AAAA,EACF;AACA,EAAA,OAAO,KAAA;AACT;AA0BA,eAAsB,iBACpB,OAAA,EACiC;AACjC,EAAA,MAAM,WAAW,OAAA,CAAQ,QAAA,IAAY,KAAK,IAAA,CAAK,OAAA,CAAQ,YAAY,WAAW,CAAA;AAC9E,EAAA,MAAM,SAAS,OAAA,CAAQ,MAAA,IAAU,KAAK,IAAA,CAAK,OAAA,CAAQ,YAAY,MAAM,CAAA;AACrE,EAAA,MAAM,MAAA,GAAS,QAAQ,MAAA,IAAU,KAAA;AAEjC,EAAA,MAAM,SAAS,IAAA,CAAK,KAAA,CAAM,MAAM,QAAA,CAAS,QAAA,EAAU,MAAM,CAAC,CAAA;AAC1D,EAAA,MAAM,UAAA,GAAa,gBAAgB,MAAM,CAAA;AACzC,EAAA,MAAM,OAAA,GAAU,eAAe,MAAM,CAAA;AACrC,EAAA,MAAM,WAAW,OAAA,KAAY,UAAA;AAC7B,EAAA,MAAM,OAAO,YAAA,CAAa,UAAA,EAAY,EAAE,MAAA,EAAQ,SAAS,CAAA;AAGzD,EAAA,MAAM,UAAA,uBAAiB,GAAA,EAA0D;AACjF,EAAA,MAAM,UAAA,uBAAiB,GAAA,EAA+C;AACtE,EAAA,IAAI,KAAA,GAAQ,CAAA;AACZ,EAAA,IAAI,QAAA;AAEJ,EAAA,IAAI,IAAA,CAAK,QAAA,CAAS,MAAA,KAAW,CAAA,EAAG;AAI9B,IAAA,QAAA,GAAW,qBAAA,CAAsB,EAAC,EAAG,EAAE,MAAM,OAAA,CAAQ,IAAA,IAAQ,iBAAiB,CAAA;AAAA,EAChF,CAAA,MAAO;AACL,IAAA,MAAM,WAAW,eAAA,CAAgB,OAAA,CAAQ,QAAA,EAAU,IAAA,CAAK,SAAS,MAAM,CAAA;AACvE,IAAA,IAAI,OAAA,CAAQ,SAAS,MAAA,EAAW;AAC9B,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAAA,wFAAA,EACe,MAAA,CAAO,IAAA,CAAK,QAAA,CAAS,MAAM,CAAC,CAAA,sEAAA;AAAA,OAE7C;AAAA,IACF;AAGA,IAAA,MAAM,IAAA,GAAO,MAAM,OAAA,CAAQ,GAAA;AAAA,MACzB,KAAK,KAAA,CAAM,GAAA;AAAA,QACT,OAAO,IAAA,KACL,IAAA,CAAK,KAAA,CAAM,MAAM,QAAA,CAAS,IAAA,CAAK,IAAA,CAAK,OAAA,CAAQ,UAAA,EAAY,IAAI,CAAA,EAAG,MAAM,CAAC;AAAA;AAC1E,KACF;AACA,IAAA,QAAA,GAAW,sBAAsB,IAAA,EAAM;AAAA,MACrC,MAAM,OAAA,CAAQ,IAAA;AAAA,MACd,GAAI,QAAQ,OAAA,GAAU,EAAE,SAAS,OAAA,CAAQ,OAAA,KAAY;AAAC,KACvD,CAAA;AAID,IAAA,MAAM,KAAA,uBAAY,GAAA,EAAoB;AACtC,IAAA,KAAA,MAAW,IAAA,IAAQ,KAAK,QAAA,EAAU;AAChC,MAAA,MAAM,SAAA,GAAY,SAAS,KAAA,CAAM,IAAA,CAAK,OAAO,CAAA,GAAI,IAAA,CAAK,MAAA,CAAO,WAAA,EAAa,CAAA;AAE1E,MAAA,IAAI,CAAC,SAAA,EAAW;AACd,QAAA,MAAM,IAAI,KAAA;AAAA,UACR,CAAA,iCAAA,EAAoC,KAAK,MAAM,CAAA,CAAA,EAAI,KAAK,OAAO,CAAA,MAAA,EACtD,KAAK,IAAI,CAAA,2BAAA;AAAA,SACpB;AAAA,MACF;AACA,MAAA,MAAM,OAAO,QAAA,CAAS,SAAA,EAAW,IAAA,CAAK,MAAA,EAAQ,KAAK,OAAO,CAAA;AAC1D,MAAA,MAAM,GAAA,GAAM,CAAA,EAAG,IAAA,CAAK,GAAG,IAAI,IAAI,CAAA,CAAA;AAC/B,MAAA,MAAM,MAAA,GAAS,KAAA,CAAM,GAAA,CAAI,GAAG,CAAA;AAC5B,MAAA,IAAI,WAAW,MAAA,EAAW;AACxB,QAAA,MAAM,IAAI,KAAA;AAAA,UACR,CAAA,YAAA,EAAe,MAAM,CAAA,KAAA,EAAQ,IAAA,CAAK,MAAA,CAAO,WAAA,EAAa,CAAA,CAAA,EAAI,IAAA,CAAK,OAAO,CAAA,2BAAA,EAC5C,GAAG,CAAA,uCAAA;AAAA,SAC/B;AAAA,MACF;AACA,MAAA,KAAA,CAAM,GAAA,CAAI,GAAA,EAAK,CAAA,EAAG,IAAA,CAAK,MAAA,CAAO,aAAa,CAAA,CAAA,EAAI,IAAA,CAAK,OAAO,CAAA,CAAE,CAAA;AAC7D,MAAA,UAAA,CAAW,GAAA,CAAI,CAAA,EAAG,IAAA,CAAK,MAAA,CAAO,aAAa,CAAA,CAAA,EAAI,IAAA,CAAK,OAAO,CAAA,CAAA,EAAI,EAAE,GAAG,IAAA,EAAM,MAAM,CAAA;AAChF,MAAA,MAAM,QAAQ,UAAA,CAAW,GAAA,CAAI,IAAA,CAAK,GAAG,KAAK,EAAC;AAC3C,MAAA,KAAA,CAAM,KAAK,EAAE,KAAA,EAAO,IAAA,CAAK,KAAA,EAAO,MAAM,CAAA;AACtC,MAAA,UAAA,CAAW,GAAA,CAAI,IAAA,CAAK,GAAA,EAAK,KAAK,CAAA;AAAA,IAChC;AAGA,IAAA,KAAA,MAAW,KAAA,IAAS,KAAK,SAAA,EAAW;AAClC,MAAA,IAAI,MAAM,IAAA,EAAM;AACd,QAAA,MAAM,EAAA,CAAG,IAAA,CAAK,IAAA,CAAK,MAAA,EAAQ,KAAA,CAAM,GAAG,CAAA,EAAG,EAAE,SAAA,EAAW,IAAA,EAAM,KAAA,EAAO,IAAA,EAAM,CAAA;AAAA,MACzE;AAAA,IACF;AACA,IAAA,MAAM,gBAAgB,QAAA,CAAS,aAAA;AAC/B,IAAA,MAAM,gBAAgB,QAAA,CAAS,aAAA;AAC/B,IAAA,MAAM,aAAA,CAAc;AAAA,MAClB,OAAO,aAAA,CAAc;AAAA,QACnB,KAAA,EAAO,EAAE,MAAA,EAAQ,QAAA,EAAS;AAAA,QAC1B,GAAI,QAAQ,QAAA,GAAW,EAAE,UAAU,OAAA,CAAQ,QAAA,KAAa;AAAC,OAC1D,CAAA;AAAA,MACD,MAAA,EAAQ,MAAA;AAAA,MACR,GAAA,EAAK,WAAA;AAAA,MACL,OAAA,EAAS,MAAA;AAAA,MACT,YAAY,KAAA,EAAwB;AAClC,QAAA,KAAA,IAAS,IAAI,KAAA,CAAM,MAAA,GAAS,GAAG,CAAA,IAAK,CAAA,EAAG,KAAK,CAAA,EAAG;AAC7C,UAAA,MAAM,IAAA,GAAO,MAAM,CAAC,CAAA;AACpB,UAAA,IAAI,CAAC,IAAA,EAAM;AACT,YAAA;AAAA,UACF;AACA,UAAA,MAAM,GAAA,GAAM,cAAA,CAAe,IAAA,CAAK,OAAO,CAAA;AACvC,UAAA,MAAM,SAAA,GAAY,GAAA,GAAM,UAAA,CAAW,GAAA,CAAI,GAAG,CAAA,GAAI,MAAA;AAC9C,UAAA,IAAI,CAAC,SAAA,EAAW;AACd,YAAA,KAAA,CAAM,MAAA,CAAO,GAAG,CAAC,CAAA;AACjB,YAAA;AAAA,UACF;AACA,UAAA,IAAA,CAAK,IAAA,GAAO,KAAK,IAAA,CAAK,SAAA,CAAU,KAAK,CAAA,EAAG,SAAA,CAAU,IAAI,CAAA,IAAA,CAAM,CAAA;AAC5D,UAAA,KAAA,IAAS,CAAA;AAAA,QACX;AAAA,MACF;AAAA,KACD,CAAA;AAAA,EACH;AAIA,EAAA,MAAM,WAAW,OAAA,CAAQ,OAAA,IAAW,OAAA,EAAS,OAAA,CAAQ,OAAO,EAAE,CAAA;AAC9D,EAAA,MAAM,MAAA,GAAS,CAAC,IAAA,KAAyB;AACvC,IAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,OAAA,CAAQ,WAAA,EAAa,EAAE,CAAA;AAC1C,IAAA,OAAO,KAAA,GAAQ,CAAA,EAAG,OAAO,CAAA,CAAA,EAAI,KAAK,CAAA,CAAA,GAAK,OAAA;AAAA,EACzC,CAAA;AACA,EAAA,KAAA,MAAW,IAAA,IAAQ,KAAK,WAAA,EAAa;AACnC,IAAA,MAAM,SAAS,IAAA,CAAK,IAAA,CAAK,QAAQ,CAAA,EAAG,IAAA,CAAK,IAAI,CAAA,IAAA,CAAM,CAAA;AACnD,IAAA,IAAI,KAAA,GAAuB,IAAA;AAC3B,IAAA,IAAI;AACF,MAAA,KAAA,GAAQ,gBAAA,CAAiB,MAAM,QAAA,CAAS,MAAA,EAAQ,MAAM,CAAC,CAAA;AAAA,IACzD,CAAA,CAAA,MAAQ;AACN,MAAA,OAAA,CAAQ,IAAA;AAAA,QACN,CAAA,4BAAA,EAA+B,IAAA,CAAK,IAAI,CAAA,wBAAA,EACnC,MAAM,CAAA,8CAAA;AAAA,OACb;AACA,MAAA;AAAA,IACF;AACA,IAAA,IAAI,UAAU,IAAA,EAAM;AAClB,MAAA,KAAA,GAAQ,KAAK,IAAA,CAAK,KAAA,CAAM,GAAG,CAAA,CAAE,GAAA,MAAS,IAAA,CAAK,IAAA;AAC3C,MAAA,OAAA,CAAQ,IAAA;AAAA,QACN,CAAA,YAAA,EAAe,IAAA,CAAK,IAAI,CAAA,gEAAA,EACE,KAAK,CAAA,EAAA;AAAA,OACjC;AAAA,IACF;AACA,IAAA,MAAM,QAAQ,UAAA,CAAW,GAAA,CAAI,IAAA,CAAK,GAAG,KAAK,EAAC;AAE3C,IAAA,KAAA,CAAM,IAAA,CAAK;AAAA,MACT,OAAO,IAAA,CAAK,KAAA;AAAA,MACZ,IAAA,EAAM,CAAA,CAAA,EAAI,KAAA,CAAM,OAAA,CAAQ,UAAA,EAAY,EAAE,CAAC,CAAA,EAAA,EAAK,MAAA,CAAO,IAAA,CAAK,IAAI,CAAC,CAAA,CAAA;AAAA,KAC9D,CAAA;AACD,IAAA,UAAA,CAAW,GAAA,CAAI,IAAA,CAAK,GAAA,EAAK,KAAK,CAAA;AAAA,EAChC;AAIA,EAAA,MAAM,KAAA,GAAQ;AAAA,IACZ,GAAG,IAAA,CAAK,KAAA;AAAA,IACR,GAAG,IAAA,CAAK,SAAA,CAAU,GAAA,CAAI,CAAC,KAAA,MAAW;AAAA,MAChC,IAAA,EAAM,CAAA,EAAG,KAAA,CAAM,GAAG,CAAA,UAAA,CAAA;AAAA,MAClB,OAAA,EAAS;AAAA,QACP,OAAO,KAAA,CAAM,KAAA;AAAA,QACb,GAAI,KAAA,CAAM,IAAA,GAAO,EAAE,IAAA,EAAM,IAAA,KAAS,EAAC;AAAA,QACnC,GAAI,MAAM,IAAA,GAAO,EAAE,MAAM,KAAA,CAAM,IAAA,KAAS,EAAC;AAAA;AAAA;AAAA,QAGzC,GAAI,YAAY,CAAC,KAAA,CAAM,OAAO,EAAE,WAAA,EAAa,KAAA,EAAM,GAAI,EAAC;AAAA,QACxD,KAAA,EAAO;AAAA,UACL,GAAA,CAAI,UAAA,CAAW,GAAA,CAAI,KAAA,CAAM,GAAG,KAAK,EAAC,EAAG,GAAA,CAAI,CAAC,IAAA,MAAU;AAAA,YAClD,OAAO,IAAA,CAAK,KAAA;AAAA,YACZ,KAAA,EAAO,CAAC,IAAA,CAAK,IAAI;AAAA,WACnB,CAAE,CAAA;AAAA,UACF,GAAG,KAAA,CAAM,QAAA,CAAS,GAAA,CAAI,CAAC,KAAA,MAAW;AAAA,YAChC,OAAO,KAAA,CAAM,KAAA;AAAA;AAAA;AAAA,YAGb,OACE,QAAA,IAAY,KAAA,CAAM,IAAA,GACd,CAAC,MAAM,KAAA,CAAM,KAAK,CAAA,GAAA,CAAA,EAAO,CAAA,GAAA,EAAM,MAAM,MAAM,CAAA,CAAE,CAAA,GAC7C,CAAC,MAAM,MAAM;AAAA,WACrB,CAAE;AAAA,SACJ,CACG,IAAA,CAAK,CAAC,CAAA,EAAG,MAAM,CAAA,CAAE,KAAA,GAAQ,CAAA,CAAE,KAAK,CAAA,CAChC,OAAA,CAAQ,CAAC,KAAA,KAAU,MAAM,KAAK;AAAA;AACnC,KACF,CAAE;AAAA,GACJ;AACA,EAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACxB,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,IAAA,CAAK,MAAA,EAAQ,KAAK,IAAI,CAAA;AAC1C,IAAA,MAAM,KAAA,CAAM,KAAK,OAAA,CAAQ,MAAM,GAAG,EAAE,SAAA,EAAW,MAAM,CAAA;AACrD,IAAA,MAAM,SAAA,CAAU,QAAQ,CAAA,EAAG,IAAA,CAAK,UAAU,IAAA,CAAK,OAAA,EAAS,IAAA,EAAM,CAAC,CAAC;AAAA,CAAI,CAAA;AAAA,EACtE;AAIA,EAAA,MAAM,QAAA,GAAW,CAAC,KAAA,KAAkD;AAClE,IAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACxB,MAAA,IAAI,IAAA,CAAK,SAAS,OAAA,EAAS;AACzB,QAAA,MAAM,GAAA,GAAM,QAAA,CAAS,IAAA,CAAK,KAAK,CAAA;AAC/B,QAAA,IAAI,GAAA,EAAK;AACP,UAAA,OAAO,GAAA;AAAA,QACT;AAAA,MACF,CAAA,MAAA,IAAW,IAAA,CAAK,IAAA,KAAS,KAAA,EAAO;AAC9B,QAAA,OAAO,MAAA,CAAO,KAAK,IAAI,CAAA;AAAA,MACzB,CAAA,MAAO;AACL,QAAA,MAAM,SAAA,GAAY,UAAA,CAAW,GAAA,CAAI,CAAA,EAAG,IAAA,CAAK,MAAA,CAAO,WAAA,EAAa,CAAA,CAAA,EAAI,IAAA,CAAK,OAAO,CAAA,CAAE,CAAA;AAC/E,QAAA,IAAI,SAAA,EAAW;AACb,UAAA,OAAO,GAAG,OAAO,CAAA,CAAA,EAAI,UAAU,GAAG,CAAA,CAAA,EAAI,UAAU,IAAI,CAAA,CAAA;AAAA,QACtD;AAAA,MACF;AAAA,IACF;AACA,IAAA,OAAO,MAAA;AAAA,EACT,CAAA;AACA,EAAA,MAAM,cAAc,CAAC,KAAA,KACnB,KAAA,CAAM,OAAA,CAAQ,CAAC,IAAA,KAAS;AACtB,IAAA,IAAI,IAAA,CAAK,SAAS,OAAA,EAAS;AACzB,MAAA,OAAO,WAAA,CAAY,KAAK,KAAK,CAAA;AAAA,IAC/B;AACA,IAAA,OAAO,KAAK,IAAA,KAAS,KAAA,GAAQ,CAAC,IAAA,CAAK,IAAI,IAAI,EAAC;AAAA,EAC9C,CAAC,CAAA;AAMH,EAAA,MAAM,WAAA,GAAc,IAAA,CAAK,SAAA,CAAU,MAAA,CAAO,CAAC,KAAA,KAAU,KAAA,CAAM,IAAI,CAAA,CAAE,GAAA,CAAI,CAAC,KAAA,KAAU,MAAM,GAAG,CAAA;AACzF,EAAA,IAAI,WAAA,GAAc,CAAA;AAClB,EAAA,MAAM,WAAsB,EAAC;AAC7B,EAAA,KAAA,MAAW,OAAO,UAAA,EAAY;AAC5B,IAAA,IAAI,KAAA;AACJ,IAAA,IAAI,UAAA,CAAW,GAAA,CAAI,KAAK,CAAA,EAAG;AACzB,MAAA,MAAM,IAAA,GAAO,YAAY,WAAW,CAAA;AACpC,MAAA,WAAA,IAAe,CAAA;AACf,MAAA,IAAI,SAAS,MAAA,EAAW;AACtB,QAAA,KAAA,GAAQ;AAAA,UACN,uBAAO,GAAA,CAAI,CAAC,GAAG,OAAO,CAAA,CAAA,EAAI,IAAI,CAAA,CAAA,EAAI,GAAG,YAAY,GAAA,CAAI,KAAK,EAAE,GAAA,CAAI,CAAC,SAAS,MAAA,CAAO,IAAI,CAAC,CAAC,CAAC;AAAA,SAC1F;AAAA,MACF;AAAA,IACF;AACA,IAAA,MAAM,GAAA,GAAM,QAAA,CAAS,GAAA,CAAI,KAAK,CAAA;AAC9B,IAAA,IAAI,CAAC,GAAA,EAAK;AACR,MAAA;AAAA,IACF;AACA,IAAA,QAAA,CAAS,IAAA,CAAK;AAAA,MACZ,MAAM,GAAA,CAAI,KAAA;AAAA,MACV,GAAA;AAAA,MACA,GAAI,IAAI,IAAA,GAAO,EAAE,MAAM,GAAA,CAAI,IAAA,KAAS,EAAC;AAAA,MACrC,GAAI,KAAA,GAAQ,EAAE,KAAA,KAAU;AAAC,KAC1B,CAAA;AAAA,EACH;AACA,EAAA,MAAM,SAAA;AAAA,IACJ,IAAA,CAAK,IAAA,CAAK,OAAA,CAAQ,UAAA,EAAY,UAAU,CAAA;AAAA,IACxC,GAAG,IAAA,CAAK,SAAA,CAAU,QAAA,EAAU,IAAA,EAAM,CAAC,CAAC;AAAA;AAAA,GACtC;AAIA,EAAA,MAAM,eAAuC,EAAC;AAC9C,EAAA,MAAM,eAAgF,EAAC;AACvF,EAAA,KAAA,MAAW,IAAA,IAAQ,KAAK,QAAA,EAAU;AAChC,IAAA,MAAM,SAAA,GAAY,UAAA,CAAW,GAAA,CAAI,CAAA,EAAG,IAAA,CAAK,MAAA,CAAO,WAAA,EAAa,CAAA,CAAA,EAAI,IAAA,CAAK,OAAO,CAAA,CAAE,CAAA;AAC/E,IAAA,IAAI,SAAA,EAAW;AACb,MAAA,MAAM,GAAA,GAAM,GAAG,OAAO,CAAA,CAAA,EAAI,UAAU,GAAG,CAAA,CAAA,EAAI,UAAU,IAAI,CAAA,CAAA;AACzD,MAAA,YAAA,CAAa,GAAG,CAAA,GAAI,IAAA,CAAK,MAAA,CAAO,WAAA,EAAY;AAC5C,MAAA,YAAA,CAAa,GAAG,CAAA,GAAI;AAAA,QAClB,MAAM,IAAA,CAAK,IAAA;AAAA,QACX,MAAA,EAAQ,IAAA,CAAK,MAAA,CAAO,WAAA,EAAY;AAAA,QAChC,OAAO,IAAA,CAAK;AAAA,OACd;AAAA,IACF;AAAA,EACF;AAMA,EAAA,MAAM,WAAA,GAAc,IAAA,CAAK,IAAA,CAAK,OAAA,CAAQ,YAAY,kBAAkB,CAAA;AACpE,EAAA,MAAM,aAAA,GAAgB,IAAA,CAAK,IAAA,CAAK,OAAA,CAAQ,YAAY,gBAAgB,CAAA;AACpE,EAAA,MAAM,eAAA,GACJ,MAAM,QAAA,CAA4D,WAAW,CAAA;AAC/E,EAAA,MAAM,SAAA,GAAa,MAAM,QAAA,CAAiC,aAAa,KAAM,EAAC;AAC9E,EAAA,IAAI,eAAA,EAAiB;AACnB,IAAA,MAAM,iBAAiB,IAAI,GAAA;AAAA,MACzB,OAAO,OAAA,CAAQ,YAAY,EAAE,GAAA,CAAI,CAAC,CAAC,GAAA,EAAK,CAAC,MAAM,CAAC,CAAA,EAAG,EAAE,MAAM,CAAA,CAAA,EAAI,EAAE,KAAK,CAAA,CAAA,EAAI,GAAG,CAAC;AAAA,KAChF;AACA,IAAA,KAAA,MAAW,CAAC,MAAA,EAAQ,MAAM,KAAK,MAAA,CAAO,OAAA,CAAQ,eAAe,CAAA,EAAG;AAC9D,MAAA,MAAM,MAAA,GAAS,eAAe,GAAA,CAAI,CAAA,EAAG,OAAO,MAAM,CAAA,CAAA,EAAI,MAAA,CAAO,KAAK,CAAA,CAAE,CAAA;AACpE,MAAA,IAAI,MAAA,KAAW,MAAA,IAAa,MAAA,KAAW,MAAA,EAAQ;AAC7C,QAAA,SAAA,CAAU,MAAM,CAAA,GAAI,MAAA;AAAA,MACtB;AAAA,IACF;AAAA,EACF;AAGA,EAAA,MAAM,gBAAA,GAAmB,CAAC,IAAA,KAAgC;AACxD,IAAA,MAAM,IAAA,mBAAO,IAAI,GAAA,CAAI,CAAC,IAAI,CAAC,CAAA;AAC3B,IAAA,IAAI,OAAA,GAAU,UAAU,IAAI,CAAA;AAC5B,IAAA,OAAO,OAAA,KAAY,MAAA,IAAa,EAAE,OAAA,IAAW,YAAA,CAAA,EAAe;AAC1D,MAAA,IAAI,IAAA,CAAK,GAAA,CAAI,OAAO,CAAA,EAAG;AACrB,QAAA,OAAO,IAAA;AAAA,MACT;AACA,MAAA,IAAA,CAAK,IAAI,OAAO,CAAA;AAChB,MAAA,OAAA,GAAU,UAAU,OAAO,CAAA;AAAA,IAC7B;AACA,IAAA,OAAO,OAAA,IAAW,IAAA;AAAA,EACpB,CAAA;AACA,EAAA,MAAM,gBAAwC,EAAC;AAC/C,EAAA,KAAA,MAAW,MAAA,IAAU,MAAA,CAAO,IAAA,CAAK,SAAS,CAAA,EAAG;AAC3C,IAAA,IAAI,UAAU,YAAA,EAAc;AAC1B,MAAA;AAAA,IACF;AACA,IAAA,MAAM,WAAA,GAAc,iBAAiB,MAAM,CAAA;AAC3C,IAAA,IAAI,WAAA,KAAgB,IAAA,IAAQ,WAAA,KAAgB,MAAA,EAAQ;AAClD,MAAA,aAAA,CAAc,MAAM,CAAA,GAAI,WAAA;AAAA,IAC1B;AAAA,EACF;AAEA,EAAA,MAAM,SAAA;AAAA,IACJ,IAAA,CAAK,IAAA,CAAK,OAAA,CAAQ,UAAA,EAAY,kBAAkB,CAAA;AAAA,IAChD,GAAG,IAAA,CAAK,SAAA,CAAU,YAAA,EAAc,IAAA,EAAM,CAAC,CAAC;AAAA;AAAA,GAC1C;AACA,EAAA,MAAM,SAAA,CAAU,aAAa,CAAA,EAAG,IAAA,CAAK,UAAU,YAAA,EAAc,IAAA,EAAM,CAAC,CAAC;AAAA,CAAI,CAAA;AACzE,EAAA,MAAM,SAAA,CAAU,eAAe,CAAA,EAAG,IAAA,CAAK,UAAU,aAAA,EAAe,IAAA,EAAM,CAAC,CAAC;AAAA,CAAI,CAAA;AAE5E,EAAA,OAAO;AAAA,IACL,KAAA;AAAA,IACA,OAAO,KAAA,CAAM,MAAA;AAAA,IACb,QAAA;AAAA,IACA,YAAA;AAAA,IACA,YAAA;AAAA,IACA,SAAA,EAAW,aAAA;AAAA,IACX;AAAA,GACF;AACF;AAGA,eAAe,SAAY,IAAA,EAAiC;AAC1D,EAAA,IAAI;AACF,IAAA,OAAO,KAAK,KAAA,CAAM,MAAM,QAAA,CAAS,IAAA,EAAM,MAAM,CAAC,CAAA;AAAA,EAChD,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,IAAA;AAAA,EACT;AACF","file":"generator.js","sourcesContent":["/**\n * `@blaaiz/docs-core/generator` — builds the docs content tree from `docs.json`.\n *\n * One call turns the site's navigation file into everything fumadocs needs:\n * generated API pages placed in their configured groups, and `meta.json` files\n * encoding tab and group order. Authors reorganize the docs by editing\n * `docs.json` — never by moving files.\n *\n * Node-only. A site that publishes an API reference injects `createOpenAPI` and\n * `generateFiles` from its own `fumadocs-openapi` install, so this package takes\n * no dependency on it. A site of prose alone injects nothing: with no API pages\n * in `docs.json` the generator writes the `meta.json` files and `nav.json` and\n * never touches `fumadocs-openapi`.\n *\n * @packageDocumentation\n */\n\nimport { mkdir, readFile, rm, writeFile } from 'node:fs/promises';\nimport path from 'node:path';\n\nimport { mergeOpenApiDocuments } from '../adapters/index.js';\nimport { parseNavigation } from '../adapters/index.js';\nimport type {\n NavItem,\n OpenApiDocument,\n OpenApiInfo,\n OpenApiServer,\n SidebarStyle,\n} from '../core/index.js';\nimport { planDocsTree } from '../core/index.js';\nimport { hasOpenApi } from '../core/docs-tree.js';\n\n/**\n * The two fumadocs-openapi functions the generator needs, injected by the site\n * (`createOpenAPI` from `fumadocs-openapi/server`, `generateFiles` from\n * `fumadocs-openapi`). Typed loosely so this package needs no fumadocs types.\n *\n * @public\n */\nexport interface FumadocsOpenApi {\n readonly createOpenAPI: (...args: never[]) => unknown;\n readonly generateFiles: (...args: never[]) => unknown;\n}\n\n/**\n * Options for {@link generateDocsTree}.\n *\n * @public\n */\nexport interface GenerateDocsTreeOptions {\n /** The site's content directory (holds `docs.json`, spec files, and `docs/`). */\n readonly contentDir: string;\n /** Path to the navigation file. Default `<contentDir>/docs.json`. */\n readonly docsJson?: string;\n /** Output directory for the page tree. Default `<contentDir>/docs`. */\n readonly outDir?: string;\n /** Folder for generated API pages. Default `api`. */\n readonly apiDir?: string;\n /**\n * `info` block of the merged OpenAPI document. Required when `docs.json`\n * publishes API pages; ignored on a site of prose alone, which merges no\n * documents.\n */\n readonly info?: OpenApiInfo;\n /** Servers of the merged document. */\n readonly servers?: readonly OpenApiServer[];\n /** Same-origin proxy route for the try-it playground. */\n readonly proxyUrl?: string;\n /** URL base the fumadocs loader serves pages under. Default `/docs`. */\n readonly baseUrl?: string;\n /**\n * The site's fumadocs-openapi functions. Required when `docs.json` publishes\n * API pages; omit it on a site of prose alone.\n */\n readonly fumadocs?: FumadocsOpenApi;\n}\n\n/**\n * A top-navigation link: one tab, pointing at its first page.\n *\n * @public\n */\nexport interface NavLink {\n readonly text: string;\n readonly url: string;\n /** The tab's icon name from `docs.json`, for the site to resolve. */\n readonly icon?: string;\n /**\n * Path prefixes the link is active for. Emitted for an API tab, whose\n * territory the generator knows exactly: the tab's generated folder plus\n * the folders of any doc pages it declares. `docsNavLinks` derives the\n * section from the URL when this is absent.\n */\n readonly match?: readonly string[];\n}\n\n/**\n * What the generator produced.\n *\n * @public\n */\nexport interface GenerateDocsTreeResult {\n readonly pages: number;\n readonly metas: number;\n /**\n * Top-nav links, one per tab, each pointing at the tab's first page — written\n * to `<contentDir>/nav.json` for the site's layout. Keeps the top nav in sync\n * with `docs.json` instead of a hand-maintained list.\n */\n readonly navLinks: readonly NavLink[];\n /**\n * Page URL to HTTP method, written to `<contentDir>/api-methods.json` for the\n * sidebar method badges (`apiMethodBadges` in `@blaaiz/docs-core/ui`).\n */\n readonly methodsByUrl: Readonly<Record<string, string>>;\n /**\n * Page URL to its operation's source, written to\n * `<contentDir>/api-sources.json` — the Markdown route serves the original\n * spec from it.\n */\n readonly sourcesByUrl: Readonly<\n Record<string, { readonly file: string; readonly method: string; readonly route: string }>\n >;\n /**\n * Old page URL to its current one, written to `<contentDir>/redirects.json`.\n * A renamed operation (its summary edited, its page moved) leaves a\n * forwarding address here; the site's middleware serves permanent redirects\n * from it through `redirectTarget`.\n */\n readonly redirects: Readonly<Record<string, string>>;\n /** The merged OpenAPI document (reusable for the runtime page renderer). */\n readonly document: OpenApiDocument;\n}\n\ninterface GeneratedFile {\n path: string;\n content: string;\n}\n\n/**\n * Kebab-case free text, splitting camelCase words. Distinct from the\n * planner's `slugify`, which names folders and must not re-split names that\n * already exist on disk.\n */\nfunction kebabCase(value: string): string {\n return value\n .replace(/([a-z0-9])([A-Z])/g, '$1 $2')\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '');\n}\n\n/**\n * The page slug for one operation: its summary, kebab-cased — the readable\n * URL. A summary that kebab-cases to nothing falls back to the\n * `operationId`, then to `METHOD path`, which always yields a slug.\n */\nfunction pageSlug(\n operation: { operationId?: string; summary?: string },\n method: string,\n apiPath: string,\n): string {\n for (const candidate of [operation.summary, operation.operationId]) {\n if (candidate !== undefined) {\n const slug = kebabCase(candidate);\n if (slug !== '') {\n return slug;\n }\n }\n }\n return kebabCase(`${method} ${apiPath}`);\n}\n\n/**\n * The frontmatter `title` of an MDX page, or null when the file has none.\n * Reads only the leading frontmatter block; quotes around the value are\n * stripped.\n */\nfunction frontmatterTitle(source: string): string | null {\n const block = /^---\\r?\\n([\\s\\S]*?)\\r?\\n---/.exec(source);\n if (!block?.[1]) {\n return null;\n }\n const line = /^title\\s*:\\s*(.+)\\s*$/m.exec(block[1]);\n if (!line?.[1]) {\n return null;\n }\n return line[1].replace(/^(['\"])(.*)\\1$/, '$2');\n}\n\n/** The `METHOD path` key of the first operation a generated page renders. */\nfunction operationKeyOf(content: string): string | null {\n const match = /operations=\\{(\\[[\\s\\S]*?\\])\\}/.exec(content);\n if (!match?.[1]) {\n return null;\n }\n try {\n const ops = JSON.parse(match[1]) as { path: string; method: string }[];\n const op = ops[0];\n return op ? `${op.method.toUpperCase()} ${op.path}` : null;\n } catch {\n return null;\n }\n}\n\n/**\n * The `info` a docs-only run reports on its (empty) merged document. Nothing\n * renders it: with no API pages there is no reference view to title.\n */\nconst PROSE_ONLY_INFO: OpenApiInfo = { title: 'No API reference', version: '0.0.0' };\n\n/** The optional top-level `sidebar` key of `docs.json`, validated. */\nfunction sidebarStyleOf(docsJson: Record<string, unknown>): SidebarStyle {\n const value = docsJson['sidebar'] ?? 'collapsible';\n if (value !== 'collapsible' && value !== 'headings') {\n throw new Error(\n `[docs-core] docs.json has sidebar: ${JSON.stringify(value)}. Use 'headings' ` +\n `(flat group headings, nested groups closed) or 'collapsible' (every group a folder).`,\n );\n }\n return value;\n}\n\n/**\n * The fumadocs functions, checked. Called only when the navigation publishes\n * API pages, so the message can name the exact reason they are needed.\n */\nfunction requireFumadocs(value: unknown, apiPageCount: number): FumadocsOpenApi {\n const because = `docs.json publishes ${String(apiPageCount)} API page(s)`;\n if (typeof value !== 'object' || value === null) {\n throw new Error(\n `[docs-core] generateDocsTree: the 'fumadocs' option is required because ${because}. ` +\n \"Pass { createOpenAPI, generateFiles } from the site's fumadocs-openapi install.\",\n );\n }\n const candidate = value as Record<string, unknown>;\n for (const name of ['createOpenAPI', 'generateFiles'] as const) {\n if (typeof candidate[name] !== 'function') {\n throw new Error(\n `[docs-core] generateDocsTree: fumadocs.${name} must be a function because ${because}. ` +\n `Import ${name} from ${name === 'createOpenAPI' ? \"'fumadocs-openapi/server'\" : \"'fumadocs-openapi'\"}.`,\n );\n }\n }\n return value as FumadocsOpenApi;\n}\n\n/**\n * Build the docs content tree from `docs.json`.\n *\n * Reads the navigation, merges every referenced spec, generates one page per\n * declared endpoint into its configured group folder, and writes the\n * `meta.json` files that give the sidebar the navigation's exact structure and\n * order. Operations present in a spec but not declared in `docs.json` get no\n * page — the navigation decides what is published, as on Mintlify.\n *\n * A site of prose alone needs neither option: with no API pages in `docs.json`\n * the run writes the `meta.json` files, `nav.json`, and an empty\n * `api-methods.json`, and never loads `fumadocs-openapi`.\n *\n * @example\n * ```ts\n * // A site of prose alone.\n * await generateDocsTree({ contentDir: 'content' });\n * ```\n *\n * @param options - directories, document info, and the injected fumadocs functions\n * @returns counts and the merged document\n * @throws when `docs.json` publishes API pages and `fumadocs` or `info` is missing\n * @public\n */\nexport async function generateDocsTree(\n options: GenerateDocsTreeOptions,\n): Promise<GenerateDocsTreeResult> {\n const docsJson = options.docsJson ?? path.join(options.contentDir, 'docs.json');\n const outDir = options.outDir ?? path.join(options.contentDir, 'docs');\n const apiDir = options.apiDir ?? 'api';\n\n const parsed = JSON.parse(await readFile(docsJson, 'utf8')) as Record<string, unknown>;\n const navigation = parseNavigation(parsed);\n const sidebar = sidebarStyleOf(parsed);\n const headings = sidebar === 'headings';\n const plan = planDocsTree(navigation, { apiDir, sidebar });\n\n // Where each planned API page lands. Both stay empty on a prose-only site.\n const placements = new Map<string, { dir: string; order: number; slug: string }>();\n const groupPages = new Map<string, { order: number; slug: string }[]>();\n let pages = 0;\n let document: OpenApiDocument;\n\n if (plan.apiPages.length === 0) {\n // A site of prose alone. Nothing to merge, nothing to generate, and — most\n // importantly — nothing removed: a hand-written folder named `api` is the\n // site's own content, not our output.\n document = mergeOpenApiDocuments([], { info: options.info ?? PROSE_ONLY_INFO });\n } else {\n const fumadocs = requireFumadocs(options.fumadocs, plan.apiPages.length);\n if (options.info === undefined) {\n throw new Error(\n `[docs-core] generateDocsTree: the 'info' option is required because docs.json ` +\n `publishes ${String(plan.apiPages.length)} API page(s). ` +\n 'Pass { title, version } for the merged OpenAPI document.',\n );\n }\n\n // Merge exactly the specs the navigation references.\n const docs = await Promise.all(\n plan.specs.map(\n async (file) =>\n JSON.parse(await readFile(path.join(options.contentDir, file), 'utf8')) as unknown,\n ),\n );\n document = mergeOpenApiDocuments(docs, {\n info: options.info,\n ...(options.servers ? { servers: options.servers } : {}),\n });\n\n // Resolve each planned page's slug from the merged document. Two pages in\n // one folder must never share a slug: that would be a silent overwrite.\n const taken = new Map<string, string>();\n for (const page of plan.apiPages) {\n const operation = document.paths[page.apiPath]?.[page.method.toLowerCase()] as\n { operationId?: string; summary?: string } | undefined;\n if (!operation) {\n throw new Error(\n `[docs-core] docs.json references ${page.method} ${page.apiPath}, ` +\n `but ${page.file} defines no such operation.`,\n );\n }\n const slug = pageSlug(operation, page.method, page.apiPath);\n const key = `${page.dir}/${slug}`;\n const holder = taken.get(key);\n if (holder !== undefined) {\n throw new Error(\n `[docs-core] ${holder} and ${page.method.toUpperCase()} ${page.apiPath} both ` +\n `resolve to the page '${key}'. Give one of them a distinct summary.`,\n );\n }\n taken.set(key, `${page.method.toUpperCase()} ${page.apiPath}`);\n placements.set(`${page.method.toUpperCase()} ${page.apiPath}`, { ...page, slug });\n const group = groupPages.get(page.dir) ?? [];\n group.push({ order: page.order, slug });\n groupPages.set(page.dir, group);\n }\n\n // Fresh API tree, then generate pages and route each into its planned folder.\n for (const group of plan.apiGroups) {\n if (group.root) {\n await rm(path.join(outDir, group.dir), { recursive: true, force: true });\n }\n }\n const createOpenAPI = fumadocs.createOpenAPI as (input: unknown) => unknown;\n const generateFiles = fumadocs.generateFiles as (input: unknown) => Promise<void>;\n await generateFiles({\n input: createOpenAPI({\n input: { blaaiz: document },\n ...(options.proxyUrl ? { proxyUrl: options.proxyUrl } : {}),\n }),\n output: outDir,\n per: 'operation',\n groupBy: 'none',\n beforeWrite(files: GeneratedFile[]) {\n for (let i = files.length - 1; i >= 0; i -= 1) {\n const file = files[i];\n if (!file) {\n continue;\n }\n const key = operationKeyOf(file.content);\n const placement = key ? placements.get(key) : undefined;\n if (!placement) {\n files.splice(i, 1); // not declared in docs.json -> not published\n continue;\n }\n file.path = path.join(placement.dir, `${placement.slug}.mdx`);\n pages += 1;\n }\n },\n });\n }\n\n // Doc pages declared inside API tabs become link entries in their group's\n // meta, at their declared position; the page keeps living in its own folder.\n const baseUrl = (options.baseUrl ?? '/docs').replace(/\\/$/, '');\n const docUrl = (file: string): string => {\n const clean = file.replace(/\\/?index$/, '');\n return clean ? `${baseUrl}/${clean}` : baseUrl;\n };\n for (const link of plan.apiDocLinks) {\n const source = path.join(outDir, `${link.file}.mdx`);\n let title: string | null = null;\n try {\n title = frontmatterTitle(await readFile(source, 'utf8'));\n } catch {\n console.warn(\n `[docs-core] docs.json lists ${link.file} inside an API tab, but ` +\n `${source} does not exist. The sidebar entry is skipped.`,\n );\n continue;\n }\n if (title === null) {\n title = link.file.split('/').pop() ?? link.file;\n console.warn(\n `[docs-core] ${link.file}.mdx has no frontmatter title; its sidebar ` +\n `entry falls back to '${title}'.`,\n );\n }\n const group = groupPages.get(link.dir) ?? [];\n // The meta link syntax cannot carry these characters in a name or URL.\n group.push({\n order: link.order,\n slug: `[${title.replace(/[[\\]()]/g, '')}](${docUrl(link.file)})`,\n });\n groupPages.set(link.dir, group);\n }\n\n // Meta files: the plan's static ones, plus each API group's entries — its\n // resolved page slugs and child folders, merged back into declared order.\n const metas = [\n ...plan.metas,\n ...plan.apiGroups.map((group) => ({\n path: `${group.dir}/meta.json`,\n content: {\n title: group.title,\n ...(group.root ? { root: true } : {}),\n ...(group.icon ? { icon: group.icon } : {}),\n // A nested group starts closed in the headings style, whatever the\n // site's defaultOpenLevel.\n ...(headings && !group.root ? { defaultOpen: false } : {}),\n pages: [\n ...(groupPages.get(group.dir) ?? []).map((page) => ({\n order: page.order,\n names: [page.slug],\n })),\n ...group.children.map((child) => ({\n order: child.order,\n // In the headings style a tab-level group is a flat heading with\n // its pages extracted beneath it, not a collapsible folder.\n names:\n headings && group.root\n ? [`---${child.title}---`, `...${child.folder}`]\n : [child.folder],\n })),\n ]\n .sort((a, b) => a.order - b.order)\n .flatMap((entry) => entry.names),\n },\n })),\n ];\n for (const meta of metas) {\n const target = path.join(outDir, meta.path);\n await mkdir(path.dirname(target), { recursive: true });\n await writeFile(target, `${JSON.stringify(meta.content, null, 2)}\\n`);\n }\n\n // Top-nav links: each tab points at its first page. The URL of an API page\n // needs its resolved slug, which is why this runs here, not in the planner.\n const firstUrl = (items: readonly NavItem[]): string | undefined => {\n for (const item of items) {\n if (item.kind === 'group') {\n const url = firstUrl(item.items);\n if (url) {\n return url;\n }\n } else if (item.kind === 'doc') {\n return docUrl(item.file);\n } else {\n const placement = placements.get(`${item.method.toUpperCase()} ${item.apiPath}`);\n if (placement) {\n return `${baseUrl}/${placement.dir}/${placement.slug}`;\n }\n }\n }\n return undefined;\n };\n const tabDocFiles = (items: readonly NavItem[]): string[] =>\n items.flatMap((item) => {\n if (item.kind === 'group') {\n return tabDocFiles(item.items);\n }\n return item.kind === 'doc' ? [item.file] : [];\n });\n\n // An API tab's active-match territory: its generated folder plus the exact\n // URLs of the doc pages it declares — pages, never their whole folders. The\n // tabs' generated folders sit in plan.apiGroups' root entries, in\n // navigation order.\n const apiTabRoots = plan.apiGroups.filter((group) => group.root).map((group) => group.dir);\n let apiTabIndex = 0;\n const navLinks: NavLink[] = [];\n for (const tab of navigation) {\n let match: string[] | undefined;\n if (hasOpenApi(tab.items)) {\n const root = apiTabRoots[apiTabIndex];\n apiTabIndex += 1;\n if (root !== undefined) {\n match = [\n ...new Set([`${baseUrl}/${root}`, ...tabDocFiles(tab.items).map((file) => docUrl(file))]),\n ];\n }\n }\n const url = firstUrl(tab.items);\n if (!url) {\n continue;\n }\n navLinks.push({\n text: tab.title,\n url,\n ...(tab.icon ? { icon: tab.icon } : {}),\n ...(match ? { match } : {}),\n });\n }\n await writeFile(\n path.join(options.contentDir, 'nav.json'),\n `${JSON.stringify(navLinks, null, 2)}\\n`,\n );\n\n // Page URL -> HTTP method, for the sidebar method badges; and page URL ->\n // the operation's source file, for the Markdown route.\n const methodsByUrl: Record<string, string> = {};\n const sourcesByUrl: Record<string, { file: string; method: string; route: string }> = {};\n for (const page of plan.apiPages) {\n const placement = placements.get(`${page.method.toUpperCase()} ${page.apiPath}`);\n if (placement) {\n const url = `${baseUrl}/${placement.dir}/${placement.slug}`;\n methodsByUrl[url] = page.method.toUpperCase();\n sourcesByUrl[url] = {\n file: page.file,\n method: page.method.toUpperCase(),\n route: page.apiPath,\n };\n }\n }\n // A renamed page leaves a forwarding address. The previous run's\n // api-sources.json keys each operation's old URL; where the same operation\n // (METHOD route) now lives at a new URL, redirects.json gains old -> new.\n // Existing entries are re-pointed through renames and dropped when their\n // old URL is a live page again.\n const sourcesPath = path.join(options.contentDir, 'api-sources.json');\n const redirectsPath = path.join(options.contentDir, 'redirects.json');\n const previousSources =\n await readJson<Record<string, { method: string; route: string }>>(sourcesPath);\n const redirects = (await readJson<Record<string, string>>(redirectsPath)) ?? {};\n if (previousSources) {\n const urlByOperation = new Map(\n Object.entries(sourcesByUrl).map(([url, s]) => [`${s.method} ${s.route}`, url]),\n );\n for (const [oldUrl, source] of Object.entries(previousSources)) {\n const newUrl = urlByOperation.get(`${source.method} ${source.route}`);\n if (newUrl !== undefined && newUrl !== oldUrl) {\n redirects[oldUrl] = newUrl;\n }\n }\n }\n // Each entry points at a live page: chains are chased to their end, cycles\n // and entries whose old URL is a live page again are dropped.\n const finalDestination = (from: string): string | null => {\n const seen = new Set([from]);\n let current = redirects[from];\n while (current !== undefined && !(current in sourcesByUrl)) {\n if (seen.has(current)) {\n return null;\n }\n seen.add(current);\n current = redirects[current];\n }\n return current ?? null;\n };\n const liveRedirects: Record<string, string> = {};\n for (const oldUrl of Object.keys(redirects)) {\n if (oldUrl in sourcesByUrl) {\n continue;\n }\n const destination = finalDestination(oldUrl);\n if (destination !== null && destination !== oldUrl) {\n liveRedirects[oldUrl] = destination;\n }\n }\n\n await writeFile(\n path.join(options.contentDir, 'api-methods.json'),\n `${JSON.stringify(methodsByUrl, null, 2)}\\n`,\n );\n await writeFile(sourcesPath, `${JSON.stringify(sourcesByUrl, null, 2)}\\n`);\n await writeFile(redirectsPath, `${JSON.stringify(liveRedirects, null, 2)}\\n`);\n\n return {\n pages,\n metas: metas.length,\n navLinks,\n methodsByUrl,\n sourcesByUrl,\n redirects: liveRedirects,\n document,\n };\n}\n\n/** A JSON file's parsed contents, or null when it does not exist or parse. */\nasync function readJson<T>(file: string): Promise<T | null> {\n try {\n return JSON.parse(await readFile(file, 'utf8')) as T;\n } catch {\n return null;\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/generator/index.ts"],"names":[],"mappings":"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","file":"generator.js","sourcesContent":["/**\n * `@blaaiz/docs-core/generator` — builds the docs content tree from `docs.json`.\n *\n * One call turns the site's navigation file into everything fumadocs needs:\n * generated API pages placed in their configured groups, and `meta.json` files\n * encoding tab and group order. Authors reorganize the docs by editing\n * `docs.json` — never by moving files.\n *\n * Node-only. A site that publishes an API reference injects `createOpenAPI` and\n * `generateFiles` from its own `fumadocs-openapi` install, so this package takes\n * no dependency on it. A site of prose alone injects nothing: with no API pages\n * in `docs.json` the generator writes the `meta.json` files and `nav.json` and\n * never touches `fumadocs-openapi`.\n *\n * @packageDocumentation\n */\n\nimport { mkdir, readFile, rm, writeFile } from 'node:fs/promises';\nimport path from 'node:path';\n\nimport { mergeOpenApiDocuments } from '../adapters/index.js';\nimport { parseNavigation } from '../adapters/index.js';\nimport type {\n NavItem,\n OpenApiDocument,\n OpenApiInfo,\n OpenApiServer,\n SidebarStyle,\n} from '../core/index.js';\nimport { planDocsTree } from '../core/index.js';\nimport { hasOpenApi } from '../core/docs-tree.js';\n\n/**\n * The two fumadocs-openapi functions the generator needs, injected by the site\n * (`createOpenAPI` from `fumadocs-openapi/server`, `generateFiles` from\n * `fumadocs-openapi`). Typed loosely so this package needs no fumadocs types.\n *\n * @public\n */\nexport interface FumadocsOpenApi {\n readonly createOpenAPI: (...args: never[]) => unknown;\n readonly generateFiles: (...args: never[]) => unknown;\n}\n\n/**\n * Options for {@link generateDocsTree}.\n *\n * @public\n */\nexport interface GenerateDocsTreeOptions {\n /** The site's content directory (holds `docs.json`, spec files, and `docs/`). */\n readonly contentDir: string;\n /** Path to the navigation file. Default `<contentDir>/docs.json`. */\n readonly docsJson?: string;\n /** Output directory for the page tree. Default `<contentDir>/docs`. */\n readonly outDir?: string;\n /** Folder for generated API pages. Default `api`. */\n readonly apiDir?: string;\n /**\n * `info` block of the merged OpenAPI document. Required when `docs.json`\n * publishes API pages; ignored on a site of prose alone, which merges no\n * documents.\n */\n readonly info?: OpenApiInfo;\n /** Servers of the merged document. */\n readonly servers?: readonly OpenApiServer[];\n /** Same-origin proxy route for the try-it playground. */\n readonly proxyUrl?: string;\n /** URL base the fumadocs loader serves pages under. Default `/docs`. */\n readonly baseUrl?: string;\n /**\n * The site's fumadocs-openapi functions. Required when `docs.json` publishes\n * API pages; omit it on a site of prose alone.\n */\n readonly fumadocs?: FumadocsOpenApi;\n}\n\n/**\n * A top-navigation link: one tab, pointing at its first page.\n *\n * @public\n */\nexport interface NavLink {\n readonly text: string;\n readonly url: string;\n /** The tab's icon name from `docs.json`, for the site to resolve. */\n readonly icon?: string;\n /**\n * Path prefixes the link is active for. Emitted for an API tab, whose\n * territory the generator knows exactly: the tab's generated folder plus\n * the folders of any doc pages it declares. `docsNavLinks` derives the\n * section from the URL when this is absent.\n */\n readonly match?: readonly string[];\n}\n\n/**\n * What the generator produced.\n *\n * @public\n */\nexport interface GenerateDocsTreeResult {\n readonly pages: number;\n readonly metas: number;\n /**\n * Top-nav links, one per tab, each pointing at the tab's first page — written\n * to `<contentDir>/nav.json` for the site's layout. Keeps the top nav in sync\n * with `docs.json` instead of a hand-maintained list.\n */\n readonly navLinks: readonly NavLink[];\n /**\n * Page URL to HTTP method, written to `<contentDir>/api-methods.json` for the\n * sidebar method badges (`apiMethodBadges` in `@blaaiz/docs-core/ui`).\n */\n readonly methodsByUrl: Readonly<Record<string, string>>;\n /**\n * Page URL to its operation's source, written to\n * `<contentDir>/api-sources.json` — the Markdown route serves the original\n * spec from it.\n */\n readonly sourcesByUrl: Readonly<\n Record<string, { readonly file: string; readonly method: string; readonly route: string }>\n >;\n /**\n * Old page URL to its current one, written to `<contentDir>/redirects.json`.\n * A renamed operation (its summary edited, its page moved) leaves a\n * forwarding address here; the site's middleware serves permanent redirects\n * from it through `redirectTarget`.\n */\n readonly redirects: Readonly<Record<string, string>>;\n /** The merged OpenAPI document (reusable for the runtime page renderer). */\n readonly document: OpenApiDocument;\n}\n\ninterface GeneratedFile {\n path: string;\n content: string;\n}\n\n/**\n * Kebab-case free text, splitting camelCase words. Distinct from the\n * planner's `slugify`, which names folders and must not re-split names that\n * already exist on disk.\n */\nfunction kebabCase(value: string): string {\n return value\n .replace(/([a-z0-9])([A-Z])/g, '$1 $2')\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '');\n}\n\n/**\n * The page slug for one operation: its summary, kebab-cased — the readable\n * URL. A summary that kebab-cases to nothing falls back to the\n * `operationId`, then to `METHOD path`, which always yields a slug.\n */\nfunction pageSlug(\n operation: { operationId?: string; summary?: string },\n method: string,\n apiPath: string,\n): string {\n for (const candidate of [operation.summary, operation.operationId]) {\n if (candidate !== undefined) {\n const slug = kebabCase(candidate);\n if (slug !== '') {\n return slug;\n }\n }\n }\n return kebabCase(`${method} ${apiPath}`);\n}\n\n/**\n * The frontmatter `title` of an MDX page, or null when the file has none.\n * Reads only the leading frontmatter block; quotes around the value are\n * stripped.\n */\nfunction frontmatterTitle(source: string): string | null {\n const block = /^---\\r?\\n([\\s\\S]*?)\\r?\\n---/.exec(source);\n if (!block?.[1]) {\n return null;\n }\n const line = /^title\\s*:\\s*(.+)\\s*$/m.exec(block[1]);\n if (!line?.[1]) {\n return null;\n }\n return line[1].replace(/^(['\"])(.*)\\1$/, '$2');\n}\n\n/** The `METHOD path` key of the first operation a generated page renders. */\nfunction operationKeyOf(content: string): string | null {\n const match = /operations=\\{(\\[[\\s\\S]*?\\])\\}/.exec(content);\n if (!match?.[1]) {\n return null;\n }\n try {\n const ops = JSON.parse(match[1]) as { path: string; method: string }[];\n const op = ops[0];\n return op ? `${op.method.toUpperCase()} ${op.path}` : null;\n } catch {\n return null;\n }\n}\n\n/**\n * The `info` a docs-only run reports on its (empty) merged document. Nothing\n * renders it: with no API pages there is no reference view to title.\n */\nconst PROSE_ONLY_INFO: OpenApiInfo = { title: 'No API reference', version: '0.0.0' };\n\n/** The optional top-level `sidebar` key of `docs.json`, validated. */\nfunction sidebarStyleOf(docsJson: Record<string, unknown>): SidebarStyle {\n const value = docsJson['sidebar'] ?? 'collapsible';\n if (value !== 'collapsible' && value !== 'headings') {\n throw new Error(\n `[docs-core] docs.json has sidebar: ${JSON.stringify(value)}. Use 'headings' ` +\n `(flat group headings, nested groups closed) or 'collapsible' (every group a folder).`,\n );\n }\n return value;\n}\n\n/**\n * The fumadocs functions, checked. Called only when the navigation publishes\n * API pages, so the message can name the exact reason they are needed.\n */\nfunction requireFumadocs(value: unknown, apiPageCount: number): FumadocsOpenApi {\n const because = `docs.json publishes ${String(apiPageCount)} API page(s)`;\n if (typeof value !== 'object' || value === null) {\n throw new Error(\n `[docs-core] generateDocsTree: the 'fumadocs' option is required because ${because}. ` +\n \"Pass { createOpenAPI, generateFiles } from the site's fumadocs-openapi install.\",\n );\n }\n const candidate = value as Record<string, unknown>;\n for (const name of ['createOpenAPI', 'generateFiles'] as const) {\n if (typeof candidate[name] !== 'function') {\n throw new Error(\n `[docs-core] generateDocsTree: fumadocs.${name} must be a function because ${because}. ` +\n `Import ${name} from ${name === 'createOpenAPI' ? \"'fumadocs-openapi/server'\" : \"'fumadocs-openapi'\"}.`,\n );\n }\n }\n return value as FumadocsOpenApi;\n}\n\n/**\n * Build the docs content tree from `docs.json`.\n *\n * Reads the navigation, merges every referenced spec, generates one page per\n * declared endpoint into its configured group folder, and writes the\n * `meta.json` files that give the sidebar the navigation's exact structure and\n * order. Operations present in a spec but not declared in `docs.json` get no\n * page — the navigation decides what is published, as on Mintlify.\n *\n * A site of prose alone needs neither option: with no API pages in `docs.json`\n * the run writes the `meta.json` files, `nav.json`, and an empty\n * `api-methods.json`, and never loads `fumadocs-openapi`.\n *\n * @example\n * ```ts\n * // A site of prose alone.\n * await generateDocsTree({ contentDir: 'content' });\n * ```\n *\n * @param options - directories, document info, and the injected fumadocs functions\n * @returns counts and the merged document\n * @throws when `docs.json` publishes API pages and `fumadocs` or `info` is missing\n * @public\n */\nexport async function generateDocsTree(\n options: GenerateDocsTreeOptions,\n): Promise<GenerateDocsTreeResult> {\n const docsJson = options.docsJson ?? path.join(options.contentDir, 'docs.json');\n const outDir = options.outDir ?? path.join(options.contentDir, 'docs');\n const apiDir = options.apiDir ?? 'api';\n\n const parsed = JSON.parse(await readFile(docsJson, 'utf8')) as Record<string, unknown>;\n const navigation = parseNavigation(parsed);\n const sidebar = sidebarStyleOf(parsed);\n const headings = sidebar === 'headings';\n const plan = planDocsTree(navigation, { apiDir, sidebar });\n\n // Where each planned API page lands. Both stay empty on a prose-only site.\n const placements = new Map<string, { dir: string; order: number; slug: string }>();\n const groupPages = new Map<string, { order: number; slug: string }[]>();\n let pages = 0;\n let document: OpenApiDocument;\n\n if (plan.apiPages.length === 0) {\n // A site of prose alone. Nothing to merge, nothing to generate, and — most\n // importantly — nothing removed: a hand-written folder named `api` is the\n // site's own content, not our output.\n document = mergeOpenApiDocuments([], { info: options.info ?? PROSE_ONLY_INFO });\n } else {\n const fumadocs = requireFumadocs(options.fumadocs, plan.apiPages.length);\n if (options.info === undefined) {\n throw new Error(\n `[docs-core] generateDocsTree: the 'info' option is required because docs.json ` +\n `publishes ${String(plan.apiPages.length)} API page(s). ` +\n 'Pass { title, version } for the merged OpenAPI document.',\n );\n }\n\n // Merge exactly the specs the navigation references.\n const docs = await Promise.all(\n plan.specs.map(\n async (file) =>\n JSON.parse(await readFile(path.join(options.contentDir, file), 'utf8')) as unknown,\n ),\n );\n document = mergeOpenApiDocuments(docs, {\n info: options.info,\n ...(options.servers ? { servers: options.servers } : {}),\n });\n\n // Resolve each planned page's slug from the merged document. Two pages in\n // one folder must never share a slug: that would be a silent overwrite.\n const taken = new Map<string, string>();\n for (const page of plan.apiPages) {\n const operation = document.paths[page.apiPath]?.[page.method.toLowerCase()] as\n { operationId?: string; summary?: string } | undefined;\n if (!operation) {\n throw new Error(\n `[docs-core] docs.json references ${page.method} ${page.apiPath}, ` +\n `but ${page.file} defines no such operation.`,\n );\n }\n const slug = pageSlug(operation, page.method, page.apiPath);\n const key = `${page.dir}/${slug}`;\n const holder = taken.get(key);\n if (holder !== undefined) {\n throw new Error(\n `[docs-core] ${holder} and ${page.method.toUpperCase()} ${page.apiPath} both ` +\n `resolve to the page '${key}'. Give one of them a distinct summary.`,\n );\n }\n taken.set(key, `${page.method.toUpperCase()} ${page.apiPath}`);\n placements.set(`${page.method.toUpperCase()} ${page.apiPath}`, { ...page, slug });\n const group = groupPages.get(page.dir) ?? [];\n group.push({ order: page.order, slug });\n groupPages.set(page.dir, group);\n }\n\n // Fresh API tree, then generate pages and route each into its planned\n // folder. A declared doc page inside a generated folder would be wiped\n // with it: refuse before deleting anything.\n for (const link of plan.apiDocLinks) {\n const owner = plan.apiGroups.find(\n (group) => group.root && (link.file === group.dir || link.file.startsWith(`${group.dir}/`)),\n );\n if (owner) {\n throw new Error(\n `[docs-core] docs.json places the doc page '${link.file}' inside '${owner.dir}', ` +\n `which the generator regenerates and wipes on every run. Move the page ` +\n `outside '${owner.dir}/'.`,\n );\n }\n }\n for (const group of plan.apiGroups) {\n if (group.root) {\n await rm(path.join(outDir, group.dir), { recursive: true, force: true });\n }\n }\n const createOpenAPI = fumadocs.createOpenAPI as (input: unknown) => unknown;\n const generateFiles = fumadocs.generateFiles as (input: unknown) => Promise<void>;\n await generateFiles({\n input: createOpenAPI({\n input: { blaaiz: document },\n ...(options.proxyUrl ? { proxyUrl: options.proxyUrl } : {}),\n }),\n output: outDir,\n per: 'operation',\n groupBy: 'none',\n beforeWrite(files: GeneratedFile[]) {\n for (let i = files.length - 1; i >= 0; i -= 1) {\n const file = files[i];\n if (!file) {\n continue;\n }\n const key = operationKeyOf(file.content);\n const placement = key ? placements.get(key) : undefined;\n if (!placement) {\n files.splice(i, 1); // not declared in docs.json -> not published\n continue;\n }\n file.path = path.join(placement.dir, `${placement.slug}.mdx`);\n pages += 1;\n }\n },\n });\n }\n\n // Doc pages declared inside API tabs become link entries in their group's\n // meta, at their declared position; the page keeps living in its own folder.\n const baseUrl = (options.baseUrl ?? '/docs').replace(/\\/$/, '');\n const docUrl = (file: string): string => {\n const clean = file.replace(/\\/?index$/, '');\n return clean ? `${baseUrl}/${clean}` : baseUrl;\n };\n for (const link of plan.apiDocLinks) {\n const source = path.join(outDir, `${link.file}.mdx`);\n let title: string | null = null;\n try {\n title = frontmatterTitle(await readFile(source, 'utf8'));\n } catch {\n console.warn(\n `[docs-core] docs.json lists ${link.file} inside an API tab, but ` +\n `${source} does not exist. The sidebar entry is skipped.`,\n );\n continue;\n }\n if (title === null) {\n title = link.file.split('/').pop() ?? link.file;\n console.warn(\n `[docs-core] ${link.file}.mdx has no frontmatter title; its sidebar ` +\n `entry falls back to '${title}'.`,\n );\n }\n const group = groupPages.get(link.dir) ?? [];\n // The meta link syntax cannot carry these characters in a name or URL.\n group.push({\n order: link.order,\n slug: `[${title.replace(/[[\\]()]/g, '')}](${docUrl(link.file)})`,\n });\n groupPages.set(link.dir, group);\n }\n\n // Meta files: the plan's static ones, plus each API group's entries — its\n // resolved page slugs and child folders, merged back into declared order.\n const metas = [\n ...plan.metas,\n ...plan.apiGroups.map((group) => ({\n path: `${group.dir}/meta.json`,\n content: {\n title: group.title,\n ...(group.root ? { root: true } : {}),\n ...(group.icon ? { icon: group.icon } : {}),\n // A nested group starts closed in the headings style, whatever the\n // site's defaultOpenLevel.\n ...(headings && !group.root ? { defaultOpen: false } : {}),\n pages: [\n ...(groupPages.get(group.dir) ?? []).map((page) => ({\n order: page.order,\n names: [page.slug],\n })),\n ...group.children.map((child) => ({\n order: child.order,\n // In the headings style a tab-level group is a flat heading with\n // its pages extracted beneath it, not a collapsible folder.\n names:\n headings && group.root\n ? [`---${child.title}---`, `...${child.folder}`]\n : [child.folder],\n })),\n ]\n .sort((a, b) => a.order - b.order)\n .flatMap((entry) => entry.names),\n },\n })),\n ];\n for (const meta of metas) {\n const target = path.join(outDir, meta.path);\n await mkdir(path.dirname(target), { recursive: true });\n await writeFile(target, `${JSON.stringify(meta.content, null, 2)}\\n`);\n }\n\n // Top-nav links: each tab points at its first page. The URL of an API page\n // needs its resolved slug, which is why this runs here, not in the planner.\n const firstUrl = (items: readonly NavItem[]): string | undefined => {\n for (const item of items) {\n if (item.kind === 'group') {\n const url = firstUrl(item.items);\n if (url) {\n return url;\n }\n } else if (item.kind === 'doc') {\n return docUrl(item.file);\n } else {\n const placement = placements.get(`${item.method.toUpperCase()} ${item.apiPath}`);\n if (placement) {\n return `${baseUrl}/${placement.dir}/${placement.slug}`;\n }\n }\n }\n return undefined;\n };\n const tabDocFiles = (items: readonly NavItem[]): string[] =>\n items.flatMap((item) => {\n if (item.kind === 'group') {\n return tabDocFiles(item.items);\n }\n return item.kind === 'doc' ? [item.file] : [];\n });\n\n // An API tab's active-match territory: its generated folder plus the exact\n // URLs of the doc pages it declares — pages, never their whole folders. The\n // tabs' generated folders sit in plan.apiGroups' root entries, in\n // navigation order.\n const apiTabRoots = plan.apiGroups.filter((group) => group.root).map((group) => group.dir);\n let apiTabIndex = 0;\n const navLinks: NavLink[] = [];\n for (const tab of navigation) {\n let match: string[] | undefined;\n if (hasOpenApi(tab.items)) {\n const root = apiTabRoots[apiTabIndex];\n apiTabIndex += 1;\n if (root !== undefined) {\n match = [\n ...new Set([`${baseUrl}/${root}`, ...tabDocFiles(tab.items).map((file) => docUrl(file))]),\n ];\n }\n }\n const url = firstUrl(tab.items);\n if (!url) {\n continue;\n }\n navLinks.push({\n text: tab.title,\n url,\n ...(tab.icon ? { icon: tab.icon } : {}),\n ...(match ? { match } : {}),\n });\n }\n await writeFile(\n path.join(options.contentDir, 'nav.json'),\n `${JSON.stringify(navLinks, null, 2)}\\n`,\n );\n\n // Page URL -> HTTP method, for the sidebar method badges; and page URL ->\n // the operation's source file, for the Markdown route.\n const methodsByUrl: Record<string, string> = {};\n const sourcesByUrl: Record<string, { file: string; method: string; route: string }> = {};\n for (const page of plan.apiPages) {\n const placement = placements.get(`${page.method.toUpperCase()} ${page.apiPath}`);\n if (placement) {\n const url = `${baseUrl}/${placement.dir}/${placement.slug}`;\n methodsByUrl[url] = page.method.toUpperCase();\n sourcesByUrl[url] = {\n file: page.file,\n method: page.method.toUpperCase(),\n route: page.apiPath,\n };\n }\n }\n // A renamed page leaves a forwarding address. The previous run's\n // api-sources.json keys each operation's old URL; where the same operation\n // (METHOD route) now lives at a new URL, redirects.json gains old -> new.\n // Existing entries are re-pointed through renames and dropped when their\n // old URL is a live page again.\n const sourcesPath = path.join(options.contentDir, 'api-sources.json');\n const redirectsPath = path.join(options.contentDir, 'redirects.json');\n const previousSources =\n await readJson<Record<string, { method: string; route: string }>>(sourcesPath);\n const redirects = (await readJson<Record<string, string>>(redirectsPath)) ?? {};\n if (previousSources) {\n const urlByOperation = new Map(\n Object.entries(sourcesByUrl).map(([url, s]) => [`${s.method} ${s.route}`, url]),\n );\n for (const [oldUrl, source] of Object.entries(previousSources)) {\n const newUrl = urlByOperation.get(`${source.method} ${source.route}`);\n if (newUrl !== undefined && newUrl !== oldUrl) {\n redirects[oldUrl] = newUrl;\n }\n }\n }\n // Each entry points at a live page: chains are chased to their end, cycles\n // and entries whose old URL is a live page again are dropped.\n const finalDestination = (from: string): string | null => {\n const seen = new Set([from]);\n let current = redirects[from];\n while (current !== undefined && !(current in sourcesByUrl)) {\n if (seen.has(current)) {\n return null;\n }\n seen.add(current);\n current = redirects[current];\n }\n return current ?? null;\n };\n const liveRedirects: Record<string, string> = {};\n for (const oldUrl of Object.keys(redirects)) {\n if (oldUrl in sourcesByUrl) {\n continue;\n }\n const destination = finalDestination(oldUrl);\n if (destination !== null && destination !== oldUrl) {\n liveRedirects[oldUrl] = destination;\n }\n }\n\n await writeFile(\n path.join(options.contentDir, 'api-methods.json'),\n `${JSON.stringify(methodsByUrl, null, 2)}\\n`,\n );\n await writeFile(sourcesPath, `${JSON.stringify(sourcesByUrl, null, 2)}\\n`);\n await writeFile(redirectsPath, `${JSON.stringify(liveRedirects, null, 2)}\\n`);\n\n return {\n pages,\n metas: metas.length,\n navLinks,\n methodsByUrl,\n sourcesByUrl,\n redirects: liveRedirects,\n document,\n };\n}\n\n/** A JSON file's parsed contents, or null when it does not exist or parse. */\nasync function readJson<T>(file: string): Promise<T | null> {\n try {\n return JSON.parse(await readFile(file, 'utf8')) as T;\n } catch {\n return null;\n }\n}\n"]}
package/dist/index.cjs CHANGED
@@ -1783,6 +1783,11 @@ function planDocsTree(navigation, options = {}) {
1783
1783
  entries.push(...planDocItems(item.items, dir, false));
1784
1784
  continue;
1785
1785
  }
1786
+ if (!groupDir.startsWith(`${dir}/`)) {
1787
+ throw new Error(
1788
+ `[docs-core] The doc group '${item.title}' keeps its pages in '${groupDir}', outside its tab's folder '${dir}'. Move the pages under '${dir}/', or give the group a tab of its own.`
1789
+ );
1790
+ }
1786
1791
  const folder = groupDir.slice(dir.length + 1);
1787
1792
  if (headings && tabLevel) {
1788
1793
  entries.push(`---${item.title}---`, `...${folder}`);