@plebeiantech/lat.md 0.12.3-fork.9 → 0.13.4-fork.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (340) hide show
  1. package/README.md +27 -3
  2. package/dist/src/cli/agent-invocation.d.ts +6 -0
  3. package/dist/src/cli/agent-invocation.js +23 -0
  4. package/dist/src/cli/checklist-menu.d.ts +2 -1
  5. package/dist/src/cli/checklist-menu.js +3 -2
  6. package/dist/src/cli/fork-instructions.d.ts +1 -0
  7. package/dist/src/cli/fork-instructions.js +8 -32
  8. package/dist/src/cli/fork-scaffold.js +2 -2
  9. package/dist/src/cli/graph.d.ts +1 -1
  10. package/dist/src/cli/hook.d.ts +26 -0
  11. package/dist/src/cli/hook.js +115 -64
  12. package/dist/src/cli/index.js +178 -70
  13. package/dist/src/cli/init-preferences.d.ts +4 -0
  14. package/dist/src/cli/init-preferences.js +36 -0
  15. package/dist/src/cli/init.d.ts +2 -1
  16. package/dist/src/cli/init.js +100 -60
  17. package/dist/src/cli/reindex.d.ts +1 -1
  18. package/dist/src/cli/reindex.js +7 -10
  19. package/dist/src/cli/search.d.ts +14 -4
  20. package/dist/src/cli/search.js +115 -101
  21. package/dist/src/cli/ui-build-server.d.ts +12 -0
  22. package/dist/src/cli/ui-build-server.js +26 -0
  23. package/dist/src/cli/ui-build.d.ts +1 -1
  24. package/dist/src/cli/ui-build.js +1 -1
  25. package/dist/src/cli/ui.d.ts +1 -1
  26. package/dist/src/cli/ui.js +1 -1
  27. package/dist/src/cli/wiki.d.ts +12 -0
  28. package/dist/src/cli/wiki.js +85 -0
  29. package/dist/src/fork/antigravity-hook.d.ts +47 -0
  30. package/dist/src/fork/antigravity-hook.js +424 -0
  31. package/dist/src/fork/antigravity-init.d.ts +17 -0
  32. package/dist/src/fork/antigravity-init.js +124 -0
  33. package/dist/src/fork/upstream-drift.js +1 -1
  34. package/dist/src/fork/upstream-guard.d.ts +1 -1
  35. package/dist/src/fork/upstream-guard.js +8 -7
  36. package/dist/src/graph-export.js +7 -7
  37. package/dist/src/knowledge/index.js +2 -2
  38. package/dist/src/mcp/server.js +34 -18
  39. package/dist/src/search/cache.d.ts +3 -0
  40. package/dist/src/search/cache.js +104 -0
  41. package/dist/src/search/chunks.d.ts +23 -0
  42. package/dist/src/search/chunks.js +200 -0
  43. package/dist/src/search/db.d.ts +28 -16
  44. package/dist/src/search/db.js +100 -40
  45. package/dist/src/search/embedder.d.ts +2 -1
  46. package/dist/src/search/embedder.js +7 -6
  47. package/dist/src/search/index.d.ts +6 -2
  48. package/dist/src/search/index.js +176 -70
  49. package/dist/src/search/lexical.d.ts +6 -0
  50. package/dist/src/search/lexical.js +36 -0
  51. package/dist/src/search/lock.d.ts +5 -0
  52. package/dist/src/search/lock.js +45 -0
  53. package/dist/src/search/query.d.ts +26 -0
  54. package/dist/src/search/query.js +103 -0
  55. package/dist/src/search/search.d.ts +16 -7
  56. package/dist/src/search/search.js +165 -21
  57. package/dist/src/search/types.d.ts +15 -0
  58. package/dist/src/view/client/assets/GraphView-D_xnk0bP.js +456 -0
  59. package/dist/src/view/client/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  60. package/dist/src/view/client/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  61. package/dist/src/view/client/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  62. package/dist/src/view/client/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  63. package/dist/src/view/client/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  64. package/dist/src/view/client/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  65. package/dist/src/view/client/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  66. package/dist/src/view/client/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  67. package/dist/src/view/client/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  68. package/dist/src/view/client/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  69. package/dist/src/view/client/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  70. package/dist/src/view/client/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  71. package/dist/src/view/client/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  72. package/dist/src/view/client/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  73. package/dist/src/view/client/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  74. package/dist/src/view/client/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  75. package/dist/src/view/client/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  76. package/dist/src/view/client/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  77. package/dist/src/view/client/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  78. package/dist/src/view/client/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  79. package/dist/src/view/client/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  80. package/dist/src/view/client/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  81. package/dist/src/view/client/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  82. package/dist/src/view/client/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  83. package/dist/src/view/client/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  84. package/dist/src/view/client/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  85. package/dist/src/view/client/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  86. package/dist/src/view/client/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  87. package/dist/src/view/client/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  88. package/dist/src/view/client/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  89. package/dist/src/view/client/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  90. package/dist/src/view/client/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  91. package/dist/src/view/client/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  92. package/dist/src/view/client/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  93. package/dist/src/view/client/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  94. package/dist/src/view/client/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  95. package/dist/src/view/client/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  96. package/dist/src/view/client/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  97. package/dist/src/view/client/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  98. package/dist/src/view/client/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  99. package/dist/src/view/client/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  100. package/dist/src/view/client/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  101. package/dist/src/view/client/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  102. package/dist/src/view/client/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  103. package/dist/src/view/client/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  104. package/dist/src/view/client/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  105. package/dist/src/view/client/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  106. package/dist/src/view/client/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  107. package/dist/src/view/client/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  108. package/dist/src/view/client/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  109. package/dist/src/view/client/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  110. package/dist/src/view/client/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  111. package/dist/src/view/client/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  112. package/dist/src/view/client/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  113. package/dist/src/view/client/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  114. package/dist/src/view/client/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  115. package/dist/src/view/client/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  116. package/dist/src/view/client/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  117. package/dist/src/view/client/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  118. package/dist/src/view/client/assets/MarkdownEditor-bjqt_FoF.js +28 -0
  119. package/dist/src/view/client/assets/OrbitControls-DwcpQNja.js +1 -0
  120. package/dist/src/view/client/assets/STLLoader-CLWTKt5i.js +1 -0
  121. package/dist/src/view/client/assets/_commonjsHelpers-CqkleIqs.js +1 -0
  122. package/dist/src/view/client/assets/abnfDiagram-VCTEODGH-kWGvtRnL.js +1 -0
  123. package/dist/src/view/client/assets/arc-BPAFd7cP.js +1 -0
  124. package/dist/src/view/client/assets/architectureDiagram-5GKGNRK7-CsY39oIR.js +36 -0
  125. package/dist/src/view/client/assets/blockDiagram-I7D4REHJ-DHGBG7BJ.js +129 -0
  126. package/dist/src/view/client/assets/c4Diagram-7LVT6UL2-vywK4eb7.js +38 -0
  127. package/dist/src/view/client/assets/channel-CX71L_qV.js +1 -0
  128. package/dist/src/view/client/assets/chunk-2Q5K7J3B-Do3Hz_ci.js +1 -0
  129. package/dist/src/view/client/assets/chunk-5VM5RSS4-B5R1lSeY.js +15 -0
  130. package/dist/src/view/client/assets/chunk-F27PBJKO-Dm55FM_W.js +1 -0
  131. package/dist/src/view/client/assets/chunk-IMKFNOWR-ChptrvvM.js +231 -0
  132. package/dist/src/view/client/assets/chunk-JWPE2WC7-BRQBjrKS.js +1 -0
  133. package/dist/src/view/client/assets/chunk-POPQ4Y6H-CainVZIL.js +1 -0
  134. package/dist/src/view/client/assets/chunk-SVP7TREG-DsKsNGmM.js +88 -0
  135. package/dist/src/view/client/assets/chunk-TICWLB2K-BctkMcJc.js +206 -0
  136. package/dist/src/view/client/assets/chunk-XXDRQBXY-BN7SgOUT.js +1 -0
  137. package/dist/src/view/client/assets/classDiagram-ZZMXUADV-B1VyU6zO.js +1 -0
  138. package/dist/src/view/client/assets/classDiagram-v2-VYDZK3BY-B1VyU6zO.js +1 -0
  139. package/dist/src/view/client/assets/cose-bilkent-JH36ORCC-BszzwrMe.js +1 -0
  140. package/dist/src/view/client/assets/cynefin-OW5HDTMX-CdRyAkPs.js +166 -0
  141. package/dist/src/view/client/assets/cynefinDiagram-5FMLGOSQ-BLvKPZ1u.js +62 -0
  142. package/dist/src/view/client/assets/cytoscape.esm-DzSz-X2X.js +321 -0
  143. package/dist/src/view/client/assets/dagre-GXQ25YYZ-V68w_l65.js +4 -0
  144. package/dist/src/view/client/assets/defaultLocale-DX6XiGOO.js +1 -0
  145. package/dist/src/view/client/assets/diagram-S7CK7UJ4-C2i4eEMg.js +30 -0
  146. package/dist/src/view/client/assets/diagram-UQ7AKVKN-BiwWh5jC.js +41 -0
  147. package/dist/src/view/client/assets/diagram-VSXAHHWV-hkKR_vyS.js +3 -0
  148. package/dist/src/view/client/assets/diagram-VX7I27RA-DzRCSMAc.js +24 -0
  149. package/dist/src/view/client/assets/diagram-Z3DM3KII-fhSFPxyK.js +24 -0
  150. package/dist/src/view/client/assets/ebnfDiagram-PWID7BFC-CCEUDKSA.js +1 -0
  151. package/dist/src/view/client/assets/erDiagram-RLTQ6QDP-FJ2IMyTi.js +99 -0
  152. package/dist/src/view/client/assets/flowDiagram-HODETNUW-BvasdLeK.js +168 -0
  153. package/dist/src/view/client/assets/ganttDiagram-EL5Y4UJY-CiwkBZvY.js +292 -0
  154. package/dist/src/view/client/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
  155. package/dist/src/view/client/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
  156. package/dist/src/view/client/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
  157. package/dist/src/view/client/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
  158. package/dist/src/view/client/assets/geist-mono-cyrillic-ext-wght-normal-X_5orZeX.woff2 +0 -0
  159. package/dist/src/view/client/assets/geist-mono-cyrillic-wght-normal-DiZS0aHC.woff2 +0 -0
  160. package/dist/src/view/client/assets/geist-mono-latin-ext-wght-normal-Bwz-egvJ.woff2 +0 -0
  161. package/dist/src/view/client/assets/geist-mono-latin-wght-normal-XN7g48iV.woff2 +0 -0
  162. package/dist/src/view/client/assets/geist-mono-symbols2-wght-normal-CO5SzqOn.woff2 +0 -0
  163. package/dist/src/view/client/assets/geist-mono-vietnamese-wght-normal-DadHysG0.woff2 +0 -0
  164. package/dist/src/view/client/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
  165. package/dist/src/view/client/assets/gitGraphDiagram-WWUBYQGX-DYlP2_js.js +106 -0
  166. package/dist/src/view/client/assets/index-BnENo9bV.js +1 -0
  167. package/dist/src/view/client/assets/index-CQ7EOox7.css +1 -0
  168. package/dist/src/view/client/assets/index-CynNDkxt.js +13 -0
  169. package/dist/src/view/client/assets/infoDiagram-27XIBGKW-DRPB2c48.js +2 -0
  170. package/dist/src/view/client/assets/init-Gi6I4Gst.js +1 -0
  171. package/dist/src/view/client/assets/ishikawaDiagram-5VMMS53U-8VV429Cv.js +70 -0
  172. package/dist/src/view/client/assets/journeyDiagram-3NMN7TZE-Dzmwnf83.js +139 -0
  173. package/dist/src/view/client/assets/kanban-definition-UXKFOSKX-pYyZZ1i-.js +89 -0
  174. package/dist/src/view/client/assets/katex-HP8lGamR.js +257 -0
  175. package/dist/src/view/client/assets/layout-GF9k6yGo.js +1 -0
  176. package/dist/src/view/client/assets/linear-Yt2sSOYV.js +1 -0
  177. package/dist/src/view/client/assets/logo-RR_KBOAT.svg +149 -0
  178. package/dist/src/view/client/assets/maplibre-gl-CDX6Zuow.js +800 -0
  179. package/dist/src/view/client/assets/mermaid.core-ChWYCHN0.js +310 -0
  180. package/dist/src/view/client/assets/mindmap-definition-YA3MSWOX-Cc4fQvf1.js +96 -0
  181. package/dist/src/view/client/assets/ordinal-Cboi1Yqb.js +1 -0
  182. package/dist/src/view/client/assets/pegDiagram-XKGWAZYB-C0SsB8bZ.js +1 -0
  183. package/dist/src/view/client/assets/pieDiagram-E7YTZNPT-DIJpqERZ.js +39 -0
  184. package/dist/src/view/client/assets/quadrantDiagram-AXDQQJYC--d-gl6j-.js +7 -0
  185. package/dist/src/view/client/assets/railroadDiagram-O6MQD6OU-BQxwTt7z.js +1 -0
  186. package/dist/src/view/client/assets/requirementDiagram-BXWQKSXE-B_oTeD2A.js +84 -0
  187. package/dist/src/view/client/assets/sankeyDiagram-P5KCCOFB-DLukmE8X.js +40 -0
  188. package/dist/src/view/client/assets/sequenceDiagram-WJ2MYXX4-BNjVB2sc.js +162 -0
  189. package/dist/src/view/client/assets/sizeCapture-INFHLROL-BZecQTOw.js +1 -0
  190. package/dist/src/view/client/assets/stateDiagram-D77RDMKH-Bu1urvXC.js +1 -0
  191. package/dist/src/view/client/assets/stateDiagram-v2-MP3YSRHH-5pc_697G.js +1 -0
  192. package/dist/src/view/client/assets/swimlanes-42K2YHIH-bLAlcepB.js +1 -0
  193. package/dist/src/view/client/assets/swimlanesDiagram-VR7AAH4N-BbOpZjpp.js +8 -0
  194. package/dist/src/view/client/assets/three.module-PDSP0dbZ.js +4108 -0
  195. package/dist/src/view/client/assets/timeline-definition-24CTP7MA-tbwCNvcA.js +120 -0
  196. package/dist/src/view/client/assets/vennDiagram-4TSXK5OY-D8U8Fk0N.js +34 -0
  197. package/dist/src/view/client/assets/wardleyDiagram-VM6X3IG4-LHBVIeNb.js +78 -0
  198. package/dist/src/view/client/assets/xychartDiagram-S5SC5T6Z-CN4tfaFV.js +7 -0
  199. package/dist/src/view/client/index.html +2 -2
  200. package/dist/src/view/client-shell.d.ts +2 -0
  201. package/dist/src/view/client-shell.js +5 -0
  202. package/dist/src/view/diagnostics.d.ts +3 -2
  203. package/dist/src/view/diagnostics.js +49 -58
  204. package/dist/src/view/document-edit.d.ts +8 -0
  205. package/dist/src/view/document-edit.js +25 -0
  206. package/dist/src/view/document-route.d.ts +14 -0
  207. package/dist/src/view/document-route.js +137 -0
  208. package/dist/src/view/document-tree.d.ts +18 -0
  209. package/dist/src/view/document-tree.js +139 -0
  210. package/dist/src/view/external-document-tree.d.ts +3 -0
  211. package/dist/src/view/external-document-tree.js +674 -0
  212. package/dist/src/view/git-diff.js +202 -2
  213. package/dist/src/view/git.js +1 -1
  214. package/dist/src/view/graph.d.ts +4 -3
  215. package/dist/src/view/graph.js +100 -9
  216. package/dist/src/view/highlight.d.ts +10 -2
  217. package/dist/src/view/highlight.js +50648 -3166
  218. package/dist/src/view/markdown.d.ts +3 -2
  219. package/dist/src/view/markdown.js +256 -54
  220. package/dist/src/view/preindexed-search.d.ts +15 -0
  221. package/dist/src/view/preindexed-search.js +25 -0
  222. package/dist/src/view/protocol.d.ts +53 -7
  223. package/dist/src/view/publication.d.ts +2 -0
  224. package/dist/src/view/publication.js +78 -0
  225. package/dist/src/view/references.d.ts +11 -26
  226. package/dist/src/view/references.js +121 -112
  227. package/dist/src/view/repository.d.ts +14 -4
  228. package/dist/src/view/repository.js +260 -19
  229. package/dist/src/view/request-security.d.ts +5 -0
  230. package/dist/src/view/request-security.js +46 -0
  231. package/dist/src/view/search-result.d.ts +4 -0
  232. package/dist/src/view/search-result.js +25 -0
  233. package/dist/src/view/search.d.ts +7 -2
  234. package/dist/src/view/search.js +23 -34
  235. package/dist/src/view/server-build.d.ts +21 -0
  236. package/dist/src/view/server-build.js +145 -0
  237. package/dist/src/view/server-deployment.d.ts +31 -0
  238. package/dist/src/view/server-deployment.js +123 -0
  239. package/dist/src/view/server-index-worker.d.ts +6 -0
  240. package/dist/src/view/server-index-worker.js +14 -0
  241. package/dist/src/view/server.d.ts +11 -2
  242. package/dist/src/view/server.js +245 -73
  243. package/dist/src/view/source-target.d.ts +2 -0
  244. package/dist/src/view/source-target.js +49 -8
  245. package/dist/src/view/static-build.d.ts +18 -2
  246. package/dist/src/view/static-build.js +449 -110
  247. package/dist/src/view/static-protocol.d.ts +26 -2
  248. package/dist/src/view/static-protocol.js +5 -0
  249. package/dist/src/view/store.d.ts +20 -3
  250. package/dist/src/view/store.js +304 -48
  251. package/dist/src/view/table-of-contents.d.ts +2 -2
  252. package/dist/src/view/table-of-contents.js +2 -22
  253. package/dist/src/view/vercel-build.d.ts +24 -0
  254. package/dist/src/view/vercel-build.js +152 -0
  255. package/dist/src/view/vercel-server-build.d.ts +10 -0
  256. package/dist/src/view/vercel-server-build.js +61 -0
  257. package/dist/src/wiki/catalog.d.ts +21 -0
  258. package/dist/src/wiki/catalog.js +256 -0
  259. package/dist/src/wiki/publish.d.ts +27 -0
  260. package/dist/src/wiki/publish.js +265 -0
  261. package/dist/src/wiki/render.d.ts +20 -0
  262. package/dist/src/wiki/render.js +163 -0
  263. package/dist/src/wiki/transform.d.ts +7 -0
  264. package/dist/src/wiki/transform.js +264 -0
  265. package/dist/vendor/lat.md-server/cli.d.ts +2 -0
  266. package/dist/vendor/lat.md-server/cli.js +12 -0
  267. package/dist/vendor/lat.md-server/index.d.ts +38 -0
  268. package/dist/vendor/lat.md-server/index.js +128 -0
  269. package/package.json +68 -22
  270. package/plugins/lat-md/README.md +61 -0
  271. package/plugins/lat-md/hooks.json +38 -0
  272. package/plugins/lat-md/plugin.json +9 -0
  273. package/plugins/lat-md/rules/AGENTS.md +344 -0
  274. package/plugins/lat-md/skills/lat-md/SKILL.md +210 -0
  275. package/plugins/lat-md/skills/lat-md-conventions/SKILL.md +226 -0
  276. package/templates/AGENTS.md +26 -7
  277. package/templates/cursor-rules.md +23 -7
  278. package/templates/fork/conventions.md +133 -11
  279. package/templates/opencode-plugin.ts +9 -9
  280. package/templates/pi-extension.ts +11 -11
  281. package/templates/skill/SKILL.md +35 -3
  282. package/dist/src/cli/check-coverage.d.ts +0 -2
  283. package/dist/src/cli/check-coverage.js +0 -71
  284. package/dist/src/cli/check-frontmatter.d.ts +0 -12
  285. package/dist/src/cli/check-frontmatter.js +0 -44
  286. package/dist/src/cli/check-mode.d.ts +0 -21
  287. package/dist/src/cli/check-mode.js +0 -364
  288. package/dist/src/cli/check-status.d.ts +0 -74
  289. package/dist/src/cli/check-status.js +0 -217
  290. package/dist/src/cli/check.d.ts +0 -45
  291. package/dist/src/cli/check.js +0 -781
  292. package/dist/src/cli/context.d.ts +0 -11
  293. package/dist/src/cli/context.js +0 -58
  294. package/dist/src/cli/expand.d.ts +0 -8
  295. package/dist/src/cli/expand.js +0 -94
  296. package/dist/src/cli/gen-index.d.ts +0 -45
  297. package/dist/src/cli/gen-index.js +0 -235
  298. package/dist/src/cli/link-scheme.d.ts +0 -54
  299. package/dist/src/cli/link-scheme.js +0 -101
  300. package/dist/src/cli/locate.d.ts +0 -2
  301. package/dist/src/cli/locate.js +0 -17
  302. package/dist/src/cli/refs.d.ts +0 -22
  303. package/dist/src/cli/refs.js +0 -229
  304. package/dist/src/cli/section.d.ts +0 -43
  305. package/dist/src/cli/section.js +0 -252
  306. package/dist/src/cli/select-menu.d.ts +0 -13
  307. package/dist/src/cli/select-menu.js +0 -100
  308. package/dist/src/code-refs.d.ts +0 -17
  309. package/dist/src/code-refs.js +0 -298
  310. package/dist/src/config.d.ts +0 -29
  311. package/dist/src/config.js +0 -83
  312. package/dist/src/context.d.ts +0 -23
  313. package/dist/src/context.js +0 -11
  314. package/dist/src/extensions/wiki-link/from-markdown.d.ts +0 -5
  315. package/dist/src/extensions/wiki-link/from-markdown.js +0 -34
  316. package/dist/src/extensions/wiki-link/index.d.ts +0 -4
  317. package/dist/src/extensions/wiki-link/index.js +0 -3
  318. package/dist/src/extensions/wiki-link/syntax.d.ts +0 -14
  319. package/dist/src/extensions/wiki-link/syntax.js +0 -121
  320. package/dist/src/extensions/wiki-link/to-markdown.d.ts +0 -5
  321. package/dist/src/extensions/wiki-link/to-markdown.js +0 -28
  322. package/dist/src/extensions/wiki-link/types.d.ts +0 -34
  323. package/dist/src/format.d.ts +0 -8
  324. package/dist/src/format.js +0 -43
  325. package/dist/src/init-version.d.ts +0 -10
  326. package/dist/src/init-version.js +0 -49
  327. package/dist/src/lattice.d.ts +0 -109
  328. package/dist/src/lattice.js +0 -817
  329. package/dist/src/parser.d.ts +0 -3
  330. package/dist/src/parser.js +0 -18
  331. package/dist/src/source-parser.d.ts +0 -27
  332. package/dist/src/source-parser.js +0 -916
  333. package/dist/src/untrusted.d.ts +0 -18
  334. package/dist/src/untrusted.js +0 -93
  335. package/dist/src/view/client/assets/index-C3iaZyYT.css +0 -1
  336. package/dist/src/view/client/assets/index-KWi67IMz.js +0 -334
  337. package/dist/src/view/client/assets/logo-CNF8zs47.svg +0 -182
  338. package/dist/src/walk.d.ts +0 -18
  339. package/dist/src/walk.js +0 -27
  340. /package/dist/src/{extensions/wiki-link → search}/types.js +0 -0
@@ -1,22 +1,29 @@
1
+ import { createPublicationPolicy } from './publication.js';
1
2
  import { createHash } from 'node:crypto';
2
3
  import { cp, lstat, mkdir, mkdtemp, readFile, rename, rm, writeFile, } from 'node:fs/promises';
3
4
  import { dirname, join, parse, relative, resolve, sep } from 'node:path';
4
5
  import { fileURLToPath } from 'node:url';
6
+ import { isDocumentPath } from '@plebeiantech/lat.md-core/document-formats';
5
7
  import { DEFAULT_VIEW_LOGO_TEXT } from './protocol.js';
6
- import { viewStaticSourceKey, } from './static-protocol.js';
8
+ import { documentTreeUrls, rewriteDocumentTreeUrls } from './document-tree.js';
9
+ import { VIEW_STATIC_BOOTSTRAP_ID, viewStaticDocumentRequest, viewStaticSourceKey, } from './static-protocol.js';
7
10
  import { createViewStore } from './store.js';
8
- const BUILD_MARKER = '.lat-ui-build';
11
+ import { rewriteClientAssetUrls } from './client-shell.js';
12
+ import { documentResourcePath, documentPath, documentUrl, rawDocumentPath, validateDocumentRoutes, } from './document-route.js';
9
13
  const defaultClientDir = fileURLToPath(new URL('./client/', import.meta.url));
14
+ const VIEW_BUILD_MOVE_MAX_RETRIES = 10;
15
+ const VIEW_BUILD_MOVE_RETRY_DELAY_MS = 150;
10
16
  function isInside(parent, child) {
11
17
  const rel = relative(parent, child);
12
18
  return rel === '' || (rel !== '..' && !rel.startsWith(`..${sep}`));
13
19
  }
14
20
  export function normalizeStaticViewBasePath(value) {
15
- const parsed = new URL(value || '/', 'http://lat.local');
21
+ const path = value.startsWith('/') ? value : `/${value}`;
22
+ const parsed = new URL(path, 'http://lat.local');
16
23
  if (parsed.origin !== 'http://lat.local' ||
17
24
  parsed.search ||
18
25
  parsed.hash ||
19
- !value.startsWith('/')) {
26
+ value.includes('://')) {
20
27
  throw new Error('Static UI base path must be an absolute URL path');
21
28
  }
22
29
  return parsed.pathname.endsWith('/')
@@ -44,8 +51,8 @@ function decodeHtmlUrlAttribute(value) {
44
51
  return value.replace(/&(amp|#38|#x26);/gi, '&');
45
52
  }
46
53
  /** Convert a live view route to its physical static-directory URL. */
47
- export function staticViewUrl(value, basePath) {
48
- if (!value || value.startsWith('#'))
54
+ export function staticViewUrl(value, basePath, entryPath) {
55
+ if (!value || !value.startsWith('/') || value.startsWith('//'))
49
56
  return value;
50
57
  let url;
51
58
  try {
@@ -56,7 +63,20 @@ export function staticViewUrl(value, basePath) {
56
63
  }
57
64
  if (url.origin !== 'http://lat.local')
58
65
  return value;
59
- for (const prefix of ['/docs/', '/code/']) {
66
+ const markdownPath = documentPath(url.pathname.replace(/\/$/, ''));
67
+ if (markdownPath || rawDocumentPath(url.pathname)) {
68
+ const route = url.pathname.slice(1).replace(/\/+$/, '');
69
+ const suffix = `${url.search}${url.hash}`;
70
+ if (entryPath && markdownPath === entryPath) {
71
+ return `${basePath}${suffix}`;
72
+ }
73
+ return `${basePath}${route}${suffix}`;
74
+ }
75
+ if (url.pathname.startsWith('/resources/')) {
76
+ const route = url.pathname.slice(1);
77
+ return `${basePath}${route}${url.search}${url.hash}`;
78
+ }
79
+ for (const prefix of ['/code/', '/external/']) {
60
80
  if (!url.pathname.startsWith(prefix))
61
81
  continue;
62
82
  const route = url.pathname.slice(1).replace(/\/+$/, '');
@@ -65,96 +85,96 @@ export function staticViewUrl(value, basePath) {
65
85
  if (url.pathname === '/graph') {
66
86
  return `${basePath}graph/${url.search}${url.hash}`;
67
87
  }
88
+ if (url.pathname === '/' || url.pathname === '/search') {
89
+ return `${basePath}${url.pathname.slice(1)}${url.search}${url.hash}`;
90
+ }
68
91
  return value;
69
92
  }
70
93
  function documentPathFromUrl(value) {
71
- if (!value.pathname.startsWith('/docs/'))
72
- return null;
73
- try {
74
- return value.pathname
75
- .slice('/docs/'.length)
76
- .split('/')
77
- .map(decodeURIComponent)
78
- .join('/');
79
- }
80
- catch {
81
- return null;
82
- }
94
+ return documentPath(value.pathname) ?? rawDocumentPath(value.pathname);
83
95
  }
84
- function rewriteHtmlLink(value, basePath, sourcePath, documentPaths) {
85
- const direct = staticViewUrl(value, basePath);
96
+ function rewriteHtmlLink(value, basePath, entryPath, sourcePath, documentPaths) {
97
+ const direct = staticViewUrl(value, basePath, entryPath);
86
98
  if (direct !== value || !sourcePath || value.startsWith('#'))
87
99
  return direct;
88
100
  let resolved;
89
101
  try {
90
- const currentPath = sourcePath.split('/').map(encodeURIComponent).join('/');
91
- resolved = new URL(decodeHtmlUrlAttribute(value), `http://lat.local/docs/${currentPath}`);
102
+ const externalAt = sourcePath.indexOf(':');
103
+ const currentRoute = externalAt > 0 && !documentPaths.has(sourcePath)
104
+ ? `/external/${encodeURIComponent(sourcePath.slice(0, externalAt))}/${sourcePath
105
+ .slice(externalAt + 1)
106
+ .split('/')
107
+ .map(encodeURIComponent)
108
+ .join('/')}`
109
+ : documentUrl(sourcePath);
110
+ resolved = new URL(decodeHtmlUrlAttribute(value), `http://lat.local${currentRoute}`);
92
111
  }
93
112
  catch {
94
113
  return value;
95
114
  }
96
115
  const documentPath = documentPathFromUrl(resolved);
116
+ if (resolved.pathname.startsWith('/external/')) {
117
+ return staticViewUrl(`${resolved.pathname}${resolved.search}${resolved.hash}`, basePath, entryPath);
118
+ }
97
119
  if (resolved.origin !== 'http://lat.local' ||
98
120
  !documentPath ||
99
121
  !documentPaths.has(documentPath)) {
100
122
  return value;
101
123
  }
102
- return staticViewUrl(`${resolved.pathname}${resolved.search}${resolved.hash}`, basePath);
124
+ return staticViewUrl(`${documentUrl(documentPath)}${resolved.search}${resolved.hash}`, basePath, entryPath);
103
125
  }
104
- function rewriteHtmlLinks(html, basePath, sourcePath, documentPaths) {
105
- return html.replace(/href="([^"]*)"/g, (attribute, value) => {
106
- const rewritten = rewriteHtmlLink(value, basePath, sourcePath, documentPaths);
107
- if (rewritten === value)
108
- return attribute;
109
- return `href="${rewritten.replaceAll('&', '&').replaceAll('"', '"')}"`;
110
- });
126
+ function rewriteDocumentLinks(tree, basePath, entryPath, sourcePath, documentPaths) {
127
+ return rewriteDocumentTreeUrls(tree, (value) => rewriteHtmlLink(value, basePath, entryPath, sourcePath, documentPaths));
111
128
  }
112
- function rewriteMarkdownReference(reference, basePath, sourcePath, documentPaths) {
129
+ function rewriteMarkdownReference(reference, basePath, entryPath, sourcePath, documentPaths) {
113
130
  return {
114
131
  ...reference,
115
- paragraphHtml: rewriteHtmlLinks(reference.paragraphHtml, basePath, sourcePath, documentPaths),
116
- url: staticViewUrl(reference.url, basePath),
132
+ paragraphTree: rewriteDocumentLinks(reference.paragraphTree, basePath, entryPath, sourcePath, documentPaths),
133
+ url: staticViewUrl(reference.url, basePath, entryPath),
117
134
  };
118
135
  }
119
- function rewriteBackReference(reference, basePath, sectionPaths, documentPaths) {
136
+ function rewriteBackReference(reference, basePath, entryPath, sectionPaths, documentPaths) {
120
137
  return reference.kind === 'markdown'
121
- ? rewriteMarkdownReference(reference, basePath, sectionPaths.get(reference.sectionId.split('#', 1)[0]) ?? null, documentPaths)
122
- : { ...reference, url: staticViewUrl(reference.url, basePath) };
138
+ ? rewriteMarkdownReference(reference, basePath, entryPath, sectionPaths.get(reference.sectionId.split('#', 1)[0]) ?? null, documentPaths)
139
+ : {
140
+ ...reference,
141
+ url: staticViewUrl(reference.url, basePath, entryPath),
142
+ };
123
143
  }
124
- function rewriteDocument(document, basePath, sectionPaths, documentPaths) {
144
+ function rewriteDocument(document, basePath, entryPath, sectionPaths, documentPaths) {
125
145
  return {
126
146
  ...document,
127
- html: rewriteHtmlLinks(document.html, basePath, document.path, documentPaths),
128
- gitHtml: null,
147
+ tree: rewriteDocumentLinks(document.tree, basePath, entryPath, document.path, documentPaths),
148
+ gitTree: null,
129
149
  backReferences: document.backReferences.map((section) => ({
130
150
  ...section,
131
- references: section.references.map((reference) => rewriteBackReference(reference, basePath, sectionPaths, documentPaths)),
151
+ references: section.references.map((reference) => rewriteBackReference(reference, basePath, entryPath, sectionPaths, documentPaths)),
132
152
  })),
133
153
  };
134
154
  }
135
- function rewriteSourceReference(reference, basePath, sectionPaths, documentPaths) {
155
+ function rewriteSourceReference(reference, basePath, entryPath, sectionPaths, documentPaths) {
136
156
  const sectionPath = reference.sectionId.split('#', 1)[0];
137
157
  return {
138
158
  ...reference,
139
- paragraphHtml: rewriteHtmlLinks(reference.paragraphHtml, basePath, sectionPaths.get(sectionPath) ?? null, documentPaths),
140
- url: staticViewUrl(reference.url, basePath),
159
+ paragraphTree: rewriteDocumentLinks(reference.paragraphTree, basePath, entryPath, sectionPaths.get(sectionPath) ?? null, documentPaths),
160
+ url: staticViewUrl(reference.url, basePath, entryPath),
141
161
  };
142
162
  }
143
- function rewriteSource(source, basePath, sectionPaths, documentPaths) {
163
+ function rewriteSource(source, basePath, entryPath, sectionPaths, documentPaths) {
144
164
  return {
145
165
  ...source,
146
166
  context: source.context
147
- ? rewriteSourceReference(source.context, basePath, sectionPaths, documentPaths)
167
+ ? rewriteSourceReference(source.context, basePath, entryPath, sectionPaths, documentPaths)
148
168
  : null,
149
- otherReferences: source.otherReferences.map((reference) => rewriteSourceReference(reference, basePath, sectionPaths, documentPaths)),
169
+ otherReferences: source.otherReferences.map((reference) => rewriteSourceReference(reference, basePath, entryPath, sectionPaths, documentPaths)),
150
170
  };
151
171
  }
152
- function rewriteGraph(graph, basePath) {
172
+ function rewriteGraph(graph, basePath, entryPath) {
153
173
  return {
154
174
  ...graph,
155
175
  nodes: graph.nodes.map(({ gitStatus: _gitStatus, ...node }) => ({
156
176
  ...node,
157
- url: staticViewUrl(node.url, basePath),
177
+ url: staticViewUrl(node.url, basePath, entryPath),
158
178
  })),
159
179
  };
160
180
  }
@@ -163,8 +183,14 @@ function positiveInteger(value) {
163
183
  return Number.isInteger(parsed) && parsed > 0 ? parsed : 0;
164
184
  }
165
185
  function sourceRequest(value) {
166
- const url = new URL(decodeHtmlUrlAttribute(value), 'http://lat.local');
167
- if (!url.pathname.startsWith('/code/'))
186
+ let url;
187
+ try {
188
+ url = new URL(decodeHtmlUrlAttribute(value), 'http://lat.local');
189
+ }
190
+ catch {
191
+ return null;
192
+ }
193
+ if (url.origin !== 'http://lat.local' || !url.pathname.startsWith('/code/'))
168
194
  return null;
169
195
  let path;
170
196
  let symbol;
@@ -174,6 +200,14 @@ function sourceRequest(value) {
174
200
  .split('/')
175
201
  .map(decodeURIComponent)
176
202
  .join('/');
203
+ if (path.split('/').some((part) => !part || part === '.' || part === '..') ||
204
+ path.includes('\\') ||
205
+ path.includes('\0') ||
206
+ /^[a-z]:/i.test(path))
207
+ return null;
208
+ // Encoded separators cannot create extra route components after decoding.
209
+ if (/%(?:2f|5c)/i.test(url.pathname))
210
+ return null;
177
211
  symbol = decodeURIComponent(url.hash.slice(1));
178
212
  }
179
213
  catch {
@@ -187,21 +221,104 @@ function sourceRequest(value) {
187
221
  at: positiveInteger(url.searchParams.get('at')),
188
222
  };
189
223
  }
224
+ function externalRequest(value, external) {
225
+ let url;
226
+ try {
227
+ url = new URL(decodeHtmlUrlAttribute(value), 'http://lat.local');
228
+ }
229
+ catch {
230
+ return null;
231
+ }
232
+ if (url.origin !== 'http://lat.local' ||
233
+ !url.pathname.startsWith('/external/'))
234
+ return null;
235
+ try {
236
+ const parts = url.pathname
237
+ .slice('/external/'.length)
238
+ .split('/')
239
+ .map(decodeURIComponent);
240
+ const handle = parts.shift() ?? '';
241
+ const path = parts.join('/');
242
+ if (!handle || !path)
243
+ return null;
244
+ const fragment = decodeURIComponent(url.hash.slice(1));
245
+ const target = `${handle}:${path}${fragment ? `#${fragment}` : ''}`;
246
+ const parsed = external?.parse(target);
247
+ if (parsed) {
248
+ if (!isDocumentPath(parsed.resolvedPath)) {
249
+ return parsed.identity;
250
+ }
251
+ const hash = parsed.identity.indexOf('#');
252
+ return hash === -1 ? parsed.identity : parsed.identity.slice(0, hash);
253
+ }
254
+ return `${handle}:${path}${isDocumentPath(path) || !fragment ? '' : `#${fragment}`}`;
255
+ }
256
+ catch {
257
+ return null;
258
+ }
259
+ }
260
+ function externalRequestsFromDocument(document, requests, external, externalBase) {
261
+ const add = (value) => {
262
+ let candidate = value;
263
+ if (externalBase && !value.startsWith('#')) {
264
+ try {
265
+ const colon = externalBase.indexOf(':');
266
+ const base = `/external/${encodeURIComponent(externalBase.slice(0, colon))}/${externalBase
267
+ .slice(colon + 1)
268
+ .split('/')
269
+ .map(encodeURIComponent)
270
+ .join('/')}`;
271
+ const resolved = new URL(decodeHtmlUrlAttribute(value), `http://lat.local${base}`);
272
+ if (resolved.origin === 'http://lat.local') {
273
+ candidate = `${resolved.pathname}${resolved.hash}`;
274
+ }
275
+ }
276
+ catch {
277
+ // Leave malformed links to the renderer rather than exporting them.
278
+ }
279
+ }
280
+ const request = externalRequest(candidate, external);
281
+ if (request)
282
+ requests.add(request);
283
+ };
284
+ for (const value of documentTreeUrls(document.tree))
285
+ add(value);
286
+ for (const section of document.backReferences) {
287
+ for (const reference of section.references) {
288
+ add(reference.url);
289
+ if (reference.kind === 'markdown') {
290
+ for (const value of documentTreeUrls(reference.paragraphTree))
291
+ add(value);
292
+ }
293
+ }
294
+ }
295
+ }
296
+ function externalRequestsFromSource(source, requests, external) {
297
+ for (const reference of [
298
+ ...(source.context ? [source.context] : []),
299
+ ...source.otherReferences,
300
+ ]) {
301
+ for (const value of documentTreeUrls(reference.paragraphTree)) {
302
+ const request = externalRequest(value, external);
303
+ if (request)
304
+ requests.add(request);
305
+ }
306
+ }
307
+ }
190
308
  function sourceRequestsFromDocument(document, requests) {
191
309
  const add = (value) => {
192
310
  const request = sourceRequest(value);
193
311
  if (request)
194
312
  requests.set(viewStaticSourceKey(request), request);
195
313
  };
196
- for (const match of document.html.matchAll(/href="([^"]*)"/g))
197
- add(match[1]);
314
+ for (const value of documentTreeUrls(document.tree))
315
+ add(value);
198
316
  for (const section of document.backReferences) {
199
317
  for (const reference of section.references) {
200
318
  add(reference.url);
201
319
  if (reference.kind === 'markdown') {
202
- for (const match of reference.paragraphHtml.matchAll(/href="([^"]*)"/g)) {
203
- add(match[1]);
204
- }
320
+ for (const value of documentTreeUrls(reference.paragraphTree))
321
+ add(value);
205
322
  }
206
323
  }
207
324
  }
@@ -212,20 +329,33 @@ function sourceRequestsFromSource(source, requests) {
212
329
  ...source.otherReferences,
213
330
  ];
214
331
  for (const reference of references) {
215
- for (const match of reference.paragraphHtml.matchAll(/href="([^"]*)"/g)) {
216
- const request = sourceRequest(match[1]);
332
+ for (const value of documentTreeUrls(reference.paragraphTree)) {
333
+ const request = sourceRequest(value);
217
334
  if (request)
218
335
  requests.set(viewStaticSourceKey(request), request);
219
336
  }
220
337
  }
221
338
  }
222
- function dataFile(kind, key) {
223
- const digest = createHash('sha256').update(key).digest('hex').slice(0, 20);
339
+ function serializeJson(value) {
340
+ return `${JSON.stringify(value)}\n`;
341
+ }
342
+ function dataFile(kind, content) {
343
+ const digest = createHash('sha256')
344
+ .update(content)
345
+ .digest('hex')
346
+ .slice(0, 20);
224
347
  return `data/${kind}/${digest}.json`;
225
348
  }
226
349
  async function writeJson(path, value) {
227
350
  await mkdir(dirname(path), { recursive: true });
228
- await writeFile(path, `${JSON.stringify(value)}\n`);
351
+ await writeFile(path, serializeJson(value));
352
+ }
353
+ async function writeDataJson(outputDir, kind, value) {
354
+ const content = serializeJson(value);
355
+ const path = dataFile(kind, content);
356
+ await mkdir(dirname(join(outputDir, path)), { recursive: true });
357
+ await writeFile(join(outputDir, path), content);
358
+ return path;
229
359
  }
230
360
  async function outputExists(path) {
231
361
  try {
@@ -238,16 +368,47 @@ async function outputExists(path) {
238
368
  throw error;
239
369
  }
240
370
  }
241
- async function validateOutput(outputDir, projectRoot) {
371
+ /** Refuse build targets that exist or could replace the project itself. */
372
+ export async function validateViewBuildOutput(outputDir, projectRoot, kind = 'Static', force = false) {
242
373
  if (outputDir === parse(outputDir).root ||
243
374
  outputDir === projectRoot ||
244
375
  isInside(outputDir, projectRoot)) {
245
- throw new Error('Static UI output must not contain the project root');
376
+ throw new Error(`${kind} UI output must not contain the project root`);
246
377
  }
247
- if (await outputExists(outputDir)) {
248
- throw new Error(`Static UI output already exists: ${outputDir}`);
378
+ if (!force && (await outputExists(outputDir))) {
379
+ throw new Error(`${kind} UI output already exists: ${outputDir}. Use --force to replace it.`);
249
380
  }
250
381
  }
382
+ const defaultViewBuildMoveDependencies = {
383
+ move: rename,
384
+ wait: (milliseconds) => new Promise((resolveWait) => setTimeout(resolveWait, milliseconds)),
385
+ };
386
+ /** Commit a staged build after transient filesystem locks have cleared. */
387
+ export async function moveViewBuildOutput(stagingDir, outputDir, dependencies = defaultViewBuildMoveDependencies) {
388
+ for (let retries = 0;; retries += 1) {
389
+ try {
390
+ await dependencies.move(stagingDir, outputDir);
391
+ return;
392
+ }
393
+ catch (error) {
394
+ const code = error.code;
395
+ if (retries >= VIEW_BUILD_MOVE_MAX_RETRIES ||
396
+ (code !== 'EBUSY' && code !== 'EPERM' && code !== 'ENOTEMPTY')) {
397
+ throw error;
398
+ }
399
+ await dependencies.wait(VIEW_BUILD_MOVE_RETRY_DELAY_MS);
400
+ }
401
+ }
402
+ }
403
+ /** Remove abandoned staging after transient filesystem locks have cleared. */
404
+ export async function removeViewBuildStaging(stagingDir) {
405
+ await rm(stagingDir, {
406
+ recursive: true,
407
+ force: true,
408
+ maxRetries: VIEW_BUILD_MOVE_MAX_RETRIES,
409
+ retryDelay: VIEW_BUILD_MOVE_RETRY_DELAY_MS,
410
+ });
411
+ }
251
412
  function sectionDocumentPaths(documents) {
252
413
  const result = new Map();
253
414
  for (const path of documents.keys()) {
@@ -255,30 +416,65 @@ function sectionDocumentPaths(documents) {
255
416
  }
256
417
  return result;
257
418
  }
258
- function clientShell(html, basePath) {
259
- const assets = html.replace(/(["'])\/assets\//g, (_match, quote) => `${quote}${basePath}assets/`);
260
- const configValue = JSON.stringify({ basePath }).replaceAll('<', '\\u003c');
261
- const config = `<script>globalThis.__LAT_STATIC_VIEW__=${configValue}</script>`;
419
+ function clientShell(html, basePath, entry, searchApi) {
420
+ const assets = rewriteClientAssetUrls(html, basePath);
421
+ const configValue = encodeURIComponent(JSON.stringify({ basePath, entry, searchApi }));
422
+ const config = `<meta name="lat-static-view" content="${configValue}">`;
262
423
  return assets.includes('</head>')
263
424
  ? assets.replace('</head>', ` ${config}\n </head>`)
264
425
  : `${config}\n${assets}`;
265
426
  }
266
- function redirectShell(target) {
427
+ function bootstrapShell(shell, manifest, responses = {}) {
428
+ const bootstrap = { manifest, responses };
429
+ const value = JSON.stringify(bootstrap).replaceAll('<', '\\u003c');
430
+ const data = `<script id="${VIEW_STATIC_BOOTSTRAP_ID}" type="application/json">${value}</script>`;
431
+ return shell.includes('</head>')
432
+ ? shell.replace('</head>', ` ${data}\n </head>`)
433
+ : `${data}\n${shell}`;
434
+ }
435
+ const REDIRECT_SCRIPT_PATH = 'data/redirect.js';
436
+ const REDIRECT_SCRIPT = `const meta = document.querySelector('meta[name="lat-redirect"]');
437
+ if (meta) {
438
+ try {
439
+ window.location.replace(decodeURIComponent(meta.content) + window.location.search + window.location.hash);
440
+ } catch {}
441
+ }
442
+ `;
443
+ function redirectShell(target, basePath) {
267
444
  const escaped = target.replaceAll('&', '&amp;').replaceAll('"', '&quot;');
268
- const scriptTarget = JSON.stringify(target).replaceAll('<', '\\u003c');
445
+ const scriptUrl = `${basePath}${REDIRECT_SCRIPT_PATH}`
446
+ .replaceAll('&', '&amp;')
447
+ .replaceAll('"', '&quot;');
269
448
  return `<!doctype html>
270
449
  <html lang="en">
271
450
  <head>
272
451
  <meta charset="UTF-8" />
273
- <meta http-equiv="refresh" content="0;url=${escaped}" />
452
+ <meta name="lat-redirect" content="${encodeURIComponent(target)}" />
274
453
  <title>lat.md</title>
454
+ <script src="${scriptUrl}" defer></script>
275
455
  </head>
276
- <body><script>window.location.replace(${scriptTarget} + window.location.hash)</script></body>
456
+ <body><a href="${escaped}">Open lat.md</a></body>
277
457
  </html>
278
458
  `;
279
459
  }
460
+ /** All authored route writes must remain strictly below the payload root. */
461
+ function publicationOutputPath(root, ...parts) {
462
+ if (parts.some((part) => part.includes('\\') ||
463
+ part.includes('\0') ||
464
+ part.split('/').some((segment) => segment === '..' || segment === '.')))
465
+ throw new Error('Unsafe export route');
466
+ const path = resolve(root, ...parts);
467
+ const rel = relative(root, path);
468
+ if (!rel ||
469
+ rel.startsWith(`..${sep}`) ||
470
+ rel === '..' ||
471
+ /^[a-z]:/i.test(rel) ||
472
+ rel.startsWith(sep))
473
+ throw new Error('Export route is outside the output directory');
474
+ return path;
475
+ }
280
476
  async function writeRouteShell(outputDir, route, shell) {
281
- const path = join(outputDir, ...route.split('/'), 'index.html');
477
+ const path = publicationOutputPath(outputDir, route, 'index.html');
282
478
  await mkdir(dirname(path), { recursive: true });
283
479
  await writeFile(path, shell);
284
480
  }
@@ -288,27 +484,56 @@ export async function buildStaticView(ctx, requestedOutput, options = {}) {
288
484
  const basePath = normalizeStaticViewBasePath(options.basePath ?? '/');
289
485
  const clientDir = options.clientDir ?? defaultClientDir;
290
486
  const logoText = options.logoText ?? DEFAULT_VIEW_LOGO_TEXT;
291
- await validateOutput(outputDir, ctx.projectRoot);
487
+ await validateViewBuildOutput(outputDir, ctx.projectRoot, 'Static', options.force);
292
488
  await mkdir(dirname(outputDir), { recursive: true });
293
- const stagingDir = await mkdtemp(join(dirname(outputDir), '.lat-ui-build-'));
294
- const store = await createViewStore(ctx.latDir, ctx.projectRoot, {
295
- codeExcludePaths: [outputDir],
296
- git: false,
297
- watch: false,
298
- });
489
+ const stagingDir = await mkdtemp(join(dirname(outputDir), '.lat-static-staging-'));
490
+ let store;
299
491
  try {
492
+ store = await createViewStore(ctx.latDir, ctx.projectRoot, {
493
+ codeExcludePaths: [outputDir, ...(options.codeExcludePaths ?? [])],
494
+ git: false,
495
+ watch: false,
496
+ externalIgnoreLocal: true,
497
+ externalCa: options.externalCa,
498
+ publishable: await createPublicationPolicy(ctx.projectRoot),
499
+ });
300
500
  const clientHtml = await readFile(join(clientDir, 'index.html'), 'utf8');
301
501
  const payloadDir = staticViewPayloadDir(stagingDir, basePath);
302
502
  await mkdir(payloadDir, { recursive: true });
303
503
  await cp(clientDir, payloadDir, { recursive: true });
304
- const shell = clientShell(clientHtml, basePath);
305
504
  const index = { ...store.getIndex(), git: null, logoText };
505
+ validateDocumentRoutes(index.files);
506
+ const shell = clientShell(clientHtml, basePath, index.entry, options.searchApi);
306
507
  const documents = new Map();
508
+ const documentResources = new Set();
307
509
  const sourceRequests = new Map();
308
- for (const path of index.files) {
510
+ const externalRequests = new Set();
511
+ for (const file of index.externalFiles) {
512
+ externalRequests.add(file.target);
513
+ }
514
+ const pendingDocuments = new Set(index.files);
515
+ for (const path of pendingDocuments) {
309
516
  const document = await store.getDocument(path);
310
517
  documents.set(path, document);
518
+ for (const url of documentTreeUrls(document.tree)) {
519
+ let parsed;
520
+ try {
521
+ parsed = new URL(url, 'http://lat.local');
522
+ }
523
+ catch {
524
+ continue;
525
+ }
526
+ if (parsed.origin !== 'http://lat.local')
527
+ continue;
528
+ const previewPath = documentPath(parsed.pathname);
529
+ if (previewPath?.startsWith('@/'))
530
+ pendingDocuments.add(previewPath);
531
+ const resourcePath = documentResourcePath(parsed.pathname);
532
+ if (resourcePath)
533
+ documentResources.add(resourcePath);
534
+ }
311
535
  sourceRequestsFromDocument(document, sourceRequests);
536
+ externalRequestsFromDocument(document, externalRequests, store.snapshot.external);
312
537
  }
313
538
  const documentPaths = new Set(documents.keys());
314
539
  const sectionPaths = sectionDocumentPaths(documents);
@@ -317,6 +542,37 @@ export async function buildStaticView(ctx, requestedOutput, options = {}) {
317
542
  const request = sourceRequest(node.url);
318
543
  if (request)
319
544
  sourceRequests.set(viewStaticSourceKey(request), request);
545
+ if (node.externalTarget)
546
+ externalRequests.add(node.externalTarget);
547
+ }
548
+ const externals = new Map();
549
+ const pendingExternal = [...externalRequests];
550
+ for (let index = 0; index < pendingExternal.length; index++) {
551
+ const target = pendingExternal[index];
552
+ if (externals.has(target))
553
+ continue;
554
+ let external;
555
+ try {
556
+ external = await store.getExternal(target);
557
+ }
558
+ catch (error) {
559
+ throw new Error(`Could not export external source ${target}: ${error.message}`);
560
+ }
561
+ externals.set(target, external);
562
+ const before = externalRequests.size;
563
+ if (external.kind === 'markdown') {
564
+ externalRequestsFromDocument(external.document, externalRequests, store.snapshot.external, external.document.path);
565
+ }
566
+ else {
567
+ externalRequestsFromSource(external.source, externalRequests, store.snapshot.external);
568
+ }
569
+ if (externalRequests.size > before) {
570
+ for (const next of externalRequests) {
571
+ if (!externals.has(next) && !pendingExternal.includes(next)) {
572
+ pendingExternal.push(next);
573
+ }
574
+ }
575
+ }
320
576
  }
321
577
  const sources = new Map();
322
578
  const pending = [...sourceRequests.keys()];
@@ -346,63 +602,146 @@ export async function buildStaticView(ctx, requestedOutput, options = {}) {
346
602
  }
347
603
  }
348
604
  }
605
+ const rewrittenGraph = rewriteGraph(graph, basePath, index.entry);
349
606
  const manifest = {
350
607
  version: 1,
351
608
  index,
352
- graph: 'data/graph.json',
609
+ graph: await writeDataJson(payloadDir, 'graphs', rewrittenGraph),
353
610
  documents: {},
354
611
  sources: {},
612
+ externals: {},
355
613
  };
356
- await writeJson(join(payloadDir, manifest.graph), rewriteGraph(graph, basePath));
614
+ const documentBootstraps = new Map();
357
615
  for (const [path, document] of documents) {
358
- const dataPath = dataFile('documents', path);
359
- manifest.documents[path] = dataPath;
360
- await writeJson(join(payloadDir, dataPath), rewriteDocument(document, basePath, sectionPaths, documentPaths));
361
- const route = `docs/${path}`;
362
- await writeRouteShell(payloadDir, route, shell);
616
+ const rewritten = rewriteDocument(document, basePath, index.entry, sectionPaths, documentPaths);
617
+ manifest.documents[path] = await writeDataJson(payloadDir, 'documents', rewritten);
618
+ if (!path.startsWith('@/')) {
619
+ const source = await store.getDocumentSource(path);
620
+ const rawPath = publicationOutputPath(payloadDir, path);
621
+ await mkdir(dirname(rawPath), { recursive: true });
622
+ await writeFile(rawPath, source.content);
623
+ }
624
+ const route = path.startsWith('@/') ? path : path.slice(0, -'.md'.length);
625
+ documentBootstraps.set(route, {
626
+ request: viewStaticDocumentRequest(path),
627
+ document: rewritten,
628
+ });
629
+ }
630
+ for (const path of [...documentResources].sort()) {
631
+ let content;
632
+ try {
633
+ content = await store.getDocumentResource(path);
634
+ }
635
+ catch (error) {
636
+ throw new Error(`Could not export document resource ${path}: ${error.message}`);
637
+ }
638
+ const outputPath = publicationOutputPath(payloadDir, 'resources', path);
639
+ await mkdir(dirname(outputPath), { recursive: true });
640
+ await writeFile(outputPath, content);
363
641
  }
364
642
  const sourceFiles = new Map();
365
643
  for (const [key, source] of sources) {
366
- const rewritten = rewriteSource(source, basePath, sectionPaths, documentPaths);
367
- const { path, content, highlightedHtmlLines, ...view } = rewritten;
644
+ const rewritten = rewriteSource(source, basePath, index.entry, sectionPaths, documentPaths);
645
+ const { path, content, highlightedLines, ...view } = rewritten;
368
646
  let fileDataPath = sourceFiles.get(path);
369
647
  if (!fileDataPath) {
370
- fileDataPath = dataFile('source-files', path);
371
- sourceFiles.set(path, fileDataPath);
372
- await writeJson(join(payloadDir, fileDataPath), {
648
+ fileDataPath = await writeDataJson(payloadDir, 'source-files', {
373
649
  path,
374
650
  content,
375
- highlightedHtmlLines,
651
+ highlightedLines,
376
652
  });
653
+ sourceFiles.set(path, fileDataPath);
377
654
  }
378
- const viewDataPath = dataFile('source-views', key);
655
+ const viewDataPath = await writeDataJson(payloadDir, 'source-views', view);
379
656
  manifest.sources[key] = { file: fileDataPath, view: viewDataPath };
380
- await writeJson(join(payloadDir, viewDataPath), view);
381
657
  }
382
- for (const path of sourceFiles.keys()) {
383
- const route = `code/${path}`;
384
- await writeRouteShell(payloadDir, route, shell);
658
+ const externalSourceFiles = new Map();
659
+ const externalRoutes = new Set();
660
+ for (const [target, external] of externals) {
661
+ if (external.kind === 'markdown') {
662
+ const dataPath = await writeDataJson(payloadDir, 'external-documents', {
663
+ ...external,
664
+ document: rewriteDocument(external.document, basePath, index.entry, sectionPaths, documentPaths),
665
+ });
666
+ manifest.externals[target] = { kind: 'markdown', document: dataPath };
667
+ const colon = external.document.path.indexOf(':');
668
+ externalRoutes.add(`external/${external.document.path.slice(0, colon)}/${external.document.path.slice(colon + 1)}`);
669
+ continue;
670
+ }
671
+ const rewritten = rewriteSource(external.source, basePath, index.entry, sectionPaths, documentPaths);
672
+ const { path, content, highlightedLines, ...sourceView } = rewritten;
673
+ let fileDataPath = externalSourceFiles.get(path);
674
+ if (!fileDataPath) {
675
+ fileDataPath = await writeDataJson(payloadDir, 'external-source-files', {
676
+ path,
677
+ content,
678
+ highlightedLines,
679
+ });
680
+ externalSourceFiles.set(path, fileDataPath);
681
+ }
682
+ const view = {
683
+ kind: 'source',
684
+ target: external.target,
685
+ source: sourceView,
686
+ };
687
+ const viewDataPath = await writeDataJson(payloadDir, 'external-source-views', view);
688
+ manifest.externals[target] = {
689
+ kind: 'source',
690
+ file: fileDataPath,
691
+ view: viewDataPath,
692
+ };
693
+ const colon = path.indexOf(':');
694
+ externalRoutes.add(`external/${path.slice(0, colon)}/${path.slice(colon + 1)}`);
385
695
  }
386
- await writeRouteShell(payloadDir, 'graph', shell);
387
696
  await writeJson(join(payloadDir, 'data/manifest.json'), manifest);
388
- const entryRedirect = redirectShell(staticViewUrl(`/docs/${index.entry}`, basePath));
389
- await writeFile(join(payloadDir, 'index.html'), entryRedirect);
697
+ const entryRoute = index.entry.slice(0, -'.md'.length);
698
+ const entryBootstrap = documentBootstraps.get(entryRoute);
699
+ if (!entryBootstrap) {
700
+ throw new Error(`Static UI entry document is missing: ${index.entry}`);
701
+ }
702
+ for (const [route, initial] of documentBootstraps) {
703
+ if (route === entryRoute)
704
+ continue;
705
+ await writeRouteShell(payloadDir, route, bootstrapShell(shell, manifest, {
706
+ [initial.request]: initial.document,
707
+ }));
708
+ }
709
+ const genericShell = bootstrapShell(shell, manifest);
710
+ for (const path of sourceFiles.keys()) {
711
+ await writeRouteShell(payloadDir, `code/${path}`, genericShell);
712
+ }
713
+ for (const route of externalRoutes) {
714
+ await writeRouteShell(payloadDir, route, genericShell);
715
+ }
716
+ await writeRouteShell(payloadDir, 'graph', bootstrapShell(shell, manifest, { '/api/graph': rewrittenGraph }));
717
+ if (options.searchApi) {
718
+ await writeRouteShell(payloadDir, 'search', genericShell);
719
+ }
720
+ const redirectScriptPath = join(payloadDir, REDIRECT_SCRIPT_PATH);
721
+ await mkdir(dirname(redirectScriptPath), { recursive: true });
722
+ await writeFile(redirectScriptPath, REDIRECT_SCRIPT);
723
+ await writeFile(join(payloadDir, 'index.html'), bootstrapShell(shell, manifest, {
724
+ [entryBootstrap.request]: entryBootstrap.document,
725
+ }));
726
+ await writeRouteShell(payloadDir, entryRoute, redirectShell(basePath, basePath));
390
727
  if (payloadDir !== stagingDir) {
391
- await writeFile(join(stagingDir, 'index.html'), entryRedirect);
728
+ await writeFile(join(stagingDir, 'index.html'), redirectShell(basePath, basePath));
729
+ }
730
+ if (options.force) {
731
+ await rm(outputDir, { recursive: true, force: true });
392
732
  }
393
- await writeFile(join(stagingDir, BUILD_MARKER), `${JSON.stringify({ version: 1 })}\n`);
394
- await rename(stagingDir, outputDir);
733
+ await moveViewBuildOutput(stagingDir, outputDir);
395
734
  return {
396
735
  documents: documents.size,
397
736
  outputDir,
398
- sources: sources.size,
737
+ sources: sources.size + externals.size,
399
738
  };
400
739
  }
401
740
  catch (error) {
402
- await rm(stagingDir, { recursive: true, force: true });
741
+ await removeViewBuildStaging(stagingDir);
403
742
  throw error;
404
743
  }
405
744
  finally {
406
- await store.close();
745
+ await store?.close();
407
746
  }
408
747
  }